Source Code Cross Referenced for TdsCore.java in  » Database-JDBC-Connection-Pool » jTDS » net » sourceforge » jtds » jdbc » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Database JDBC Connection Pool » jTDS » net.sourceforge.jtds.jdbc 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        // jTDS JDBC Driver for Microsoft SQL Server and Sybase
0002:        // Copyright (C) 2004 The jTDS Project
0003:        //
0004:        // This library is free software; you can redistribute it and/or
0005:        // modify it under the terms of the GNU Lesser General Public
0006:        // License as published by the Free Software Foundation; either
0007:        // version 2.1 of the License, or (at your option) any later version.
0008:        //
0009:        // This library is distributed in the hope that it will be useful,
0010:        // but WITHOUT ANY WARRANTY; without even the implied warranty of
0011:        // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012:        // Lesser General Public License for more details.
0013:        //
0014:        // You should have received a copy of the GNU Lesser General Public
0015:        // License along with this library; if not, write to the Free Software
0016:        // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0017:        //
0018:        package net.sourceforge.jtds.jdbc;
0019:
0020:        import java.io.*;
0021:        import java.sql.*;
0022:        import java.util.Arrays;
0023:        import java.util.ArrayList;
0024:        import java.util.HashMap;
0025:        import java.util.Random;
0026:
0027:        import net.sourceforge.jtds.ssl.*;
0028:        import net.sourceforge.jtds.util.*;
0029:
0030:        /**
0031:         * This class implements the Sybase / Microsoft TDS protocol.
0032:         * <p>
0033:         * Implementation notes:
0034:         * <ol>
0035:         * <li>This class, together with TdsData, encapsulates all of the TDS specific logic
0036:         *     required by the driver.
0037:         * <li>This is a ground up reimplementation of the TDS protocol and is rather
0038:         *     simpler, and hopefully easier to understand, than the original.
0039:         * <li>The layout of the various Login packets is derived from the original code
0040:         *     and freeTds work, and incorporates changes including the ability to login as a TDS 5.0 user.
0041:         * <li>All network I/O errors are trapped here, reported to the log (if active)
0042:         *     and the parent Connection object is notified that the connection should be considered
0043:         *     closed.
0044:         * <li>Rather than having a large number of classes one for each token, useful information
0045:         *     about the current token is gathered together in the inner TdsToken class.
0046:         * <li>As the rest of the driver interfaces to this code via higher-level method calls there
0047:         *     should be know need for knowledge of the TDS protocol to leak out of this class.
0048:         *     It is for this reason that all the TDS Token constants are private.
0049:         * </ol>
0050:         *
0051:         * @author Mike Hutchinson
0052:         * @author Matt Brinkley
0053:         * @author Alin Sinpalean
0054:         * @author FreeTDS project
0055:         * @version $Id: TdsCore.java,v 1.115 2007/07/08 17:28:23 bheineman Exp $
0056:         */
0057:        public class TdsCore {
0058:            /**
0059:             * Inner static class used to hold information about TDS tokens read.
0060:             */
0061:            private static class TdsToken {
0062:                /** The current TDS token byte. */
0063:                byte token;
0064:                /** The status field from a DONE packet. */
0065:                byte status;
0066:                /** The operation field from a DONE packet. */
0067:                byte operation;
0068:                /** The update count from a DONE packet. */
0069:                int updateCount;
0070:                /** The nonce from an NTLM challenge packet. */
0071:                byte[] nonce;
0072:                /** NTLM authentication message. */
0073:                byte[] ntlmMessage;
0074:                /** target info for NTLM message TODO: I don't need to store these!!! */
0075:                byte[] ntlmTarget;
0076:                /** The dynamic parameters from the last TDS_DYNAMIC token. */
0077:                ColInfo[] dynamParamInfo;
0078:                /** The dynamic parameter data from the last TDS_DYNAMIC token. */
0079:                Object[] dynamParamData;
0080:
0081:                /**
0082:                 * Retrieve the update count status.
0083:                 *
0084:                 * @return <code>boolean</code> true if the update count is valid.
0085:                 */
0086:                boolean isUpdateCount() {
0087:                    return (token == TDS_DONE_TOKEN || token == TDS_DONEINPROC_TOKEN)
0088:                            && (status & DONE_ROW_COUNT) != 0;
0089:                }
0090:
0091:                /**
0092:                 * Retrieve the DONE token status.
0093:                 *
0094:                 * @return <code>boolean</code> true if the current token is a DONE packet.
0095:                 */
0096:                boolean isEndToken() {
0097:                    return token == TDS_DONE_TOKEN
0098:                            || token == TDS_DONEINPROC_TOKEN
0099:                            || token == TDS_DONEPROC_TOKEN;
0100:                }
0101:
0102:                /**
0103:                 * Retrieve the NTLM challenge status.
0104:                 *
0105:                 * @return <code>boolean</code> true if the current token is an NTLM challenge.
0106:                 */
0107:                boolean isAuthToken() {
0108:                    return token == TDS_AUTH_TOKEN;
0109:                }
0110:
0111:                /**
0112:                 * Retrieve the results pending status.
0113:                 *
0114:                 * @return <code>boolean</code> true if more results in input.
0115:                 */
0116:                boolean resultsPending() {
0117:                    return !isEndToken() || ((status & DONE_MORE_RESULTS) != 0);
0118:                }
0119:
0120:                /**
0121:                 * Retrieve the result set status.
0122:                 *
0123:                 * @return <code>boolean</code> true if the current token is a result set.
0124:                 */
0125:                boolean isResultSet() {
0126:                    return token == TDS_COLFMT_TOKEN
0127:                            || token == TDS7_RESULT_TOKEN
0128:                            || token == TDS_RESULT_TOKEN
0129:                            || token == TDS5_WIDE_RESULT
0130:                            || token == TDS_COLINFO_TOKEN
0131:                            || token == TDS_ROW_TOKEN;
0132:                }
0133:
0134:                /**
0135:                 * Retrieve the row data status.
0136:                 *
0137:                 * @return <code>boolean</code> true if the current token is a result row.
0138:                 */
0139:                public boolean isRowData() {
0140:                    return token == TDS_ROW_TOKEN;
0141:                }
0142:
0143:            }
0144:
0145:            /**
0146:             * Inner static class used to hold table meta data.
0147:             */
0148:            private static class TableMetaData {
0149:                /** Table catalog (database) name. */
0150:                String catalog;
0151:                /** Table schema (user) name. */
0152:                String schema;
0153:                /** Table name. */
0154:                String name;
0155:            }
0156:
0157:            //
0158:            // Package private constants
0159:            //
0160:            /** Minimum network packet size. */
0161:            public static final int MIN_PKT_SIZE = 512;
0162:            /** Default minimum network packet size for TDS 7.0 and newer. */
0163:            public static final int DEFAULT_MIN_PKT_SIZE_TDS70 = 4096;
0164:            /** Maximum network packet size. */
0165:            public static final int MAX_PKT_SIZE = 32768;
0166:            /** The size of the packet header. */
0167:            public static final int PKT_HDR_LEN = 8;
0168:            /** TDS 4.2 or 7.0 Query packet. */
0169:            public static final byte QUERY_PKT = 1;
0170:            /** TDS 4.2 or 5.0 Login packet. */
0171:            public static final byte LOGIN_PKT = 2;
0172:            /** TDS Remote Procedure Call. */
0173:            public static final byte RPC_PKT = 3;
0174:            /** TDS Reply packet. */
0175:            public static final byte REPLY_PKT = 4;
0176:            /** TDS Cancel packet. */
0177:            public static final byte CANCEL_PKT = 6;
0178:            /** TDS MSDTC packet. */
0179:            public static final byte MSDTC_PKT = 14;
0180:            /** TDS 5.0 Query packet. */
0181:            public static final byte SYBQUERY_PKT = 15;
0182:            /** TDS 7.0 Login packet. */
0183:            public static final byte MSLOGIN_PKT = 16;
0184:            /** TDS 7.0 NTLM Authentication packet. */
0185:            public static final byte NTLMAUTH_PKT = 17;
0186:            /** SQL 2000 prelogin negotiation packet. */
0187:            public static final byte PRELOGIN_PKT = 18;
0188:            /** SSL Mode - Login packet must be encrypted. */
0189:            public static final int SSL_ENCRYPT_LOGIN = 0;
0190:            /** SSL Mode - Client requested force encryption. */
0191:            public static final int SSL_CLIENT_FORCE_ENCRYPT = 1;
0192:            /** SSL Mode - No server certificate installed. */
0193:            public static final int SSL_NO_ENCRYPT = 2;
0194:            /** SSL Mode - Server requested force encryption. */
0195:            public static final int SSL_SERVER_FORCE_ENCRYPT = 3;
0196:
0197:            //
0198:            // Sub packet types
0199:            //
0200:            /** TDS 5.0 Parameter format token. */
0201:            private static final byte TDS5_PARAMFMT2_TOKEN = (byte) 32; // 0x20
0202:            /** TDS 5.0 Language token. */
0203:            private static final byte TDS_LANG_TOKEN = (byte) 33; // 0x21
0204:            /** TSD 5.0 Wide result set token. */
0205:            private static final byte TDS5_WIDE_RESULT = (byte) 97; // 0x61
0206:            /** TDS 5.0 Close token. */
0207:            private static final byte TDS_CLOSE_TOKEN = (byte) 113; // 0x71
0208:            /** TDS DBLIB Offsets token. */
0209:            private static final byte TDS_OFFSETS_TOKEN = (byte) 120; // 0x78
0210:            /** TDS Procedure call return status token. */
0211:            private static final byte TDS_RETURNSTATUS_TOKEN = (byte) 121; // 0x79
0212:            /** TDS Procedure ID token. */
0213:            private static final byte TDS_PROCID = (byte) 124; // 0x7C
0214:            /** TDS 7.0 Result set column meta data token. */
0215:            private static final byte TDS7_RESULT_TOKEN = (byte) 129; // 0x81
0216:            /** TDS 7.0 Computed Result set column meta data token. */
0217:            private static final byte TDS7_COMP_RESULT_TOKEN = (byte) 136; // 0x88
0218:            /** TDS 4.2 Column names token. */
0219:            private static final byte TDS_COLNAME_TOKEN = (byte) 160; // 0xA0
0220:            /** TDS 4.2 Column meta data token. */
0221:            private static final byte TDS_COLFMT_TOKEN = (byte) 161; // 0xA1
0222:            /** TDS Table name token. */
0223:            private static final byte TDS_TABNAME_TOKEN = (byte) 164; // 0xA4
0224:            /** TDS Cursor results column infomation token. */
0225:            private static final byte TDS_COLINFO_TOKEN = (byte) 165; // 0xA5
0226:            /** TDS Optional command token. */
0227:            private static final byte TDS_OPTIONCMD_TOKEN = (byte) 166; // 0xA6
0228:            /** TDS Computed result set names token. */
0229:            private static final byte TDS_COMP_NAMES_TOKEN = (byte) 167; // 0xA7
0230:            /** TDS Computed result set token. */
0231:            private static final byte TDS_COMP_RESULT_TOKEN = (byte) 168; // 0xA8
0232:            /** TDS Order by columns token. */
0233:            private static final byte TDS_ORDER_TOKEN = (byte) 169; // 0xA9
0234:            /** TDS error result token. */
0235:            private static final byte TDS_ERROR_TOKEN = (byte) 170; // 0xAA
0236:            /** TDS Information message token. */
0237:            private static final byte TDS_INFO_TOKEN = (byte) 171; // 0xAB
0238:            /** TDS Output parameter value token. */
0239:            private static final byte TDS_PARAM_TOKEN = (byte) 172; // 0xAC
0240:            /** TDS Login acknowledgement token. */
0241:            private static final byte TDS_LOGINACK_TOKEN = (byte) 173; // 0xAD
0242:            /** TDS control token. */
0243:            private static final byte TDS_CONTROL_TOKEN = (byte) 174; // 0xAE
0244:            /** TDS Result set data row token. */
0245:            private static final byte TDS_ROW_TOKEN = (byte) 209; // 0xD1
0246:            /** TDS Computed result set data row token. */
0247:            private static final byte TDS_ALTROW = (byte) 211; // 0xD3
0248:            /** TDS 5.0 parameter value token. */
0249:            private static final byte TDS5_PARAMS_TOKEN = (byte) 215; // 0xD7
0250:            /** TDS 5.0 capabilities token. */
0251:            private static final byte TDS_CAP_TOKEN = (byte) 226; // 0xE2
0252:            /** TDS environment change token. */
0253:            private static final byte TDS_ENVCHANGE_TOKEN = (byte) 227; // 0xE3
0254:            /** TDS 5.0 message token. */
0255:            private static final byte TDS_MSG50_TOKEN = (byte) 229; // 0xE5
0256:            /** TDS 5.0 RPC token. */
0257:            private static final byte TDS_DBRPC_TOKEN = (byte) 230; // 0xE6
0258:            /** TDS 5.0 Dynamic SQL token. */
0259:            private static final byte TDS5_DYNAMIC_TOKEN = (byte) 231; // 0xE7
0260:            /** TDS 5.0 parameter descriptor token. */
0261:            private static final byte TDS5_PARAMFMT_TOKEN = (byte) 236; // 0xEC
0262:            /** TDS 7.0 NTLM authentication challenge token. */
0263:            private static final byte TDS_AUTH_TOKEN = (byte) 237; // 0xED
0264:            /** TDS 5.0 Result set column meta data token. */
0265:            private static final byte TDS_RESULT_TOKEN = (byte) 238; // 0xEE
0266:            /** TDS done token. */
0267:            private static final byte TDS_DONE_TOKEN = (byte) 253; // 0xFD DONE
0268:            /** TDS done procedure token. */
0269:            private static final byte TDS_DONEPROC_TOKEN = (byte) 254; // 0xFE DONEPROC
0270:            /** TDS done in procedure token. */
0271:            private static final byte TDS_DONEINPROC_TOKEN = (byte) 255; // 0xFF DONEINPROC
0272:
0273:            //
0274:            // Environment change payload codes
0275:            //
0276:            /** Environment change: database changed. */
0277:            private static final byte TDS_ENV_DATABASE = (byte) 1;
0278:            /** Environment change: language changed. */
0279:            private static final byte TDS_ENV_LANG = (byte) 2;
0280:            /** Environment change: charset changed. */
0281:            private static final byte TDS_ENV_CHARSET = (byte) 3;
0282:            /** Environment change: network packet size changed. */
0283:            private static final byte TDS_ENV_PACKSIZE = (byte) 4;
0284:            /** Environment change: locale changed. */
0285:            private static final byte TDS_ENV_LCID = (byte) 5;
0286:            /** Environment change: TDS 8 collation changed. */
0287:            private static final byte TDS_ENV_SQLCOLLATION = (byte) 7; // TDS8 Collation
0288:
0289:            //
0290:            // Static variables used only for performance
0291:            //
0292:            /** Used to optimize the {@link #getParameters()} call */
0293:            private static final ParamInfo[] EMPTY_PARAMETER_INFO = new ParamInfo[0];
0294:
0295:            //
0296:            // End token status bytes
0297:            //
0298:            /** Done: more results are expected. */
0299:            private static final byte DONE_MORE_RESULTS = (byte) 0x01;
0300:            /** Done: command caused an error. */
0301:            private static final byte DONE_ERROR = (byte) 0x02;
0302:            /** Done: There is a valid row count. */
0303:            private static final byte DONE_ROW_COUNT = (byte) 0x10;
0304:            /** Done: Cancel acknowledgement. */
0305:            static final byte DONE_CANCEL = (byte) 0x20;
0306:            /**
0307:             * Done: Response terminator (if more than one request packet is sent, each
0308:             * response is terminated by a DONE packet with this flag set).
0309:             */
0310:            private static final byte DONE_END_OF_RESPONSE = (byte) 0x80;
0311:
0312:            //
0313:            // Prepared SQL types
0314:            //
0315:            /** Do not prepare SQL */
0316:            public static final int UNPREPARED = 0;
0317:            /** Prepare SQL using temporary stored procedures */
0318:            public static final int TEMPORARY_STORED_PROCEDURES = 1;
0319:            /** Prepare SQL using sp_executesql */
0320:            public static final int EXECUTE_SQL = 2;
0321:            /** Prepare SQL using sp_prepare and sp_execute */
0322:            public static final int PREPARE = 3;
0323:
0324:            //
0325:            // Sybase capability flags
0326:            //
0327:            /** Sybase char and binary > 255.*/
0328:            static final int SYB_LONGDATA = 1;
0329:            /** Sybase date and time data types.*/
0330:            static final int SYB_DATETIME = 2;
0331:            /** Sybase nullable bit type.*/
0332:            static final int SYB_BITNULL = 4;
0333:            /** Sybase extended column meta data.*/
0334:            static final int SYB_EXTCOLINFO = 8;
0335:            /** Sybase univarchar etc. */
0336:            static final int SYB_UNICODE = 16;
0337:            /** Sybase 15+ unitext. */
0338:            static final int SYB_UNITEXT = 32;
0339:            /** Sybase 15+ bigint. */
0340:            static final int SYB_BIGINT = 64;
0341:
0342:            /** Cancel has been generated by <code>Statement.cancel()</code>. */
0343:            private final static int ASYNC_CANCEL = 0;
0344:            /** Cancel has been generated by a query timeout. */
0345:            private final static int TIMEOUT_CANCEL = 1;
0346:
0347:            /** Map of system stored procedures that have shortcuts in TDS8. */
0348:            private static HashMap tds8SpNames = new HashMap();
0349:            static {
0350:                tds8SpNames.put("sp_cursor", new Integer(1));
0351:                tds8SpNames.put("sp_cursoropen", new Integer(2));
0352:                tds8SpNames.put("sp_cursorprepare", new Integer(3));
0353:                tds8SpNames.put("sp_cursorexecute", new Integer(4));
0354:                tds8SpNames.put("sp_cursorprepexec", new Integer(5));
0355:                tds8SpNames.put("sp_cursorunprepare", new Integer(6));
0356:                tds8SpNames.put("sp_cursorfetch", new Integer(7));
0357:                tds8SpNames.put("sp_cursoroption", new Integer(8));
0358:                tds8SpNames.put("sp_cursorclose", new Integer(9));
0359:                tds8SpNames.put("sp_executesql", new Integer(10));
0360:                tds8SpNames.put("sp_prepare", new Integer(11));
0361:                tds8SpNames.put("sp_execute", new Integer(12));
0362:                tds8SpNames.put("sp_prepexec", new Integer(13));
0363:                tds8SpNames.put("sp_prepexecrpc", new Integer(14));
0364:                tds8SpNames.put("sp_unprepare", new Integer(15));
0365:            }
0366:
0367:            //
0368:            // Class variables
0369:            //
0370:            /** Name of the client host (it can take quite a while to find it out if DNS is configured incorrectly). */
0371:            private static String hostName;
0372:            /** A reference to ntlm.SSPIJNIClient. */
0373:            private static SSPIJNIClient sspiJNIClient;
0374:
0375:            //
0376:            // Instance variables
0377:            //
0378:            /** The Connection object that created this object. */
0379:            private final ConnectionJDBC2 connection;
0380:            /** The TDS version being supported by this connection. */
0381:            private int tdsVersion;
0382:            /** The make of SQL Server (Sybase/Microsoft). */
0383:            private final int serverType;
0384:            /** The Shared network socket object. */
0385:            private final SharedSocket socket;
0386:            /** The output server request stream. */
0387:            private final RequestStream out;
0388:            /** The input server response stream. */
0389:            private final ResponseStream in;
0390:            /** True if the server response is fully read. */
0391:            private boolean endOfResponse = true;
0392:            /** True if the current result set is at end of file. */
0393:            private boolean endOfResults = true;
0394:            /** The array of column meta data objects for this result set. */
0395:            private ColInfo[] columns;
0396:            /** The array of column data objects in the current row. */
0397:            private Object[] rowData;
0398:            /** The array of table names associated with this result. */
0399:            private TableMetaData[] tables;
0400:            /** The descriptor object for the current TDS token. */
0401:            private TdsToken currentToken = new TdsToken();
0402:            /** The stored procedure return status. */
0403:            private Integer returnStatus;
0404:            /** The return parameter meta data object for the current procedure call. */
0405:            private ParamInfo returnParam;
0406:            /** The array of parameter meta data objects for the current procedure call. */
0407:            private ParamInfo[] parameters;
0408:            /** The index of the next output parameter to populate. */
0409:            private int nextParam = -1;
0410:            /** The head of the diagnostic messages chain. */
0411:            private final SQLDiagnostic messages;
0412:            /** Indicates that this object is closed. */
0413:            private boolean isClosed;
0414:            /** Flag that indicates if logon() should try to use Windows Single Sign On using SSPI. */
0415:            private boolean ntlmAuthSSO;
0416:            /** Indicates that a fatal error has occured and the connection will close. */
0417:            private boolean fatalError;
0418:            /** Mutual exclusion lock on connection. */
0419:            private Semaphore connectionLock;
0420:            /** Indicates processing a batch. */
0421:            private boolean inBatch;
0422:            /** Indicates type of SSL connection. */
0423:            private int sslMode = SSL_NO_ENCRYPT;
0424:            /** Indicates pending cancel that needs to be cleared. */
0425:            private boolean cancelPending;
0426:            /** Synchronization monitor for {@link #cancelPending}. */
0427:            private final int[] cancelMonitor = new int[1];
0428:
0429:            /**
0430:             * Construct a TdsCore object.
0431:             *
0432:             * @param connection The connection which owns this object.
0433:             * @param messages The SQLDiagnostic messages chain.
0434:             */
0435:            TdsCore(ConnectionJDBC2 connection, SQLDiagnostic messages) {
0436:                this .connection = connection;
0437:                this .socket = connection.getSocket();
0438:                this .messages = messages;
0439:                serverType = connection.getServerType();
0440:                tdsVersion = socket.getTdsVersion();
0441:                out = socket.getRequestStream(connection.getNetPacketSize(),
0442:                        connection.getMaxPrecision());
0443:                in = socket.getResponseStream(out, connection
0444:                        .getNetPacketSize());
0445:            }
0446:
0447:            /**
0448:             * Check that the connection is still open.
0449:             *
0450:             * @throws SQLException
0451:             */
0452:            private void checkOpen() throws SQLException {
0453:                if (connection.isClosed()) {
0454:                    throw new SQLException(Messages.get("error.generic.closed",
0455:                            "Connection"), "HY010");
0456:                }
0457:            }
0458:
0459:            /**
0460:             * Retrieve the TDS protocol version.
0461:             *
0462:             * @return The protocol version as an <code>int</code>.
0463:             */
0464:            int getTdsVersion() {
0465:                return tdsVersion;
0466:            }
0467:
0468:            /**
0469:             * Retrieve the current result set column descriptors.
0470:             *
0471:             * @return The column descriptors as a <code>ColInfo[]</code>.
0472:             */
0473:            ColInfo[] getColumns() {
0474:                return columns;
0475:            }
0476:
0477:            /**
0478:             * Sets the column meta data.
0479:             *
0480:             * @param columns the column descriptor array
0481:             */
0482:            void setColumns(ColInfo[] columns) {
0483:                this .columns = columns;
0484:                this .rowData = new Object[columns.length];
0485:                this .tables = null;
0486:            }
0487:
0488:            /**
0489:             * Retrieve the parameter meta data from a Sybase prepare.
0490:             *
0491:             * @return The parameter descriptors as a <code>ParamInfo[]</code>.
0492:             */
0493:            ParamInfo[] getParameters() {
0494:                if (currentToken.dynamParamInfo != null) {
0495:                    ParamInfo[] params = new ParamInfo[currentToken.dynamParamInfo.length];
0496:
0497:                    for (int i = 0; i < params.length; i++) {
0498:                        ColInfo ci = currentToken.dynamParamInfo[i];
0499:                        params[i] = new ParamInfo(ci, ci.realName, null, 0);
0500:                    }
0501:
0502:                    return params;
0503:                }
0504:
0505:                return EMPTY_PARAMETER_INFO;
0506:            }
0507:
0508:            /**
0509:             * Retrieve the current result set data items.
0510:             *
0511:             * @return the row data as an <code>Object</code> array
0512:             */
0513:            Object[] getRowData() {
0514:                return rowData;
0515:            }
0516:
0517:            /**
0518:             * Negotiate SSL settings with SQL 2000+ server.
0519:             * <p/>
0520:             * Server returns the following values for SSL mode:
0521:             * <ol>
0522:             * <ll>0 = Certificate installed encrypt login packet only.
0523:             * <li>1 = Certificate installed client requests force encryption.
0524:             * <li>2 = No certificate no encryption possible.
0525:             * <li>3 = Server requests force encryption.
0526:             * </ol>
0527:             * @param instance The server instance name.
0528:             * @param ssl The SSL URL property value.
0529:             * @throws IOException
0530:             */
0531:            void negotiateSSL(String instance, String ssl) throws IOException,
0532:                    SQLException {
0533:                if (!ssl.equalsIgnoreCase(Ssl.SSL_OFF)) {
0534:                    if (ssl.equalsIgnoreCase(Ssl.SSL_REQUIRE)
0535:                            || ssl.equalsIgnoreCase(Ssl.SSL_AUTHENTICATE)) {
0536:                        sendPreLoginPacket(instance, true);
0537:                        sslMode = readPreLoginPacket();
0538:                        if (sslMode != SSL_CLIENT_FORCE_ENCRYPT
0539:                                && sslMode != SSL_SERVER_FORCE_ENCRYPT) {
0540:                            throw new SQLException(Messages
0541:                                    .get("error.ssl.encryptionoff"), "08S01");
0542:                        }
0543:                    } else {
0544:                        sendPreLoginPacket(instance, false);
0545:                        sslMode = readPreLoginPacket();
0546:                    }
0547:                    if (sslMode != SSL_NO_ENCRYPT) {
0548:                        socket.enableEncryption(ssl);
0549:                    }
0550:                }
0551:            }
0552:
0553:            /**
0554:             * Login to the SQL Server.
0555:             *
0556:             * @param serverName server host name
0557:             * @param database   required database
0558:             * @param user       user name
0559:             * @param password   user password
0560:             * @param domain     Windows NT domain (or null)
0561:             * @param charset    required server character set
0562:             * @param appName    application name
0563:             * @param progName   library name
0564:             * @param wsid       workstation ID
0565:             * @param language   language to use for server messages
0566:             * @param macAddress client network MAC address
0567:             * @param packetSize required network packet size
0568:             * @throws SQLException if an error occurs
0569:             */
0570:            void login(final String serverName, final String database,
0571:                    final String user, final String password,
0572:                    final String domain, final String charset,
0573:                    final String appName, final String progName, String wsid,
0574:                    final String language, final String macAddress,
0575:                    final int packetSize) throws SQLException {
0576:                try {
0577:                    if (wsid.length() == 0) {
0578:                        wsid = getHostName();
0579:                    }
0580:                    if (tdsVersion >= Driver.TDS70) {
0581:                        sendMSLoginPkt(serverName, database, user, password,
0582:                                domain, appName, progName, wsid, language,
0583:                                macAddress, packetSize);
0584:                    } else if (tdsVersion == Driver.TDS50) {
0585:                        send50LoginPkt(serverName, user, password, charset,
0586:                                appName, progName, wsid, language, packetSize);
0587:                    } else {
0588:                        send42LoginPkt(serverName, user, password, charset,
0589:                                appName, progName, wsid, language, packetSize);
0590:                    }
0591:                    if (sslMode == SSL_ENCRYPT_LOGIN) {
0592:                        socket.disableEncryption();
0593:                    }
0594:                    nextToken();
0595:
0596:                    while (!endOfResponse) {
0597:                        if (currentToken.isAuthToken()) {
0598:                            sendNtlmChallengeResponse(currentToken.nonce, user,
0599:                                    password, domain);
0600:                        }
0601:
0602:                        nextToken();
0603:                    }
0604:
0605:                    messages.checkErrors();
0606:                } catch (IOException ioe) {
0607:                    throw Support.linkException(
0608:                            new SQLException(Messages.get(
0609:                                    "error.generic.ioerror", ioe.getMessage()),
0610:                                    "08S01"), ioe);
0611:                }
0612:            }
0613:
0614:            /**
0615:             * Get the next result set or update count from the TDS stream.
0616:             *
0617:             * @return <code>boolean</code> if the next item is a result set.
0618:             * @throws SQLException if an I/O or protocol error occurs; server errors
0619:             *                      are queued up and not thrown
0620:             */
0621:            boolean getMoreResults() throws SQLException {
0622:                checkOpen();
0623:                nextToken();
0624:
0625:                while (!endOfResponse && !currentToken.isUpdateCount()
0626:                        && !currentToken.isResultSet()) {
0627:                    nextToken();
0628:                }
0629:
0630:                //
0631:                // Cursor opens are followed by TDS_TAB_INFO and TDS_COL_INFO
0632:                // Process these now so that the column descriptors are updated.
0633:                // Sybase wide result set headers are followed by a TDS_CONTROL_TOKEN
0634:                // skip that as well.
0635:                //
0636:                if (currentToken.isResultSet()) {
0637:                    byte saveToken = currentToken.token;
0638:                    try {
0639:                        byte x = (byte) in.peek();
0640:
0641:                        while (x == TDS_TABNAME_TOKEN || x == TDS_COLINFO_TOKEN
0642:                                || x == TDS_CONTROL_TOKEN) {
0643:                            nextToken();
0644:                            x = (byte) in.peek();
0645:                        }
0646:                    } catch (IOException e) {
0647:                        connection.setClosed();
0648:
0649:                        throw Support.linkException(new SQLException(Messages
0650:                                .get("error.generic.ioerror", e.getMessage()),
0651:                                "08S01"), e);
0652:                    }
0653:                    currentToken.token = saveToken;
0654:                }
0655:
0656:                return currentToken.isResultSet();
0657:            }
0658:
0659:            /**
0660:             * Retrieve the status of the next result item.
0661:             *
0662:             * @return <code>boolean</code> true if the next item is a result set.
0663:             */
0664:            boolean isResultSet() {
0665:                return currentToken.isResultSet();
0666:            }
0667:
0668:            /**
0669:             * Retrieve the status of the next result item.
0670:             *
0671:             * @return <code>boolean</code> true if the next item is row data.
0672:             */
0673:            boolean isRowData() {
0674:                return currentToken.isRowData();
0675:            }
0676:
0677:            /**
0678:             * Retrieve the status of the next result item.
0679:             *
0680:             * @return <code>boolean</code> true if the next item is an update count.
0681:             */
0682:            boolean isUpdateCount() {
0683:                return currentToken.isUpdateCount();
0684:            }
0685:
0686:            /**
0687:             * Retrieve the update count from the current TDS token.
0688:             *
0689:             * @return The update count as an <code>int</code>.
0690:             */
0691:            int getUpdateCount() {
0692:                if (currentToken.isEndToken()) {
0693:                    return currentToken.updateCount;
0694:                }
0695:
0696:                return -1;
0697:            }
0698:
0699:            /**
0700:             * Retrieve the status of the response stream.
0701:             *
0702:             * @return <code>boolean</code> true if the response has been entirely consumed
0703:             */
0704:            boolean isEndOfResponse() {
0705:                return endOfResponse;
0706:            }
0707:
0708:            /**
0709:             * Empty the server response queue.
0710:             *
0711:             * @throws SQLException if an error occurs
0712:             */
0713:            void clearResponseQueue() throws SQLException {
0714:                checkOpen();
0715:                while (!endOfResponse) {
0716:                    nextToken();
0717:                }
0718:            }
0719:
0720:            /**
0721:             * Consume packets from the server response queue up to (and including) the
0722:             * first response terminator.
0723:             *
0724:             * @throws SQLException if an I/O or protocol error occurs; server errors
0725:             *                      are queued up and not thrown
0726:             */
0727:            void consumeOneResponse() throws SQLException {
0728:                checkOpen();
0729:                while (!endOfResponse) {
0730:                    nextToken();
0731:                    // If it's a response terminator, return
0732:                    if (currentToken.isEndToken()
0733:                            && (currentToken.status & DONE_END_OF_RESPONSE) != 0) {
0734:                        return;
0735:                    }
0736:                }
0737:            }
0738:
0739:            /**
0740:             * Retrieve the next data row from the result set.
0741:             *
0742:             * @return <code>false</code> if at the end of results, <code>true</code>
0743:             *         otherwise
0744:             * @throws SQLException if an I/O or protocol error occurs; server errors
0745:             *                      are queued up and not thrown
0746:             */
0747:            boolean getNextRow() throws SQLException {
0748:                if (endOfResponse || endOfResults) {
0749:                    return false;
0750:                }
0751:                checkOpen();
0752:                nextToken();
0753:
0754:                // Will either be first or next data row or end.
0755:                while (!currentToken.isRowData() && !currentToken.isEndToken()) {
0756:                    nextToken(); // Could be messages
0757:                }
0758:
0759:                return currentToken.isRowData();
0760:            }
0761:
0762:            /**
0763:             * Retrieve the status of result set.
0764:             * <p>
0765:             * This does a quick read ahead and is needed to support the isLast()
0766:             * method in the ResultSet.
0767:             *
0768:             * @return <code>boolean</code> - <code>true</code> if there is more data
0769:             *          in the result set.
0770:             */
0771:            boolean isDataInResultSet() throws SQLException {
0772:                byte x;
0773:
0774:                checkOpen();
0775:
0776:                try {
0777:                    x = (endOfResponse) ? TDS_DONE_TOKEN : (byte) in.peek();
0778:
0779:                    while (x != TDS_ROW_TOKEN && x != TDS_DONE_TOKEN
0780:                            && x != TDS_DONEINPROC_TOKEN
0781:                            && x != TDS_DONEPROC_TOKEN) {
0782:                        nextToken();
0783:                        x = (byte) in.peek();
0784:                    }
0785:
0786:                    messages.checkErrors();
0787:                } catch (IOException e) {
0788:                    connection.setClosed();
0789:                    throw Support.linkException(new SQLException(Messages.get(
0790:                            "error.generic.ioerror", e.getMessage()), "08S01"),
0791:                            e);
0792:                }
0793:
0794:                return x == TDS_ROW_TOKEN;
0795:            }
0796:
0797:            /**
0798:             * Retrieve the return status for the current stored procedure.
0799:             *
0800:             * @return The return status as an <code>Integer</code>.
0801:             */
0802:            Integer getReturnStatus() {
0803:                return this .returnStatus;
0804:            }
0805:
0806:            /**
0807:             * Inform the server that this connection is closing.
0808:             * <p>
0809:             * Used by Sybase a no-op for Microsoft.
0810:             */
0811:            synchronized void closeConnection() {
0812:                try {
0813:                    if (tdsVersion == Driver.TDS50) {
0814:                        socket.setTimeout(1000);
0815:                        out.setPacketType(SYBQUERY_PKT);
0816:                        out.write((byte) TDS_CLOSE_TOKEN);
0817:                        out.write((byte) 0);
0818:                        out.flush();
0819:                        endOfResponse = false;
0820:                        clearResponseQueue();
0821:                    }
0822:                } catch (Exception e) {
0823:                    // Ignore any exceptions as this connection
0824:                    // is closing anyway.
0825:                }
0826:            }
0827:
0828:            /**
0829:             * Close the <code>TdsCore</code> connection object and associated streams.
0830:             */
0831:            void close() throws SQLException {
0832:                if (!isClosed) {
0833:                    try {
0834:                        clearResponseQueue();
0835:                        out.close();
0836:                        in.close();
0837:                    } finally {
0838:                        isClosed = true;
0839:                    }
0840:                }
0841:            }
0842:
0843:            /**
0844:             * Send (only) one cancel packet to the server.
0845:             *
0846:             * @param timeout true if this is a query timeout cancel
0847:             */
0848:            void cancel(boolean timeout) {
0849:                Semaphore mutex = null;
0850:                try {
0851:                    mutex = connection.getMutex();
0852:                    synchronized (cancelMonitor) {
0853:                        if (!cancelPending && !endOfResponse) {
0854:                            cancelPending = socket.cancel(out.getStreamId());
0855:                        }
0856:                        // If a cancel request was sent, reset the end of response flag
0857:                        if (cancelPending) {
0858:                            cancelMonitor[0] = timeout ? TIMEOUT_CANCEL
0859:                                    : ASYNC_CANCEL;
0860:                            endOfResponse = false;
0861:                        }
0862:                    }
0863:                } finally {
0864:                    if (mutex != null) {
0865:                        mutex.release();
0866:                    }
0867:                }
0868:            }
0869:
0870:            /**
0871:             * Submit a simple SQL statement to the server and process all output.
0872:             *
0873:             * @param sql the statement to execute
0874:             * @throws SQLException if an error is returned by the server
0875:             */
0876:            void submitSQL(String sql) throws SQLException {
0877:                checkOpen();
0878:                messages.clearWarnings();
0879:
0880:                if (sql.length() == 0) {
0881:                    throw new IllegalArgumentException(
0882:                            "submitSQL() called with empty SQL String");
0883:                }
0884:
0885:                executeSQL(sql, null, null, false, 0, -1, -1, true);
0886:                clearResponseQueue();
0887:                messages.checkErrors();
0888:            }
0889:
0890:            /**
0891:             * Notifies the <code>TdsCore</code> that a batch is starting. This is so
0892:             * that it knows to use <code>sp_executesql</code> for parameterized
0893:             * queries (because there's no way to prepare a statement in the middle of
0894:             * a batch).
0895:             * <p>
0896:             * Sets the {@link #inBatch} flag.
0897:             */
0898:            void startBatch() {
0899:                inBatch = true;
0900:            }
0901:
0902:            /**
0903:             * Send an SQL statement with optional parameters to the server.
0904:             *
0905:             * @param sql          SQL statement to execute
0906:             * @param procName     stored procedure to execute or <code>null</code>
0907:             * @param parameters   parameters for call or null
0908:             * @param noMetaData   suppress meta data for cursor calls
0909:             * @param timeOut      optional query timeout or 0
0910:             * @param maxRows      the maximum number of data rows to return (-1 to
0911:             *                     leave unaltered)
0912:             * @param maxFieldSize the maximum number of bytes in a column to return
0913:             *                     (-1 to leave unaltered)
0914:             * @param sendNow      whether to send the request now or not
0915:             * @throws SQLException if an error occurs
0916:             */
0917:            synchronized void executeSQL(String sql, String procName,
0918:                    ParamInfo[] parameters, boolean noMetaData, int timeOut,
0919:                    int maxRows, int maxFieldSize, boolean sendNow)
0920:                    throws SQLException {
0921:                boolean sendFailed = true; // Used to ensure mutex is released.
0922:
0923:                try {
0924:                    //
0925:                    // Obtain a lock on the connection giving exclusive access
0926:                    // to the network connection for this thread
0927:                    //
0928:                    if (connectionLock == null) {
0929:                        connectionLock = connection.getMutex();
0930:                    }
0931:                    // Also checks if connection is open
0932:                    clearResponseQueue();
0933:                    messages.exceptions = null;
0934:
0935:                    //
0936:                    // Set the connection row count and text size if required.
0937:                    // Once set these will not be changed within a
0938:                    // batch so execution of the set rows query will
0939:                    // only occur once a the start of a batch.
0940:                    // No other thread can send until this one has finished.
0941:                    //
0942:                    setRowCountAndTextSize(maxRows, maxFieldSize);
0943:
0944:                    messages.clearWarnings();
0945:                    this .returnStatus = null;
0946:                    //
0947:                    // Normalize the parameters argument to simplify later checks
0948:                    //
0949:                    if (parameters != null && parameters.length == 0) {
0950:                        parameters = null;
0951:                    }
0952:                    this .parameters = parameters;
0953:                    //
0954:                    // Normalise the procName argument as well
0955:                    //
0956:                    if (procName != null && procName.length() == 0) {
0957:                        procName = null;
0958:                    }
0959:
0960:                    if (parameters != null && parameters[0].isRetVal) {
0961:                        returnParam = parameters[0];
0962:                        nextParam = 0;
0963:                    } else {
0964:                        returnParam = null;
0965:                        nextParam = -1;
0966:                    }
0967:
0968:                    if (parameters != null) {
0969:                        if (procName == null && sql.startsWith("EXECUTE ")) {
0970:                            //
0971:                            // If this is a callable statement that could not be fully parsed
0972:                            // into an RPC call convert to straight SQL now.
0973:                            // An example of non RPC capable SQL is {?=call sp_example('literal', ?)}
0974:                            //
0975:                            for (int i = 0; i < parameters.length; i++) {
0976:                                // Output parameters not allowed.
0977:                                if (!parameters[i].isRetVal
0978:                                        && parameters[i].isOutput) {
0979:                                    throw new SQLException(Messages.get(
0980:                                            "error.prepare.nooutparam", Integer
0981:                                                    .toString(i + 1)), "07000");
0982:                                }
0983:                            }
0984:                            sql = Support.substituteParameters(sql, parameters,
0985:                                    connection);
0986:                            parameters = null;
0987:                        } else {
0988:                            //
0989:                            // Check all parameters are either output or have values set
0990:                            //
0991:                            for (int i = 0; i < parameters.length; i++) {
0992:                                if (!parameters[i].isSet
0993:                                        && !parameters[i].isOutput) {
0994:                                    throw new SQLException(Messages.get(
0995:                                            "error.prepare.paramnotset",
0996:                                            Integer.toString(i + 1)), "07000");
0997:                                }
0998:                                parameters[i].clearOutValue();
0999:                                // FIXME Should only set TDS type if not already set
1000:                                // but we might need to take a lot of care not to
1001:                                // exceed size limitations (e.g. write 11 chars in a
1002:                                // VARCHAR(10) )
1003:                                TdsData
1004:                                        .getNativeType(connection,
1005:                                                parameters[i]);
1006:                            }
1007:                        }
1008:                    }
1009:
1010:                    try {
1011:                        switch (tdsVersion) {
1012:                        case Driver.TDS42:
1013:                            executeSQL42(sql, procName, parameters, noMetaData,
1014:                                    sendNow);
1015:                            break;
1016:                        case Driver.TDS50:
1017:                            executeSQL50(sql, procName, parameters);
1018:                            break;
1019:                        case Driver.TDS70:
1020:                        case Driver.TDS80:
1021:                        case Driver.TDS81:
1022:                            executeSQL70(sql, procName, parameters, noMetaData,
1023:                                    sendNow);
1024:                            break;
1025:                        default:
1026:                            throw new IllegalStateException(
1027:                                    "Unknown TDS version " + tdsVersion);
1028:                        }
1029:
1030:                        if (sendNow) {
1031:                            out.flush();
1032:                            connectionLock.release();
1033:                            connectionLock = null;
1034:                            sendFailed = false;
1035:                            endOfResponse = false;
1036:                            endOfResults = true;
1037:                            wait(timeOut);
1038:                        } else {
1039:                            sendFailed = false;
1040:                        }
1041:                    } catch (IOException ioe) {
1042:                        connection.setClosed();
1043:
1044:                        throw Support.linkException(new SQLException(
1045:                                Messages.get("error.generic.ioerror", ioe
1046:                                        .getMessage()), "08S01"), ioe);
1047:                    }
1048:                } finally {
1049:                    if ((sendNow || sendFailed) && connectionLock != null) {
1050:                        connectionLock.release();
1051:                        connectionLock = null;
1052:                    }
1053:                    // Clear the in batch flag
1054:                    if (sendNow) {
1055:                        inBatch = false;
1056:                    }
1057:                }
1058:            }
1059:
1060:            /**
1061:             * Prepares the SQL for use with Microsoft server.
1062:             *
1063:             * @param sql                  the SQL statement to prepare.
1064:             * @param params               the actual parameter list
1065:             * @param needCursor           true if a cursorprepare is required
1066:             * @param resultSetType        value of the resultSetType parameter when
1067:             *                             the Statement was created
1068:             * @param resultSetConcurrency value of the resultSetConcurrency parameter
1069:             *                             whenthe Statement was created
1070:             * @return name of the procedure or prepared statement handle.
1071:             * @exception SQLException
1072:             */
1073:            String microsoftPrepare(String sql, ParamInfo[] params,
1074:                    boolean needCursor, int resultSetType,
1075:                    int resultSetConcurrency) throws SQLException {
1076:                //
1077:                checkOpen();
1078:                messages.clearWarnings();
1079:
1080:                int prepareSql = connection.getPrepareSql();
1081:
1082:                if (prepareSql == TEMPORARY_STORED_PROCEDURES) {
1083:                    StringBuffer spSql = new StringBuffer(sql.length() + 32
1084:                            + params.length * 15);
1085:                    String procName = connection.getProcName();
1086:
1087:                    spSql.append("create proc ");
1088:                    spSql.append(procName);
1089:                    spSql.append(' ');
1090:
1091:                    for (int i = 0; i < params.length; i++) {
1092:                        spSql.append("@P");
1093:                        spSql.append(i);
1094:                        spSql.append(' ');
1095:                        spSql.append(params[i].sqlType);
1096:
1097:                        if (i + 1 < params.length) {
1098:                            spSql.append(',');
1099:                        }
1100:                    }
1101:
1102:                    // continue building proc
1103:                    spSql.append(" as ");
1104:                    spSql.append(Support.substituteParamMarkers(sql, params));
1105:
1106:                    try {
1107:                        submitSQL(spSql.toString());
1108:                        return procName;
1109:                    } catch (SQLException e) {
1110:                        if ("08S01".equals(e.getSQLState())) {
1111:                            // Serious (I/O) error, rethrow
1112:                            throw e;
1113:                        }
1114:
1115:                        // This exception probably caused by failure to prepare
1116:                        // Add a warning
1117:                        messages.addWarning(Support.linkException(
1118:                                new SQLWarning(Messages.get(
1119:                                        "error.prepare.prepfailed", e
1120:                                                .getMessage()),
1121:                                        e.getSQLState(), e.getErrorCode()), e));
1122:                    }
1123:
1124:                } else if (prepareSql == PREPARE) {
1125:                    int scrollOpt, ccOpt;
1126:
1127:                    ParamInfo prepParam[] = new ParamInfo[needCursor ? 6 : 4];
1128:
1129:                    // Setup prepare handle param
1130:                    prepParam[0] = new ParamInfo(Types.INTEGER, null,
1131:                            ParamInfo.OUTPUT);
1132:
1133:                    // Setup parameter descriptor param
1134:                    prepParam[1] = new ParamInfo(Types.LONGVARCHAR, Support
1135:                            .getParameterDefinitions(params), ParamInfo.UNICODE);
1136:
1137:                    // Setup sql statemement param
1138:                    prepParam[2] = new ParamInfo(Types.LONGVARCHAR, Support
1139:                            .substituteParamMarkers(sql, params),
1140:                            ParamInfo.UNICODE);
1141:
1142:                    // Setup options param
1143:                    prepParam[3] = new ParamInfo(Types.INTEGER, new Integer(1),
1144:                            ParamInfo.INPUT);
1145:
1146:                    if (needCursor) {
1147:                        // Select the correct type of Server side cursor to
1148:                        // match the scroll and concurrency options.
1149:                        scrollOpt = MSCursorResultSet.getCursorScrollOpt(
1150:                                resultSetType, resultSetConcurrency, true);
1151:                        ccOpt = MSCursorResultSet
1152:                                .getCursorConcurrencyOpt(resultSetConcurrency);
1153:
1154:                        // Setup scroll options parameter
1155:                        prepParam[4] = new ParamInfo(Types.INTEGER,
1156:                                new Integer(scrollOpt), ParamInfo.OUTPUT);
1157:
1158:                        // Setup concurrency options parameter
1159:                        prepParam[5] = new ParamInfo(Types.INTEGER,
1160:                                new Integer(ccOpt), ParamInfo.OUTPUT);
1161:                    }
1162:
1163:                    columns = null; // Will be populated if preparing a select
1164:                    try {
1165:                        executeSQL(null, needCursor ? "sp_cursorprepare"
1166:                                : "sp_prepare", prepParam, false, 0, -1, -1,
1167:                                true);
1168:
1169:                        int resultCount = 0;
1170:                        while (!endOfResponse) {
1171:                            nextToken();
1172:                            if (isResultSet()) {
1173:                                resultCount++;
1174:                            }
1175:                        }
1176:                        // columns will now hold meta data for any select statements
1177:                        if (resultCount != 1) {
1178:                            // More than one result set was returned or none
1179:                            // therefore metadata not available or unsafe.
1180:                            columns = null;
1181:                        }
1182:                        Integer prepareHandle = (Integer) prepParam[0]
1183:                                .getOutValue();
1184:                        if (prepareHandle != null) {
1185:                            return prepareHandle.toString();
1186:                        }
1187:                        // Probably an exception occured, check for it
1188:                        messages.checkErrors();
1189:                    } catch (SQLException e) {
1190:                        if ("08S01".equals(e.getSQLState())) {
1191:                            // Serious (I/O) error, rethrow
1192:                            throw e;
1193:                        }
1194:                        // This exception probably caused by failure to prepare
1195:                        // Add a warning
1196:                        messages.addWarning(Support.linkException(
1197:                                new SQLWarning(Messages.get(
1198:                                        "error.prepare.prepfailed", e
1199:                                                .getMessage()),
1200:                                        e.getSQLState(), e.getErrorCode()), e));
1201:                    }
1202:                }
1203:
1204:                return null;
1205:            }
1206:
1207:            /**
1208:             * Creates a light weight stored procedure on a Sybase server.
1209:             *
1210:             * @param sql    SQL statement to prepare
1211:             * @param params the actual parameter list
1212:             * @return name of the procedure
1213:             * @throws SQLException if an error occurs
1214:             */
1215:            synchronized String sybasePrepare(String sql, ParamInfo[] params)
1216:                    throws SQLException {
1217:                checkOpen();
1218:                messages.clearWarnings();
1219:                if (sql == null || sql.length() == 0) {
1220:                    throw new IllegalArgumentException(
1221:                            "sql parameter must be at least 1 character long.");
1222:                }
1223:
1224:                String procName = connection.getProcName();
1225:
1226:                if (procName == null || procName.length() != 11) {
1227:                    throw new IllegalArgumentException(
1228:                            "procName parameter must be 11 characters long.");
1229:                }
1230:
1231:                // TODO Check if output parameters are handled ok
1232:                // Check no text/image parameters
1233:                for (int i = 0; i < params.length; i++) {
1234:                    if ("text".equals(params[i].sqlType)
1235:                            || "unitext".equals(params[i].sqlType)
1236:                            || "image".equals(params[i].sqlType)) {
1237:                        return null; // Sadly no way
1238:                    }
1239:                }
1240:
1241:                Semaphore mutex = null;
1242:
1243:                try {
1244:                    mutex = connection.getMutex();
1245:
1246:                    out.setPacketType(SYBQUERY_PKT);
1247:                    out.write((byte) TDS5_DYNAMIC_TOKEN);
1248:
1249:                    byte buf[] = Support.encodeString(connection.getCharset(),
1250:                            sql);
1251:
1252:                    out.write((short) (buf.length + 41));
1253:                    out.write((byte) 1);
1254:                    out.write((byte) 0);
1255:                    out.write((byte) 10);
1256:                    out.writeAscii(procName.substring(1));
1257:                    out.write((short) (buf.length + 26));
1258:                    out.writeAscii("create proc ");
1259:                    out.writeAscii(procName.substring(1));
1260:                    out.writeAscii(" as ");
1261:                    out.write(buf);
1262:                    out.flush();
1263:                    endOfResponse = false;
1264:                    clearResponseQueue();
1265:                    messages.checkErrors();
1266:                    return procName;
1267:                } catch (IOException ioe) {
1268:                    connection.setClosed();
1269:                    throw Support.linkException(
1270:                            new SQLException(Messages.get(
1271:                                    "error.generic.ioerror", ioe.getMessage()),
1272:                                    "08S01"), ioe);
1273:                } catch (SQLException e) {
1274:                    if ("08S01".equals(e.getSQLState())) {
1275:                        // Serious error rethrow
1276:                        throw e;
1277:                    }
1278:
1279:                    // This exception probably caused by failure to prepare
1280:                    // Return null;
1281:                    return null;
1282:                } finally {
1283:                    if (mutex != null) {
1284:                        mutex.release();
1285:                    }
1286:                }
1287:            }
1288:
1289:            /**
1290:             * Drops a Sybase temporary stored procedure.
1291:             *
1292:             * @param procName the temporary procedure name
1293:             * @throws SQLException if an error occurs
1294:             */
1295:            synchronized void sybaseUnPrepare(String procName)
1296:                    throws SQLException {
1297:                checkOpen();
1298:                messages.clearWarnings();
1299:
1300:                if (procName == null || procName.length() != 11) {
1301:                    throw new IllegalArgumentException(
1302:                            "procName parameter must be 11 characters long.");
1303:                }
1304:
1305:                Semaphore mutex = null;
1306:                try {
1307:                    mutex = connection.getMutex();
1308:
1309:                    out.setPacketType(SYBQUERY_PKT);
1310:                    out.write((byte) TDS5_DYNAMIC_TOKEN);
1311:                    out.write((short) (15));
1312:                    out.write((byte) 4);
1313:                    out.write((byte) 0);
1314:                    out.write((byte) 10);
1315:                    out.writeAscii(procName.substring(1));
1316:                    out.write((short) 0);
1317:                    out.flush();
1318:                    endOfResponse = false;
1319:                    clearResponseQueue();
1320:                    messages.checkErrors();
1321:                } catch (IOException ioe) {
1322:                    connection.setClosed();
1323:                    throw Support.linkException(
1324:                            new SQLException(Messages.get(
1325:                                    "error.generic.ioerror", ioe.getMessage()),
1326:                                    "08S01"), ioe);
1327:                } catch (SQLException e) {
1328:                    if ("08S01".equals(e.getSQLState())) {
1329:                        // Serious error rethrow
1330:                        throw e;
1331:                    }
1332:                    // This exception probably caused by failure to unprepare
1333:                } finally {
1334:                    if (mutex != null) {
1335:                        mutex.release();
1336:                    }
1337:                }
1338:            }
1339:
1340:            /**
1341:             * Enlist the current connection in a distributed transaction or request the location of the
1342:             * MSDTC instance controlling the server we are connected to.
1343:             *
1344:             * @param type      set to 0 to request TM address or 1 to enlist connection
1345:             * @param oleTranID the 40 OLE transaction ID
1346:             * @return a <code>byte[]</code> array containing the TM address data
1347:             * @throws SQLException
1348:             */
1349:            synchronized byte[] enlistConnection(int type, byte[] oleTranID)
1350:                    throws SQLException {
1351:                Semaphore mutex = null;
1352:                try {
1353:                    mutex = connection.getMutex();
1354:
1355:                    out.setPacketType(MSDTC_PKT);
1356:                    out.write((short) type);
1357:                    switch (type) {
1358:                    case 0: // Get result set with location of MSTDC
1359:                        out.write((short) 0);
1360:                        break;
1361:                    case 1: // Set OLE transaction ID
1362:                        if (oleTranID != null) {
1363:                            out.write((short) oleTranID.length);
1364:                            out.write(oleTranID);
1365:                        } else {
1366:                            // Delist the connection from all transactions.
1367:                            out.write((short) 0);
1368:                        }
1369:                        break;
1370:                    }
1371:                    out.flush();
1372:                    endOfResponse = false;
1373:                    endOfResults = true;
1374:                } catch (IOException ioe) {
1375:                    connection.setClosed();
1376:                    throw Support.linkException(
1377:                            new SQLException(Messages.get(
1378:                                    "error.generic.ioerror", ioe.getMessage()),
1379:                                    "08S01"), ioe);
1380:                } finally {
1381:                    if (mutex != null) {
1382:                        mutex.release();
1383:                    }
1384:                }
1385:
1386:                byte[] tmAddress = null;
1387:                if (getMoreResults() && getNextRow()) {
1388:                    if (rowData.length == 1) {
1389:                        Object x = rowData[0];
1390:                        if (x instanceof  byte[]) {
1391:                            tmAddress = (byte[]) x;
1392:                        }
1393:                    }
1394:                }
1395:
1396:                clearResponseQueue();
1397:                messages.checkErrors();
1398:                return tmAddress;
1399:            }
1400:
1401:            /**
1402:             * Obtain the counts from a batch of SQL updates.
1403:             * <p/>
1404:             * If an error occurs Sybase will continue processing a batch consisting of
1405:             * TDS_LANGUAGE records whilst SQL Server will usually stop after the first
1406:             * error except when the error is caused by a duplicate key.
1407:             * Sybase will also stop after the first error when executing RPC calls.
1408:             * Care is taken to ensure that <code>SQLException</code>s are chained
1409:             * because there could be several errors reported in a batch.
1410:             *
1411:             * @param counts the <code>ArrayList</code> containing the update counts
1412:             * @param sqlEx  any previous <code>SQLException</code>(s) encountered
1413:             * @return updated <code>SQLException</code> or <code>null</code> if no
1414:             *         error has yet occured
1415:             */
1416:            SQLException getBatchCounts(ArrayList counts, SQLException sqlEx) {
1417:                Integer lastCount = JtdsStatement.SUCCESS_NO_INFO;
1418:
1419:                try {
1420:                    checkOpen();
1421:                    while (!endOfResponse) {
1422:                        nextToken();
1423:                        if (currentToken.isResultSet()) {
1424:                            // Serious error, statement must not return a result set
1425:                            throw new SQLException(Messages
1426:                                    .get("error.statement.batchnocount"),
1427:                                    "07000");
1428:                        }
1429:                        //
1430:                        // Analyse type of end token and try to extract correct
1431:                        // update count when calling stored procs.
1432:                        //
1433:                        switch (currentToken.token) {
1434:                        case TDS_DONE_TOKEN:
1435:                            if ((currentToken.status & DONE_ERROR) != 0
1436:                                    || lastCount == JtdsStatement.EXECUTE_FAILED) {
1437:                                counts.add(JtdsStatement.EXECUTE_FAILED);
1438:                            } else {
1439:                                if (currentToken.isUpdateCount()) {
1440:                                    counts.add(new Integer(
1441:                                            currentToken.updateCount));
1442:                                } else {
1443:                                    counts.add(lastCount);
1444:                                }
1445:                            }
1446:                            lastCount = JtdsStatement.SUCCESS_NO_INFO;
1447:                            break;
1448:                        case TDS_DONEINPROC_TOKEN:
1449:                            if ((currentToken.status & DONE_ERROR) != 0) {
1450:                                lastCount = JtdsStatement.EXECUTE_FAILED;
1451:                            } else if (currentToken.isUpdateCount()) {
1452:                                lastCount = new Integer(
1453:                                        currentToken.updateCount);
1454:                            }
1455:                            break;
1456:                        case TDS_DONEPROC_TOKEN:
1457:                            if ((currentToken.status & DONE_ERROR) != 0
1458:                                    || lastCount == JtdsStatement.EXECUTE_FAILED) {
1459:                                counts.add(JtdsStatement.EXECUTE_FAILED);
1460:                            } else {
1461:                                counts.add(lastCount);
1462:                            }
1463:                            lastCount = JtdsStatement.SUCCESS_NO_INFO;
1464:                            break;
1465:                        }
1466:                    }
1467:                    //
1468:                    // Check for any exceptions
1469:                    //
1470:                    messages.checkErrors();
1471:
1472:                } catch (SQLException e) {
1473:                    //
1474:                    // Chain all exceptions
1475:                    //
1476:                    if (sqlEx != null) {
1477:                        sqlEx.setNextException(e);
1478:                    } else {
1479:                        sqlEx = e;
1480:                    }
1481:                } finally {
1482:                    while (!endOfResponse) {
1483:                        // Flush rest of response
1484:                        try {
1485:                            nextToken();
1486:                        } catch (SQLException ex) {
1487:                            // Chain any exceptions to the BatchUpdateException
1488:                            if (sqlEx != null) {
1489:                                sqlEx.setNextException(ex);
1490:                            } else {
1491:                                sqlEx = ex;
1492:                            }
1493:                        }
1494:                    }
1495:                }
1496:
1497:                return sqlEx;
1498:            }
1499:
1500:            // ---------------------- Private Methods from here ---------------------
1501:
1502:            /**
1503:             * Write a TDS login packet string. Text followed by padding followed
1504:             * by a byte sized length.
1505:             */
1506:            private void putLoginString(String txt, int len) throws IOException {
1507:                byte[] tmp = Support.encodeString(connection.getCharset(), txt);
1508:                out.write(tmp, 0, len);
1509:                out.write((byte) (tmp.length < len ? tmp.length : len));
1510:            }
1511:
1512:            /**
1513:             * Send the SQL Server 2000 pre login packet.
1514:             * <p>Packet contains; netlib version, ssl mode, instance
1515:             * and process ID.
1516:             * @param instance
1517:             * @param forceEncryption
1518:             * @throws IOException
1519:             */
1520:            private void sendPreLoginPacket(String instance,
1521:                    boolean forceEncryption) throws IOException {
1522:                out.setPacketType(PRELOGIN_PKT);
1523:                // Write Netlib pointer
1524:                out.write((short) 0);
1525:                out.write((short) 21);
1526:                out.write((byte) 6);
1527:                // Write Encrypt flag pointer
1528:                out.write((short) 1);
1529:                out.write((short) 27);
1530:                out.write((byte) 1);
1531:                // Write Instance name pointer
1532:                out.write((short) 2);
1533:                out.write((short) 28);
1534:                out.write((byte) (instance.length() + 1));
1535:                // Write process ID pointer
1536:                out.write((short) 3);
1537:                out.write((short) (28 + instance.length() + 1));
1538:                out.write((byte) 4);
1539:                // Write terminator
1540:                out.write((byte) 0xFF);
1541:                // Write fake net lib ID 8.341.0
1542:                out.write(new byte[] { 0x08, 0x00, 0x01, 0x55, 0x00, 0x00 });
1543:                // Write force encryption flag
1544:                out.write((byte) (forceEncryption ? 1 : 0));
1545:                // Write instance name
1546:                out.writeAscii(instance);
1547:                out.write((byte) 0);
1548:                // Write dummy process ID
1549:                out.write(new byte[] { 0x01, 0x02, 0x00, 0x00 });
1550:                //
1551:                out.flush();
1552:            }
1553:
1554:            /**
1555:             * Process the pre login acknowledgement from the server.
1556:             * <p>Packet contains; server version no, SSL mode, instance name
1557:             * and process id.
1558:             * <p>Server returns the following values for SSL mode:
1559:             * <ol>
1560:             * <ll>0 = Certificate installed encrypt login packet only.
1561:             * <li>1 = Certificate installed client requests force encryption.
1562:             * <li>2 = No certificate no encryption possible.
1563:             * <li>3 = Server requests force encryption.
1564:             * </ol>
1565:             * @return The server side SSL mode.
1566:             * @throws IOException
1567:             */
1568:            private int readPreLoginPacket() throws IOException {
1569:                byte list[][] = new byte[8][];
1570:                byte data[][] = new byte[8][];
1571:                int recordCount = 0;
1572:
1573:                byte record[] = new byte[5];
1574:                // Read entry pointers
1575:                record[0] = (byte) in.read();
1576:                while ((record[0] & 0xFF) != 0xFF) {
1577:                    if (recordCount == list.length) {
1578:                        throw new IOException(
1579:                                "Pre Login packet has more than 8 entries");
1580:                    }
1581:                    // Read record
1582:                    in.read(record, 1, 4);
1583:                    list[recordCount++] = record;
1584:                    record = new byte[5];
1585:                    record[0] = (byte) in.read();
1586:                }
1587:                // Read entry data
1588:                for (int i = 0; i < recordCount; i++) {
1589:                    byte value[] = new byte[(byte) list[i][4]];
1590:                    in.read(value);
1591:                    data[i] = value;
1592:                }
1593:                if (Logger.isActive()) {
1594:                    // Diagnostic dump
1595:                    Logger.println("PreLogin server response");
1596:                    for (int i = 0; i < recordCount; i++) {
1597:                        Logger.println("Record " + i + " = "
1598:                                + Support.toHex(data[i]));
1599:                    }
1600:                }
1601:                if (recordCount > 1) {
1602:                    return data[1][0]; // This is the server side SSL mode
1603:                } else {
1604:                    // Response too short to include SSL mode!
1605:                    return SSL_NO_ENCRYPT;
1606:                }
1607:            }
1608:
1609:            /**
1610:             * TDS 4.2 Login Packet.
1611:             *
1612:             * @param serverName server host name
1613:             * @param user       user name
1614:             * @param password   user password
1615:             * @param charset    required server character set
1616:             * @param appName    application name
1617:             * @param progName   program name
1618:             * @param wsid       workstation ID
1619:             * @param language   server language for messages
1620:             * @param packetSize required network packet size
1621:             * @throws IOException if an I/O error occurs
1622:             */
1623:            private void send42LoginPkt(final String serverName,
1624:                    final String user, final String password,
1625:                    final String charset, final String appName,
1626:                    final String progName, final String wsid,
1627:                    final String language, final int packetSize)
1628:                    throws IOException {
1629:                final byte[] empty = new byte[0];
1630:
1631:                out.setPacketType(LOGIN_PKT);
1632:                putLoginString(wsid, 30); // Host name
1633:                putLoginString(user, 30); // user name
1634:                putLoginString(password, 30); // password
1635:                putLoginString("00000123", 30); // hostproc (offset 93 0x5d)
1636:
1637:                out.write((byte) 3); // type of int2
1638:                out.write((byte) 1); // type of int4
1639:                out.write((byte) 6); // type of char
1640:                out.write((byte) 10);// type of flt
1641:                out.write((byte) 9); // type of date
1642:                out.write((byte) 1); // notify of use db
1643:                out.write((byte) 1); // disallow dump/load and bulk insert
1644:                out.write((byte) 0); // sql interface type
1645:                out.write((byte) 0); // type of network connection
1646:
1647:                out.write(empty, 0, 7);
1648:
1649:                putLoginString(appName, 30); // appname
1650:                putLoginString(serverName, 30); // server name
1651:
1652:                out.write((byte) 0); // remote passwords
1653:                out.write((byte) password.length());
1654:                byte[] tmp = Support.encodeString(connection.getCharset(),
1655:                        password);
1656:                out.write(tmp, 0, 253);
1657:                out.write((byte) (tmp.length + 2));
1658:
1659:                out.write((byte) 4); // tds version
1660:                out.write((byte) 2);
1661:
1662:                out.write((byte) 0);
1663:                out.write((byte) 0);
1664:                putLoginString(progName, 10); // prog name
1665:
1666:                out.write((byte) 6); // prog version
1667:                out.write((byte) 0);
1668:                out.write((byte) 0);
1669:                out.write((byte) 0);
1670:
1671:                out.write((byte) 0); // auto convert short
1672:                out.write((byte) 0x0D); // type of flt4
1673:                out.write((byte) 0x11); // type of date4
1674:
1675:                putLoginString(language, 30); // language
1676:
1677:                out.write((byte) 1); // notify on lang change
1678:                out.write((short) 0); // security label hierachy
1679:                out.write((byte) 0); // security encrypted
1680:                out.write(empty, 0, 8); // security components
1681:                out.write((short) 0); // security spare
1682:
1683:                putLoginString(charset, 30); // Character set
1684:
1685:                out.write((byte) 1); // notify on charset change
1686:                putLoginString(String.valueOf(packetSize), 6); // length of tds packets
1687:
1688:                out.write(empty, 0, 8); // pad out to a longword
1689:
1690:                out.flush(); // Send the packet
1691:                endOfResponse = false;
1692:            }
1693:
1694:            /**
1695:             * TDS 5.0 Login Packet.
1696:             * <P>
1697:             * @param serverName server host name
1698:             * @param user       user name
1699:             * @param password   user password
1700:             * @param charset    required server character set
1701:             * @param appName    application name
1702:             * @param progName   library name
1703:             * @param wsid       workstation ID
1704:             * @param language   server language for messages
1705:             * @param packetSize required network packet size
1706:             * @throws IOException if an I/O error occurs
1707:             */
1708:            private void send50LoginPkt(final String serverName,
1709:                    final String user, final String password,
1710:                    final String charset, final String appName,
1711:                    final String progName, final String wsid,
1712:                    final String language, final int packetSize)
1713:                    throws IOException {
1714:                final byte[] empty = new byte[0];
1715:
1716:                out.setPacketType(LOGIN_PKT);
1717:                putLoginString(wsid, 30); // Host name
1718:                putLoginString(user, 30); // user name
1719:                putLoginString(password, 30); // password
1720:                putLoginString("00000123", 30); // hostproc (offset 93 0x5d)
1721:
1722:                out.write((byte) 3); // type of int2
1723:                out.write((byte) 1); // type of int4
1724:                out.write((byte) 6); // type of char
1725:                out.write((byte) 10);// type of flt
1726:                out.write((byte) 9); // type of date
1727:                out.write((byte) 1); // notify of use db
1728:                out.write((byte) 1); // disallow dump/load and bulk insert
1729:                out.write((byte) 0); // sql interface type
1730:                out.write((byte) 0); // type of network connection
1731:
1732:                out.write(empty, 0, 7);
1733:
1734:                putLoginString(appName, 30); // appname
1735:                putLoginString(serverName, 30); // server name
1736:                out.write((byte) 0); // remote passwords
1737:                out.write((byte) password.length());
1738:                byte[] tmp = Support.encodeString(connection.getCharset(),
1739:                        password);
1740:                out.write(tmp, 0, 253);
1741:                out.write((byte) (tmp.length + 2));
1742:
1743:                out.write((byte) 5); // tds version
1744:                out.write((byte) 0);
1745:
1746:                out.write((byte) 0);
1747:                out.write((byte) 0);
1748:                putLoginString(progName, 10); // prog name
1749:
1750:                out.write((byte) 5); // prog version
1751:                out.write((byte) 0);
1752:                out.write((byte) 0);
1753:                out.write((byte) 0);
1754:
1755:                out.write((byte) 0); // auto convert short
1756:                out.write((byte) 0x0D); // type of flt4
1757:                out.write((byte) 0x11); // type of date4
1758:
1759:                putLoginString(language, 30); // language
1760:
1761:                out.write((byte) 1); // notify on lang change
1762:                out.write((short) 0); // security label hierachy
1763:                out.write((byte) 0); // security encrypted
1764:                out.write(empty, 0, 8); // security components
1765:                out.write((short) 0); // security spare
1766:
1767:                putLoginString(charset, 30); // Character set
1768:
1769:                out.write((byte) 1); // notify on charset change
1770:                if (packetSize > 0) {
1771:                    putLoginString(String.valueOf(packetSize), 6); // specified length of tds packets
1772:                } else {
1773:                    putLoginString(String.valueOf(MIN_PKT_SIZE), 6); // Default length of tds packets
1774:                }
1775:                out.write(empty, 0, 4);
1776:                //
1777:                // Request capabilities
1778:                //
1779:                // jTDS sends   01 0B 4F FF 85 EE EF 65 7F FF FF FF D6
1780:                // Sybase 11.92 01 0A    00 00 00 23 61 41 CF FF FF C6
1781:                // Sybase 12.52 01 0A    03 84 0A E3 61 41 FF FF FF C6
1782:                // Sybase 15.00 01 0B 4F F7 85 EA EB 61 7F FF FF FF C6
1783:                //
1784:                // Response capabilities
1785:                //
1786:                // jTDS sends   02 0A 00 00 04 06 80 06 48 00 00 00
1787:                // Sybase 11.92 02 0A 00 00 00 00 00 06 00 00 00 00
1788:                // Sybase 12.52 02 0A 00 00 00 00 00 06 00 00 00 00
1789:                // Sybase 15.00 02 0A 00 00 04 00 00 06 00 00 00 00
1790:                //
1791:                byte capString[] = {
1792:                        // Request capabilities
1793:                        (byte) 0x01, (byte) 0x0B, (byte) 0x4F, (byte) 0xFF,
1794:                        (byte) 0x85, (byte) 0xEE, (byte) 0xEF, (byte) 0x65,
1795:                        (byte) 0x7F, (byte) 0xFF, (byte) 0xFF,
1796:                        (byte) 0xFF,
1797:                        (byte) 0xD6,
1798:                        // Response capabilities
1799:                        (byte) 0x02, (byte) 0x0A, (byte) 0x00, (byte) 0x02,
1800:                        (byte) 0x04, (byte) 0x06, (byte) 0x80, (byte) 0x06,
1801:                        (byte) 0x48, (byte) 0x00, (byte) 0x00, (byte) 0x0C };
1802:
1803:                if (packetSize == 0) {
1804:                    // Tell the server we will use its packet size
1805:                    capString[17] = 0;
1806:                }
1807:                out.write(TDS_CAP_TOKEN);
1808:                out.write((short) capString.length);
1809:                out.write(capString);
1810:
1811:                out.flush(); // Send the packet
1812:                endOfResponse = false;
1813:            }
1814:
1815:            /**
1816:             * Send a TDS 7 login packet.
1817:             * <p>
1818:             * This method incorporates the Windows single sign on code contributed by
1819:             * Magendran Sathaiah. To invoke single sign on just leave the user name
1820:             * blank or null. NB. This can only work if the driver is being executed on
1821:             * a Windows PC and <code>ntlmauth.dll</code> is on the path.
1822:             *
1823:             * @param serverName    server host name
1824:             * @param database      required database
1825:             * @param user          user name
1826:             * @param password      user password
1827:             * @param domain        Windows NT domain (or <code>null</code>)
1828:             * @param appName       application name
1829:             * @param progName      program name
1830:             * @param wsid          workstation ID
1831:             * @param language      server language for messages
1832:             * @param macAddress    client network MAC address
1833:             * @param netPacketSize TDS packet size to use
1834:             * @throws IOException if an I/O error occurs
1835:             */
1836:            private void sendMSLoginPkt(final String serverName,
1837:                    final String database, final String user,
1838:                    final String password, final String domain,
1839:                    final String appName, final String progName,
1840:                    final String wsid, final String language,
1841:                    final String macAddress, final int netPacketSize)
1842:                    throws IOException, SQLException {
1843:                final byte[] empty = new byte[0];
1844:                boolean ntlmAuth = false;
1845:                byte[] ntlmMessage = null;
1846:
1847:                if (user == null || user.length() == 0) {
1848:                    // See if executing on a Windows platform and if so try and
1849:                    // use the single sign on native library.
1850:                    if (Support.isWindowsOS()) {
1851:                        ntlmAuthSSO = true;
1852:                        ntlmAuth = true;
1853:                    } else {
1854:                        throw new SQLException(Messages
1855:                                .get("error.connection.sso"), "08001");
1856:                    }
1857:                } else if (domain != null && domain.length() > 0) {
1858:                    // Assume we want to use Windows authentication with
1859:                    // supplied user and password.
1860:                    ntlmAuth = true;
1861:                }
1862:
1863:                if (ntlmAuthSSO) {
1864:                    try {
1865:                        // Create the NTLM request block using the native library
1866:                        sspiJNIClient = SSPIJNIClient.getInstance();
1867:                        ntlmMessage = sspiJNIClient.invokePrepareSSORequest();
1868:                    } catch (Exception e) {
1869:                        throw new IOException("SSO Failed: " + e.getMessage());
1870:                    }
1871:                }
1872:
1873:                //mdb:begin-change
1874:                short packSize = (short) (86 + 2 * (wsid.length()
1875:                        + appName.length() + serverName.length()
1876:                        + progName.length() + database.length() + language
1877:                        .length()));
1878:                final short authLen;
1879:                //NOTE(mdb): ntlm includes auth block; sql auth includes uname and pwd.
1880:                if (ntlmAuth) {
1881:                    if (ntlmAuthSSO && ntlmMessage != null) {
1882:                        authLen = (short) ntlmMessage.length;
1883:                    } else {
1884:                        authLen = (short) (32 + domain.length());
1885:                    }
1886:                    packSize += authLen;
1887:                } else {
1888:                    authLen = 0;
1889:                    packSize += (2 * (user.length() + password.length()));
1890:                }
1891:                //mdb:end-change
1892:
1893:                out.setPacketType(MSLOGIN_PKT);
1894:                out.write((int) packSize);
1895:                // TDS version
1896:                if (tdsVersion == Driver.TDS70) {
1897:                    out.write((int) 0x70000000);
1898:                } else {
1899:                    out.write((int) 0x71000001);
1900:                }
1901:                // Network Packet size requested by client
1902:                out.write((int) netPacketSize);
1903:                // Program version?
1904:                out.write((int) 7);
1905:                // Process ID
1906:                out.write((int) 123);
1907:                // Connection ID
1908:                out.write((int) 0);
1909:                // 0x20: enable warning messages if USE <database> issued
1910:                // 0x40: change to initial database must succeed
1911:                // 0x80: enable warning messages if SET LANGUAGE issued
1912:                byte flags = (byte) (0x20 | 0x40 | 0x80);
1913:                out.write(flags);
1914:
1915:                //mdb: this byte controls what kind of auth we do.
1916:                flags = 0x03; // ODBC (JDBC) driver
1917:                if (ntlmAuth)
1918:                    flags |= 0x80; // Use NT authentication
1919:                out.write(flags);
1920:
1921:                out.write((byte) 0); // SQL type flag
1922:                out.write((byte) 0); // Reserved flag
1923:                // TODO Set Timezone and collation?
1924:                out.write(empty, 0, 4); // Time Zone
1925:                out.write(empty, 0, 4); // Collation
1926:
1927:                // Pack up value lengths, positions.
1928:                short curPos = 86;
1929:
1930:                // Hostname
1931:                out.write((short) curPos);
1932:                out.write((short) wsid.length());
1933:                curPos += wsid.length() * 2;
1934:
1935:                //mdb: NTLM doesn't send username and password...
1936:                if (!ntlmAuth) {
1937:                    // Username
1938:                    out.write((short) curPos);
1939:                    out.write((short) user.length());
1940:                    curPos += user.length() * 2;
1941:
1942:                    // Password
1943:                    out.write((short) curPos);
1944:                    out.write((short) password.length());
1945:                    curPos += password.length() * 2;
1946:                } else {
1947:                    out.write((short) curPos);
1948:                    out.write((short) 0);
1949:
1950:                    out.write((short) curPos);
1951:                    out.write((short) 0);
1952:                }
1953:
1954:                // App name
1955:                out.write((short) curPos);
1956:                out.write((short) appName.length());
1957:                curPos += appName.length() * 2;
1958:
1959:                // Server name
1960:                out.write((short) curPos);
1961:                out.write((short) serverName.length());
1962:                curPos += serverName.length() * 2;
1963:
1964:                // Unknown
1965:                out.write((short) 0);
1966:                out.write((short) 0);
1967:
1968:                // Program name
1969:                out.write((short) curPos);
1970:                out.write((short) progName.length());
1971:                curPos += progName.length() * 2;
1972:
1973:                // Server language
1974:                out.write((short) curPos);
1975:                out.write((short) language.length());
1976:                curPos += language.length() * 2;
1977:
1978:                // Database
1979:                out.write((short) curPos);
1980:                out.write((short) database.length());
1981:                curPos += database.length() * 2;
1982:
1983:                // MAC address
1984:                out.write(getMACAddress(macAddress));
1985:
1986:                //mdb: location of ntlm auth block. note that for sql auth, authLen==0.
1987:                out.write((short) curPos);
1988:                out.write((short) authLen);
1989:
1990:                //"next position" (same as total packet size)
1991:                out.write((int) packSize);
1992:
1993:                out.write(wsid);
1994:
1995:                // Pack up the login values.
1996:                //mdb: for ntlm auth, uname and pwd aren't sent up...
1997:                if (!ntlmAuth) {
1998:                    final String scrambledPw = tds7CryptPass(password);
1999:                    out.write(user);
2000:                    out.write(scrambledPw);
2001:                }
2002:
2003:                out.write(appName);
2004:                out.write(serverName);
2005:                out.write(progName);
2006:                out.write(language);
2007:                out.write(database);
2008:
2009:                //mdb: add the ntlm auth info...
2010:                if (ntlmAuth) {
2011:                    if (ntlmAuthSSO) {
2012:                        // Use the NTLM message generated by the native library
2013:                        out.write(ntlmMessage);
2014:                    } else {
2015:                        // host and domain name are _narrow_ strings.
2016:                        final byte[] domainBytes = domain.getBytes("UTF8");
2017:                        //byte[] hostBytes   = localhostname.getBytes("UTF8");
2018:
2019:                        final byte[] header = { 0x4e, 0x54, 0x4c, 0x4d, 0x53,
2020:                                0x53, 0x50, 0x00 };
2021:                        out.write(header); //header is ascii "NTLMSSP\0"
2022:                        out.write((int) 1); //sequence number = 1
2023:                        if (connection.getUseNTLMv2())
2024:                            out.write((int) 0x8b205); //flags (same as below, only with Request Target and NTLM2 set)
2025:                        else
2026:                            out.write((int) 0xb201); //flags (see below)
2027:
2028:                        // NOTE: flag reference:
2029:                        //  0x80000 = negotiate NTLM2 key
2030:                        //  0x08000 = negotiate always sign
2031:                        //  0x02000 = client is sending workstation name
2032:                        //  0x01000 = client is sending domain name
2033:                        //  0x00200 = negotiate NTLM
2034:                        //  0x00004 - Request Target, which requests that server send target
2035:                        //  0x00001 = negotiate Unicode
2036:
2037:                        //domain info
2038:                        out.write((short) domainBytes.length);
2039:                        out.write((short) domainBytes.length);
2040:                        out.write((int) 32); //offset, relative to start of auth block.
2041:
2042:                        //host info
2043:                        //NOTE(mdb): not sending host info; hope this is ok!
2044:                        out.write((short) 0);
2045:                        out.write((short) 0);
2046:                        out.write((int) 32); //offset, relative to start of auth block.
2047:
2048:                        // add the variable length data at the end...
2049:                        out.write(domainBytes);
2050:                    }
2051:                }
2052:                out.flush(); // Send the packet
2053:                endOfResponse = false;
2054:            }
2055:
2056:            /**
2057:             * Send the response to the NTLM authentication challenge.
2058:             * @param nonce The secret to hash with password.
2059:             * @param user The user name.
2060:             * @param password The user password.
2061:             * @param domain The Windows NT Dommain.
2062:             * @throws java.io.IOException
2063:             */
2064:            private void sendNtlmChallengeResponse(final byte[] nonce,
2065:                    String user, final String password, String domain)
2066:                    throws java.io.IOException {
2067:                out.setPacketType(NTLMAUTH_PKT);
2068:
2069:                // Prepare and Set NTLM Type 2 message appropriately
2070:                // Author: mahi@aztec.soft.net
2071:                if (ntlmAuthSSO) {
2072:                    byte[] ntlmMessage = currentToken.ntlmMessage;
2073:                    try {
2074:                        // Create the challenge response using the native library
2075:                        ntlmMessage = sspiJNIClient
2076:                                .invokePrepareSSOSubmit(ntlmMessage);
2077:                    } catch (Exception e) {
2078:                        throw new IOException("SSO Failed: " + e.getMessage());
2079:                    }
2080:                    out.write(ntlmMessage);
2081:                } else {
2082:                    // host and domain name are _narrow_ strings.
2083:                    //byte[] domainBytes = domain.getBytes("UTF8");
2084:                    //byte[] user        = user.getBytes("UTF8");
2085:
2086:                    byte[] lmAnswer, ntAnswer;
2087:                    //the response to the challenge...
2088:
2089:                    if (connection.getUseNTLMv2()) {
2090:                        //TODO: does this need to be random?
2091:                        //byte[] clientNonce = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
2092:                        byte[] clientNonce = new byte[8];
2093:                        (new Random()).nextBytes(clientNonce);
2094:
2095:                        lmAnswer = NtlmAuth.answerLmv2Challenge(domain, user,
2096:                                password, nonce, clientNonce);
2097:                        ntAnswer = NtlmAuth.answerNtlmv2Challenge(domain, user,
2098:                                password, nonce, currentToken.ntlmTarget,
2099:                                clientNonce);
2100:                    } else {
2101:                        //LM/NTLM (v1)
2102:                        lmAnswer = NtlmAuth.answerLmChallenge(password, nonce);
2103:                        ntAnswer = NtlmAuth.answerNtChallenge(password, nonce);
2104:                    }
2105:
2106:                    final byte[] header = { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53,
2107:                            0x50, 0x00 };
2108:                    out.write(header); //header is ascii "NTLMSSP\0"
2109:                    out.write((int) 3); //sequence number = 3
2110:                    final int domainLenInBytes = domain.length() * 2;
2111:                    final int userLenInBytes = user.length() * 2;
2112:                    //mdb: not sending hostname; I hope this is ok!
2113:                    final int hostLenInBytes = 0; //localhostname.length()*2;
2114:                    int pos = 64 + domainLenInBytes + userLenInBytes
2115:                            + hostLenInBytes;
2116:                    // lan man response: length and offset
2117:                    out.write((short) lmAnswer.length);
2118:                    out.write((short) lmAnswer.length);
2119:                    out.write((int) pos);
2120:                    pos += lmAnswer.length;
2121:                    // nt response: length and offset
2122:                    out.write((short) ntAnswer.length);
2123:                    out.write((short) ntAnswer.length);
2124:                    out.write((int) pos);
2125:                    pos = 64;
2126:                    //domain
2127:                    out.write((short) domainLenInBytes);
2128:                    out.write((short) domainLenInBytes);
2129:                    out.write((int) pos);
2130:                    pos += domainLenInBytes;
2131:
2132:                    //user
2133:                    out.write((short) userLenInBytes);
2134:                    out.write((short) userLenInBytes);
2135:                    out.write((int) pos);
2136:                    pos += userLenInBytes;
2137:                    //local hostname
2138:                    out.write((short) hostLenInBytes);
2139:                    out.write((short) hostLenInBytes);
2140:                    out.write((int) pos);
2141:                    pos += hostLenInBytes;
2142:                    //unknown
2143:                    out.write((short) 0);
2144:                    out.write((short) 0);
2145:                    out.write((int) pos);
2146:                    //flags
2147:                    if (connection.getUseNTLMv2())
2148:                        out.write((int) 0x88201);
2149:                    else
2150:                        out.write((int) 0x8201);
2151:                    //variable length stuff...
2152:                    out.write(domain);
2153:                    out.write(user);
2154:                    //Not sending hostname...I hope this is OK!
2155:                    //comm.appendChars(localhostname);
2156:
2157:                    //the response to the challenge...
2158:                    out.write(lmAnswer);
2159:                    out.write(ntAnswer);
2160:                }
2161:                out.flush();
2162:            }
2163:
2164:            /**
2165:             * Read the next TDS token from the response stream.
2166:             *
2167:             * @throws SQLException if an I/O or protocol error occurs
2168:             */
2169:            private void nextToken() throws SQLException {
2170:                checkOpen();
2171:                if (endOfResponse) {
2172:                    currentToken.token = TDS_DONE_TOKEN;
2173:                    currentToken.status = 0;
2174:                    return;
2175:                }
2176:                try {
2177:                    currentToken.token = (byte) in.read();
2178:                    switch (currentToken.token) {
2179:                    case TDS5_PARAMFMT2_TOKEN:
2180:                        tds5ParamFmt2Token();
2181:                        break;
2182:                    case TDS_LANG_TOKEN:
2183:                        tdsInvalidToken();
2184:                        break;
2185:                    case TDS5_WIDE_RESULT:
2186:                        tds5WideResultToken();
2187:                        break;
2188:                    case TDS_CLOSE_TOKEN:
2189:                        tdsInvalidToken();
2190:                        break;
2191:                    case TDS_RETURNSTATUS_TOKEN:
2192:                        tdsReturnStatusToken();
2193:                        break;
2194:                    case TDS_PROCID:
2195:                        tdsProcIdToken();
2196:                        break;
2197:                    case TDS_OFFSETS_TOKEN:
2198:                        tdsOffsetsToken();
2199:                        break;
2200:                    case TDS7_RESULT_TOKEN:
2201:                        tds7ResultToken();
2202:                        break;
2203:                    case TDS7_COMP_RESULT_TOKEN:
2204:                        tdsInvalidToken();
2205:                        break;
2206:                    case TDS_COLNAME_TOKEN:
2207:                        tds4ColNamesToken();
2208:                        break;
2209:                    case TDS_COLFMT_TOKEN:
2210:                        tds4ColFormatToken();
2211:                        break;
2212:                    case TDS_TABNAME_TOKEN:
2213:                        tdsTableNameToken();
2214:                        break;
2215:                    case TDS_COLINFO_TOKEN:
2216:                        tdsColumnInfoToken();
2217:                        break;
2218:                    case TDS_COMP_NAMES_TOKEN:
2219:                        tdsInvalidToken();
2220:                        break;
2221:                    case TDS_COMP_RESULT_TOKEN:
2222:                        tdsInvalidToken();
2223:                        break;
2224:                    case TDS_ORDER_TOKEN:
2225:                        tdsOrderByToken();
2226:                        break;
2227:                    case TDS_ERROR_TOKEN:
2228:                    case TDS_INFO_TOKEN:
2229:                        tdsErrorToken();
2230:                        break;
2231:                    case TDS_PARAM_TOKEN:
2232:                        tdsOutputParamToken();
2233:                        break;
2234:                    case TDS_LOGINACK_TOKEN:
2235:                        tdsLoginAckToken();
2236:                        break;
2237:                    case TDS_CONTROL_TOKEN:
2238:                        tdsControlToken();
2239:                        break;
2240:                    case TDS_ROW_TOKEN:
2241:                        tdsRowToken();
2242:                        break;
2243:                    case TDS_ALTROW:
2244:                        tdsInvalidToken();
2245:                        break;
2246:                    case TDS5_PARAMS_TOKEN:
2247:                        tds5ParamsToken();
2248:                        break;
2249:                    case TDS_CAP_TOKEN:
2250:                        tdsCapabilityToken();
2251:                        break;
2252:                    case TDS_ENVCHANGE_TOKEN:
2253:                        tdsEnvChangeToken();
2254:                        break;
2255:                    case TDS_MSG50_TOKEN:
2256:                        tds5ErrorToken();
2257:                        break;
2258:                    case TDS5_DYNAMIC_TOKEN:
2259:                        tds5DynamicToken();
2260:                        break;
2261:                    case TDS5_PARAMFMT_TOKEN:
2262:                        tds5ParamFmtToken();
2263:                        break;
2264:                    case TDS_AUTH_TOKEN:
2265:                        tdsNtlmAuthToken();
2266:                        break;
2267:                    case TDS_RESULT_TOKEN:
2268:                        tds5ResultToken();
2269:                        break;
2270:                    case TDS_DONE_TOKEN:
2271:                    case TDS_DONEPROC_TOKEN:
2272:                    case TDS_DONEINPROC_TOKEN:
2273:                        tdsDoneToken();
2274:                        break;
2275:                    default:
2276:                        throw new ProtocolException(
2277:                                "Invalid packet type 0x"
2278:                                        + Integer
2279:                                                .toHexString((int) currentToken.token & 0xFF));
2280:                    }
2281:                } catch (IOException ioe) {
2282:                    connection.setClosed();
2283:                    throw Support.linkException(
2284:                            new SQLException(Messages.get(
2285:                                    "error.generic.ioerror", ioe.getMessage()),
2286:                                    "08S01"), ioe);
2287:                } catch (ProtocolException pe) {
2288:                    connection.setClosed();
2289:                    throw Support.linkException(
2290:                            new SQLException(Messages.get(
2291:                                    "error.generic.tdserror", pe.getMessage()),
2292:                                    "08S01"), pe);
2293:                } catch (OutOfMemoryError err) {
2294:                    // Consume the rest of the response
2295:                    in.skipToEnd();
2296:                    endOfResponse = true;
2297:                    endOfResults = true;
2298:                    cancelPending = false;
2299:                    throw err;
2300:                }
2301:            }
2302:
2303:            /**
2304:             * Report unsupported TDS token in input stream.
2305:             *
2306:             * @throws IOException
2307:             */
2308:            private void tdsInvalidToken() throws IOException,
2309:                    ProtocolException {
2310:                in.skip(in.readShort());
2311:                throw new ProtocolException("Unsupported TDS token: 0x"
2312:                        + Integer.toHexString((int) currentToken.token & 0xFF));
2313:            }
2314:
2315:            /**
2316:             * Process TDS 5 Sybase 12+ Dynamic results parameter descriptor.
2317:             * <p>When returning output parameters this token will be followed
2318:             * by a TDS5_PARAMS_TOKEN with the actual data.
2319:             * @throws IOException
2320:             * @throws ProtocolException
2321:             */
2322:            private void tds5ParamFmt2Token() throws IOException,
2323:                    ProtocolException {
2324:                in.readInt(); // Packet length
2325:                int paramCnt = in.readShort();
2326:                ColInfo[] params = new ColInfo[paramCnt];
2327:                for (int i = 0; i < paramCnt; i++) {
2328:                    //
2329:                    // Get the parameter details using the
2330:                    // ColInfo class as the server format is the same.
2331:                    //
2332:                    ColInfo col = new ColInfo();
2333:                    int colNameLen = in.read();
2334:                    col.realName = in.readNonUnicodeString(colNameLen);
2335:                    int column_flags = in.readInt(); /*  Flags */
2336:                    col.isCaseSensitive = false;
2337:                    col.nullable = ((column_flags & 0x20) != 0) ? ResultSetMetaData.columnNullable
2338:                            : ResultSetMetaData.columnNoNulls;
2339:                    col.isWriteable = (column_flags & 0x10) != 0;
2340:                    col.isIdentity = (column_flags & 0x40) != 0;
2341:                    col.isKey = (column_flags & 0x02) != 0;
2342:                    col.isHidden = (column_flags & 0x01) != 0;
2343:
2344:                    col.userType = in.readInt();
2345:                    TdsData.readType(in, col);
2346:                    // Skip locale information
2347:                    in.skip(1);
2348:                    params[i] = col;
2349:                }
2350:                currentToken.dynamParamInfo = params;
2351:                currentToken.dynamParamData = new Object[paramCnt];
2352:            }
2353:
2354:            /**
2355:             * Process Sybase 12+ wide result token which provides enhanced
2356:             * column meta data.
2357:             *
2358:             * @throws IOException
2359:             */
2360:            private void tds5WideResultToken() throws IOException,
2361:                    ProtocolException {
2362:                in.readInt(); // Packet length
2363:                int colCnt = in.readShort();
2364:                this .columns = new ColInfo[colCnt];
2365:                this .rowData = new Object[colCnt];
2366:                this .tables = null;
2367:
2368:                for (int colNum = 0; colNum < colCnt; ++colNum) {
2369:                    ColInfo col = new ColInfo();
2370:                    //
2371:                    // Get the alias name
2372:                    //
2373:                    int nameLen = in.read();
2374:                    col.name = in.readNonUnicodeString(nameLen);
2375:                    //
2376:                    // Get the catalog name
2377:                    //
2378:                    nameLen = in.read();
2379:                    col.catalog = in.readNonUnicodeString(nameLen);
2380:                    //
2381:                    // Get the schema name
2382:                    //
2383:                    nameLen = in.read();
2384:                    col.schema = in.readNonUnicodeString(nameLen);
2385:                    //
2386:                    // Get the table name
2387:                    //
2388:                    nameLen = in.read();
2389:                    col.tableName = in.readNonUnicodeString(nameLen);
2390:                    //
2391:                    // Get the column name
2392:                    //
2393:                    nameLen = in.read();
2394:                    col.realName = in.readNonUnicodeString(nameLen);
2395:                    if (col.name == null || col.name.length() == 0) {
2396:                        col.name = col.realName;
2397:                    }
2398:                    int column_flags = in.readInt(); /*  Flags */
2399:                    col.isCaseSensitive = false;
2400:                    col.nullable = ((column_flags & 0x20) != 0) ? ResultSetMetaData.columnNullable
2401:                            : ResultSetMetaData.columnNoNulls;
2402:                    col.isWriteable = (column_flags & 0x10) != 0;
2403:                    col.isIdentity = (column_flags & 0x40) != 0;
2404:                    col.isKey = (column_flags & 0x02) != 0;
2405:                    col.isHidden = (column_flags & 0x01) != 0;
2406:
2407:                    col.userType = in.readInt();
2408:                    TdsData.readType(in, col);
2409:                    // Skip locale information
2410:                    in.skip(1);
2411:                    columns[colNum] = col;
2412:                }
2413:                endOfResults = false;
2414:            }
2415:
2416:            /**
2417:             * Process stored procedure return status token.
2418:             *
2419:             * @throws IOException
2420:             */
2421:            private void tdsReturnStatusToken() throws IOException,
2422:                    SQLException {
2423:                returnStatus = new Integer(in.readInt());
2424:                if (this .returnParam != null) {
2425:                    returnParam.setOutValue(Support.convert(this .connection,
2426:                            returnStatus, returnParam.jdbcType, connection
2427:                                    .getCharset()));
2428:                }
2429:            }
2430:
2431:            /**
2432:             * Process procedure ID token.
2433:             * <p>
2434:             * Used by DBLIB to obtain the object id of a stored procedure.
2435:             */
2436:            private void tdsProcIdToken() throws IOException {
2437:                in.skip(8);
2438:            }
2439:
2440:            /**
2441:             * Process offsets token.
2442:             * <p>
2443:             * Used by DBLIB to return the offset of various keywords in a statement.
2444:             * This saves the client from having to parse a SQL statement. Enabled with
2445:             * <code>&quot;set offsets from on&quot;</code>.
2446:             */
2447:            private void tdsOffsetsToken() throws IOException {
2448:                /*int keyword =*/in.read();
2449:                /*int unknown =*/in.read();
2450:                /*int offset  =*/in.readShort();
2451:            }
2452:
2453:            /**
2454:             * Process a TDS 7.0 result set token.
2455:             *
2456:             * @throws IOException
2457:             * @throws ProtocolException
2458:             */
2459:            private void tds7ResultToken() throws IOException,
2460:                    ProtocolException, SQLException {
2461:                endOfResults = false;
2462:
2463:                int colCnt = in.readShort();
2464:
2465:                if (colCnt < 0) {
2466:                    // Short packet returned by TDS8 when the column meta data is
2467:                    // supressed on cursor fetch etc.
2468:                    // NB. With TDS7 no result set packet is returned at all.
2469:                    return;
2470:                }
2471:
2472:                this .columns = new ColInfo[colCnt];
2473:                this .rowData = new Object[colCnt];
2474:                this .tables = null;
2475:
2476:                for (int i = 0; i < colCnt; i++) {
2477:                    ColInfo col = new ColInfo();
2478:
2479:                    col.userType = in.readShort();
2480:
2481:                    int flags = in.readShort();
2482:
2483:                    col.nullable = ((flags & 0x01) != 0) ? ResultSetMetaData.columnNullable
2484:                            : ResultSetMetaData.columnNoNulls;
2485:                    col.isCaseSensitive = (flags & 0X02) != 0;
2486:                    col.isIdentity = (flags & 0x10) != 0;
2487:                    col.isWriteable = (flags & 0x0C) != 0;
2488:                    TdsData.readType(in, col);
2489:                    // Set the charsetInfo field of col
2490:                    if (tdsVersion >= Driver.TDS80 && col.collation != null) {
2491:                        TdsData.setColumnCharset(col, connection);
2492:                    }
2493:
2494:                    int clen = in.read();
2495:
2496:                    col.realName = in.readUnicodeString(clen);
2497:                    col.name = col.realName;
2498:
2499:                    this .columns[i] = col;
2500:                }
2501:            }
2502:
2503:            /**
2504:             * Process a TDS 4.2 column names token.
2505:             * <p>
2506:             * Note: Will be followed by a COL_FMT token.
2507:             *
2508:             * @throws IOException
2509:             */
2510:            private void tds4ColNamesToken() throws IOException {
2511:                ArrayList colList = new ArrayList();
2512:
2513:                final int pktLen = in.readShort();
2514:                this .tables = null;
2515:                int bytesRead = 0;
2516:
2517:                while (bytesRead < pktLen) {
2518:                    ColInfo col = new ColInfo();
2519:                    int nameLen = in.read();
2520:                    String name = in.readNonUnicodeString(nameLen);
2521:
2522:                    bytesRead = bytesRead + 1 + nameLen;
2523:                    col.realName = name;
2524:                    col.name = name;
2525:
2526:                    colList.add(col);
2527:                }
2528:
2529:                int colCnt = colList.size();
2530:                this .columns = (ColInfo[]) colList.toArray(new ColInfo[colCnt]);
2531:                this .rowData = new Object[colCnt];
2532:            }
2533:
2534:            /**
2535:             * Process a TDS 4.2 column format token.
2536:             *
2537:             * @throws IOException
2538:             * @throws ProtocolException
2539:             */
2540:            private void tds4ColFormatToken() throws IOException,
2541:                    ProtocolException {
2542:
2543:                final int pktLen = in.readShort();
2544:
2545:                int bytesRead = 0;
2546:                int numColumns = 0;
2547:                while (bytesRead < pktLen) {
2548:                    if (numColumns > columns.length) {
2549:                        throw new ProtocolException(
2550:                                "Too many columns in TDS_COL_FMT packet");
2551:                    }
2552:                    ColInfo col = columns[numColumns];
2553:
2554:                    if (serverType == Driver.SQLSERVER) {
2555:                        col.userType = in.readShort();
2556:
2557:                        int flags = in.readShort();
2558:
2559:                        col.nullable = ((flags & 0x01) != 0) ? ResultSetMetaData.columnNullable
2560:                                : ResultSetMetaData.columnNoNulls;
2561:                        col.isCaseSensitive = (flags & 0x02) != 0;
2562:                        col.isWriteable = (flags & 0x0C) != 0;
2563:                        col.isIdentity = (flags & 0x10) != 0;
2564:                    } else {
2565:                        // Sybase does not send column flags
2566:                        col.isCaseSensitive = false;
2567:                        col.isWriteable = true;
2568:
2569:                        if (col.nullable == ResultSetMetaData.columnNoNulls) {
2570:                            col.nullable = ResultSetMetaData.columnNullableUnknown;
2571:                        }
2572:
2573:                        col.userType = in.readInt();
2574:                    }
2575:                    bytesRead += 4;
2576:
2577:                    bytesRead += TdsData.readType(in, col);
2578:
2579:                    numColumns++;
2580:                }
2581:
2582:                if (numColumns != columns.length) {
2583:                    throw new ProtocolException(
2584:                            "Too few columns in TDS_COL_FMT packet");
2585:                }
2586:
2587:                endOfResults = false;
2588:            }
2589:
2590:            /**
2591:             * Process a table name token.
2592:             * <p> Sent by select for browse or cursor functions.
2593:             *
2594:             * @throws IOException
2595:             */
2596:            private void tdsTableNameToken() throws IOException,
2597:                    ProtocolException {
2598:                final int pktLen = in.readShort();
2599:                int bytesRead = 0;
2600:                ArrayList tableList = new ArrayList();
2601:
2602:                while (bytesRead < pktLen) {
2603:                    int nameLen;
2604:                    String tabName;
2605:                    TableMetaData table;
2606:                    if (tdsVersion >= Driver.TDS81) {
2607:                        // TDS8.1 supplies the server.database.owner.table as up to
2608:                        // four separate components which allows us to have names
2609:                        // with embedded periods.
2610:                        table = new TableMetaData();
2611:                        bytesRead++;
2612:                        int tableNameToken = in.read();
2613:                        switch (tableNameToken) {
2614:                        case 4:
2615:                            nameLen = in.readShort();
2616:                            bytesRead += nameLen * 2 + 2;
2617:                            // Read and discard server name; see Bug 1403067
2618:                            in.readUnicodeString(nameLen);
2619:                        case 3:
2620:                            nameLen = in.readShort();
2621:                            bytesRead += nameLen * 2 + 2;
2622:                            table.catalog = in.readUnicodeString(nameLen);
2623:                        case 2:
2624:                            nameLen = in.readShort();
2625:                            bytesRead += nameLen * 2 + 2;
2626:                            table.schema = in.readUnicodeString(nameLen);
2627:                        case 1:
2628:                            nameLen = in.readShort();
2629:                            bytesRead += nameLen * 2 + 2;
2630:                            table.name = in.readUnicodeString(nameLen);
2631:                        case 0:
2632:                            break;
2633:                        default:
2634:                            throw new ProtocolException(
2635:                                    "Invalid table TAB_NAME_TOKEN: "
2636:                                            + tableNameToken);
2637:                        }
2638:                    } else {
2639:                        if (tdsVersion >= Driver.TDS70) {
2640:                            nameLen = in.readShort();
2641:                            bytesRead += nameLen * 2 + 2;
2642:                            tabName = in.readUnicodeString(nameLen);
2643:                        } else {
2644:                            // TDS 4.2 or TDS 5.0
2645:                            nameLen = in.read();
2646:                            bytesRead++;
2647:                            if (nameLen == 0) {
2648:                                continue; // Sybase/SQL 6.5 use a zero length name to terminate list
2649:                            }
2650:                            tabName = in.readNonUnicodeString(nameLen);
2651:                            bytesRead += nameLen;
2652:                        }
2653:                        table = new TableMetaData();
2654:                        // tabName can be a fully qualified name
2655:                        int dotPos = tabName.lastIndexOf('.');
2656:                        if (dotPos > 0) {
2657:                            table.name = tabName.substring(dotPos + 1);
2658:
2659:                            int nextPos = tabName.lastIndexOf('.', dotPos - 1);
2660:                            if (nextPos + 1 < dotPos) {
2661:                                table.schema = tabName.substring(nextPos + 1,
2662:                                        dotPos);
2663:                            }
2664:                            dotPos = nextPos;
2665:                            nextPos = tabName.lastIndexOf('.', dotPos - 1);
2666:                            if (nextPos + 1 < dotPos) {
2667:                                table.catalog = tabName.substring(nextPos + 1,
2668:                                        dotPos);
2669:                            }
2670:                        } else {
2671:                            table.name = tabName;
2672:                        }
2673:                    }
2674:                    tableList.add(table);
2675:                }
2676:                if (tableList.size() > 0) {
2677:                    this .tables = (TableMetaData[]) tableList
2678:                            .toArray(new TableMetaData[tableList.size()]);
2679:                }
2680:            }
2681:
2682:            /**
2683:             * Process a column infomation token.
2684:             * <p>Sent by select for browse or cursor functions.
2685:             * @throws IOException
2686:             * @throws ProtocolException
2687:             */
2688:            private void tdsColumnInfoToken() throws IOException,
2689:                    ProtocolException {
2690:                final int pktLen = in.readShort();
2691:                int bytesRead = 0;
2692:                int columnIndex = 0;
2693:
2694:                while (bytesRead < pktLen) {
2695:                    // Seems like all columns are always returned in the COL_INFO
2696:                    // packet and there might be more than 255 columns, so we'll
2697:                    // just increment a counter instead.
2698:                    // Ignore the column index.
2699:                    in.read();
2700:                    if (columnIndex >= columns.length) {
2701:                        throw new ProtocolException("Column index "
2702:                                + (columnIndex + 1)
2703:                                + " invalid in TDS_COLINFO packet");
2704:                    }
2705:                    ColInfo col = columns[columnIndex++];
2706:                    int tableIndex = in.read();
2707:                    // In some cases (e.g. if the user calls 'CREATE CURSOR'), the
2708:                    // TDS_TABNAME packet seems to be missing although the table index
2709:                    // in this packet is > 0. Weird.
2710:                    // If tables are available check for valid table index.
2711:                    if (tables != null && tableIndex > tables.length) {
2712:                        throw new ProtocolException("Table index " + tableIndex
2713:                                + " invalid in TDS_COLINFO packet");
2714:                    }
2715:                    byte flags = (byte) in.read(); // flags
2716:                    bytesRead += 3;
2717:
2718:                    if (tableIndex != 0 && tables != null) {
2719:                        TableMetaData table = tables[tableIndex - 1];
2720:                        col.catalog = table.catalog;
2721:                        col.schema = table.schema;
2722:                        col.tableName = table.name;
2723:                    }
2724:
2725:                    col.isKey = (flags & 0x08) != 0;
2726:                    col.isHidden = (flags & 0x10) != 0;
2727:
2728:                    // If bit 5 is set, we have a column name
2729:                    if ((flags & 0x20) != 0) {
2730:                        final int nameLen = in.read();
2731:                        bytesRead += 1;
2732:                        final String colName = in.readString(nameLen);
2733:                        bytesRead += (tdsVersion >= Driver.TDS70) ? nameLen * 2
2734:                                : nameLen;
2735:                        col.realName = colName;
2736:                    }
2737:                }
2738:            }
2739:
2740:            /**
2741:             * Process an order by token.
2742:             * <p>Sent to describe columns in an order by clause.
2743:             * @throws IOException
2744:             */
2745:            private void tdsOrderByToken() throws IOException {
2746:                // Skip this packet type
2747:                int pktLen = in.readShort();
2748:                in.skip(pktLen);
2749:            }
2750:
2751:            /**
2752:             * Process a TD4/TDS7 error or informational message.
2753:             *
2754:             * @throws IOException
2755:             */
2756:            private void tdsErrorToken() throws IOException {
2757:                int pktLen = in.readShort(); // Packet length
2758:                int sizeSoFar = 6;
2759:                int number = in.readInt();
2760:                int state = in.read();
2761:                int severity = in.read();
2762:                int msgLen = in.readShort();
2763:                String message = in.readString(msgLen);
2764:                sizeSoFar += 2 + ((tdsVersion >= Driver.TDS70) ? msgLen * 2
2765:                        : msgLen);
2766:                final int srvNameLen = in.read();
2767:                String server = in.readString(srvNameLen);
2768:                sizeSoFar += 1 + ((tdsVersion >= Driver.TDS70) ? srvNameLen * 2
2769:                        : srvNameLen);
2770:
2771:                final int procNameLen = in.read();
2772:                String procName = in.readString(procNameLen);
2773:                sizeSoFar += 1 + ((tdsVersion >= Driver.TDS70) ? procNameLen * 2
2774:                        : procNameLen);
2775:
2776:                int line = in.readShort();
2777:                sizeSoFar += 2;
2778:                // Skip any EED information to read rest of packet
2779:                if (pktLen - sizeSoFar > 0)
2780:                    in.skip(pktLen - sizeSoFar);
2781:
2782:                if (currentToken.token == TDS_ERROR_TOKEN) {
2783:                    if (severity < 10) {
2784:                        severity = 11; // Ensure treated as error
2785:                    }
2786:                    if (severity >= 20) {
2787:                        // A fatal error has occured, the connection will be closed by
2788:                        // the server immediately after the last TDS_DONE packet
2789:                        fatalError = true;
2790:                    }
2791:                } else {
2792:                    if (severity > 9) {
2793:                        severity = 9; // Ensure treated as warning
2794:                    }
2795:                }
2796:                messages.addDiagnostic(number, state, severity, message,
2797:                        server, procName, line);
2798:            }
2799:
2800:            /**
2801:             * Process output parameters.
2802:             * </p>
2803:             * Normally the output parameters are preceded by a TDS type 79
2804:             * (procedure return value) record; however there are at least two
2805:             * situations with TDS version 8 where this is not the case:
2806:             * <ol>
2807:             * <li>For the return value of a SQL 2000+ user defined function.</li>
2808:             * <li>For a remote procedure call (server.database.user.procname) where
2809:             * the 79 record is only sent if a result set is also returned by the remote
2810:             * procedure. In this case the 79 record just acts as marker for the start of
2811:             * the output parameters. The actual return value is in an output param token.</li>
2812:             * </ol>
2813:             * Output parameters are distinguished from procedure return values by the value of
2814:             * a byte that immediately follows the parameter name. A value of 1 seems to indicate
2815:             * a normal output parameter while a value of 2 indicates a procedure return value.
2816:             *
2817:             * @throws IOException
2818:             * @throws ProtocolException
2819:             */
2820:            private void tdsOutputParamToken() throws IOException,
2821:                    ProtocolException, SQLException {
2822:                in.readShort(); // Packet length
2823:                String name = in.readString(in.read()); // Column Name
2824:                // Next byte indicates if output parameter or return value
2825:                // 1 = normal output param, 2 = function or stored proc return
2826:                boolean funcReturnVal = (in.read() == 2);
2827:                // Next byte is the parameter type that we supplied which
2828:                // may not be the same as the parameter definition
2829:                /* int inputTdsType = */in.read();
2830:                // Not sure what these bytes are (they always seem to be zero).
2831:                in.skip(3);
2832:
2833:                ColInfo col = new ColInfo();
2834:                TdsData.readType(in, col);
2835:                // Set the charsetInfo field of col
2836:                if (tdsVersion >= Driver.TDS80 && col.collation != null) {
2837:                    TdsData.setColumnCharset(col, connection);
2838:                }
2839:                Object value = TdsData.readData(connection, in, col);
2840:
2841:                //
2842:                // Real output parameters will either be unnamed or will have a valid
2843:                // parameter name beginning with '@'. Ignore any other spurious parameters
2844:                // such as those returned from calls to writetext in the proc.
2845:                //
2846:                if (parameters != null
2847:                        && (name.length() == 0 || name.startsWith("@"))) {
2848:                    if (tdsVersion >= Driver.TDS80 && funcReturnVal) {
2849:                        // TDS 8 Allows function return values of types other than int
2850:                        // Also used to for return value of remote procedure calls.
2851:                        if (returnParam != null) {
2852:                            if (value != null) {
2853:                                returnParam.setOutValue(Support.convert(
2854:                                        connection, value,
2855:                                        returnParam.jdbcType, connection
2856:                                                .getCharset()));
2857:                                returnParam.collation = col.collation;
2858:                                returnParam.charsetInfo = col.charsetInfo;
2859:                            } else {
2860:                                returnParam.setOutValue(null);
2861:                            }
2862:                        }
2863:                    } else {
2864:                        // Look for next output parameter in list
2865:                        while (++nextParam < parameters.length) {
2866:                            if (parameters[nextParam].isOutput) {
2867:                                if (value != null) {
2868:                                    parameters[nextParam]
2869:                                            .setOutValue(Support
2870:                                                    .convert(
2871:                                                            connection,
2872:                                                            value,
2873:                                                            parameters[nextParam].jdbcType,
2874:                                                            connection
2875:                                                                    .getCharset()));
2876:                                    parameters[nextParam].collation = col.collation;
2877:                                    parameters[nextParam].charsetInfo = col.charsetInfo;
2878:                                } else {
2879:                                    parameters[nextParam].setOutValue(null);
2880:                                }
2881:                                break;
2882:                            }
2883:                        }
2884:                    }
2885:                }
2886:            }
2887:
2888:            /**
2889:             * Process a login acknowledgement packet.
2890:             *
2891:             * @throws IOException
2892:             */
2893:            private void tdsLoginAckToken() throws IOException {
2894:                String product;
2895:                int major, minor, build = 0;
2896:                in.readShort(); // Packet length
2897:
2898:                int ack = in.read(); // Ack TDS 5 = 5 for OK 6 for fail, 1/0 for the others
2899:
2900:                // Update the TDS protocol version in this TdsCore and in the Socket.
2901:                // The Connection will update itself immediately after this call.
2902:                // As for other objects containing a TDS version value, there are none
2903:                // at this point (we're just constructing the Connection).
2904:                tdsVersion = TdsData.getTdsVersion(((int) in.read() << 24)
2905:                        | ((int) in.read() << 16) | ((int) in.read() << 8)
2906:                        | (int) in.read());
2907:                socket.setTdsVersion(tdsVersion);
2908:
2909:                product = in.readString(in.read());
2910:
2911:                if (tdsVersion >= Driver.TDS70) {
2912:                    major = in.read();
2913:                    minor = in.read();
2914:                    build = in.read() << 8;
2915:                    build += in.read();
2916:                } else {
2917:                    if (product.toLowerCase().startsWith("microsoft")) {
2918:                        in.skip(1);
2919:                        major = in.read();
2920:                        minor = in.read();
2921:                    } else {
2922:                        major = in.read();
2923:                        minor = in.read() * 10;
2924:                        minor += in.read();
2925:                    }
2926:                    in.skip(1);
2927:                }
2928:
2929:                if (product.length() > 1 && -1 != product.indexOf('\0')) {
2930:                    product = product.substring(0, product.indexOf('\0'));
2931:                }
2932:
2933:                connection.setDBServerInfo(product, major, minor, build);
2934:
2935:                if (tdsVersion == Driver.TDS50 && ack != 5) {
2936:                    // Login rejected by server create SQLException
2937:                    messages.addDiagnostic(4002, 0, 14, "Login failed", "", "",
2938:                            0);
2939:                    currentToken.token = TDS_ERROR_TOKEN;
2940:                } else {
2941:                    // MJH 2005-11-02
2942:                    // If we get this far we are logged in OK so convert
2943:                    // any exceptions into warnings. Any exceptions are
2944:                    // likely to be caused by problems in accessing the
2945:                    // default database for this login id for SQL 6.5 and
2946:                    // Sybase ASE. SQL 7.0+ will fail to login if there is
2947:                    // no access to the default or specified database.
2948:                    // I am not convinced that this is a good idea but it
2949:                    // appears that other drivers e.g. jConnect do this and
2950:                    // return the exceptions on the connection warning chain.
2951:                    //
2952:                    SQLException ex = messages.exceptions;
2953:                    // Avoid returning useless warnings about language
2954:                    // character set etc.
2955:                    messages.clearWarnings();
2956:                    //
2957:                    // Convert exceptions to warnings
2958:                    //
2959:                    while (ex != null) {
2960:                        messages.addWarning(new SQLWarning(ex.getMessage(), ex
2961:                                .getSQLState(), ex.getErrorCode()));
2962:                        ex = ex.getNextException();
2963:                    }
2964:                    messages.exceptions = null;
2965:                }
2966:            }
2967:
2968:            /**
2969:             * Process a control token (function unknown).
2970:             *
2971:             * @throws IOException
2972:             */
2973:            private void tdsControlToken() throws IOException {
2974:                int pktLen = in.readShort();
2975:
2976:                in.skip(pktLen);
2977:            }
2978:
2979:            /**
2980:             * Process a row data token.
2981:             *
2982:             * @throws IOException
2983:             * @throws ProtocolException
2984:             */
2985:            private void tdsRowToken() throws IOException, ProtocolException {
2986:                for (int i = 0; i < columns.length; i++) {
2987:                    rowData[i] = TdsData.readData(connection, in, columns[i]);
2988:                }
2989:
2990:                endOfResults = false;
2991:            }
2992:
2993:            /**
2994:             * Process TDS 5.0 Params Token.
2995:             * Stored procedure output parameters or data returned in parameter format
2996:             * after a TDS Dynamic packet or as extended error information.
2997:             * <p>The type of the preceding token is inspected to determine if this packet
2998:             * contains output parameter result data. A TDS5_PARAMFMT2_TOKEN is sent before
2999:             * this one in Sybase 12 to introduce output parameter results.
3000:             * A TDS5_PARAMFMT_TOKEN is sent before this one to introduce extended error
3001:             * information.
3002:             *
3003:             * @throws IOException
3004:             */
3005:            private void tds5ParamsToken() throws IOException,
3006:                    ProtocolException, SQLException {
3007:                if (currentToken.dynamParamInfo == null) {
3008:                    throw new ProtocolException(
3009:                            "TDS 5 Param results token (0xD7) not preceded by param format (0xEC or 0X20).");
3010:                }
3011:
3012:                for (int i = 0; i < currentToken.dynamParamData.length; i++) {
3013:                    currentToken.dynamParamData[i] = TdsData.readData(
3014:                            connection, in, currentToken.dynamParamInfo[i]);
3015:                    String name = currentToken.dynamParamInfo[i].realName;
3016:                    //
3017:                    // Real output parameters will either be unnamed or will have a valid
3018:                    // parameter name beginning with '@'. Ignore any other Spurious parameters
3019:                    // such as those returned from calls to writetext in the proc.
3020:                    //
3021:                    if (parameters != null
3022:                            && (name.length() == 0 || name.startsWith("@"))) {
3023:                        // Sybase 12+ this token used to set output parameter results
3024:                        while (++nextParam < parameters.length) {
3025:                            if (parameters[nextParam].isOutput) {
3026:                                Object value = currentToken.dynamParamData[i];
3027:                                if (value != null) {
3028:                                    parameters[nextParam]
3029:                                            .setOutValue(Support
3030:                                                    .convert(
3031:                                                            connection,
3032:                                                            value,
3033:                                                            parameters[nextParam].jdbcType,
3034:                                                            connection
3035:                                                                    .getCharset()));
3036:                                } else {
3037:                                    parameters[nextParam].setOutValue(null);
3038:                                }
3039:                                break;
3040:                            }
3041:                        }
3042:                    }
3043:                }
3044:            }
3045:
3046:            /**
3047:             * Processes a TDS 5.0 capability token.
3048:             * <p>
3049:             * Sent after login to describe the server's capabilities.
3050:             *
3051:             * @throws IOException if an I/O error occurs
3052:             */
3053:            private void tdsCapabilityToken() throws IOException,
3054:                    ProtocolException {
3055:                in.readShort(); // Packet length
3056:                if (in.read() != 1) {
3057:                    throw new ProtocolException(
3058:                            "TDS_CAPABILITY: expected request string");
3059:                }
3060:                int capLen = in.read();
3061:                if (capLen != 11 && capLen != 0) {
3062:                    throw new ProtocolException(
3063:                            "TDS_CAPABILITY: byte count not 11");
3064:                }
3065:                byte capRequest[] = new byte[11];
3066:                if (capLen == 0) {
3067:                    Logger.println("TDS_CAPABILITY: Invalid request length");
3068:                } else {
3069:                    in.read(capRequest);
3070:                }
3071:                if (in.read() != 2) {
3072:                    throw new ProtocolException(
3073:                            "TDS_CAPABILITY: expected response string");
3074:                }
3075:                capLen = in.read();
3076:                if (capLen != 10 && capLen != 0) {
3077:                    throw new ProtocolException(
3078:                            "TDS_CAPABILITY: byte count not 10");
3079:                }
3080:                byte capResponse[] = new byte[10];
3081:                if (capLen == 0) {
3082:                    Logger.println("TDS_CAPABILITY: Invalid response length");
3083:                } else {
3084:                    in.read(capResponse);
3085:                }
3086:                //
3087:                // Request capabilities
3088:                //
3089:                // jTDS sends   01 0B 4F FF 85 EE EF 65 7F FF FF FF D6
3090:                // Sybase 11.92 01 0A    00 00 00 23 61 41 CF FF FF C6
3091:                // Sybase 12.52 01 0A    03 84 0A E3 61 41 FF FF FF C6
3092:                // Sybase 15.00 01 0B 4F F7 85 EA EB 61 7F FF FF FF C6
3093:                //
3094:                // Response capabilities
3095:                //
3096:                // jTDS sends   02 0A 00 00 04 06 80 06 48 00 00 00
3097:                // Sybase 11.92 02 0A 00 00 00 00 00 06 00 00 00 00
3098:                // Sybase 12.52 02 0A 00 00 00 00 00 06 00 00 00 00
3099:                // Sybase 15.00 02 0A 00 00 04 00 00 06 00 00 00 00
3100:                //
3101:                // Now set the correct attributes for this connection.
3102:                // See the CT_LIB documentation for details on the bit
3103:                // positions.
3104:                //
3105:                int capMask = 0;
3106:                if ((capRequest[0] & 0x02) == 0x02) {
3107:                    capMask |= SYB_UNITEXT;
3108:                }
3109:                if ((capRequest[1] & 0x03) == 0x03) {
3110:                    capMask |= SYB_DATETIME;
3111:                }
3112:                if ((capRequest[2] & 0x80) == 0x80) {
3113:                    capMask |= SYB_UNICODE;
3114:                }
3115:                if ((capRequest[3] & 0x02) == 0x02) {
3116:                    capMask |= SYB_EXTCOLINFO;
3117:                }
3118:                if ((capRequest[2] & 0x01) == 0x01) {
3119:                    capMask |= SYB_BIGINT;
3120:                }
3121:                if ((capRequest[4] & 0x04) == 0x04) {
3122:                    capMask |= SYB_BITNULL;
3123:                }
3124:                if ((capRequest[7] & 0x30) == 0x30) {
3125:                    capMask |= SYB_LONGDATA;
3126:                }
3127:                connection.setSybaseInfo(capMask);
3128:            }
3129:
3130:            /**
3131:             * Process an environment change packet.
3132:             *
3133:             * @throws IOException
3134:             * @throws SQLException
3135:             */
3136:            private void tdsEnvChangeToken() throws IOException, SQLException {
3137:                int len = in.readShort();
3138:                int type = in.read();
3139:
3140:                switch (type) {
3141:                case TDS_ENV_DATABASE: {
3142:                    int clen = in.read();
3143:                    final String newDb = in.readString(clen);
3144:                    clen = in.read();
3145:                    final String oldDb = in.readString(clen);
3146:                    connection.setDatabase(newDb, oldDb);
3147:                    break;
3148:                }
3149:
3150:                case TDS_ENV_LANG: {
3151:                    int clen = in.read();
3152:                    String language = in.readString(clen);
3153:                    clen = in.read();
3154:                    String oldLang = in.readString(clen);
3155:                    if (Logger.isActive()) {
3156:                        Logger.println("Language changed from " + oldLang
3157:                                + " to " + language);
3158:                    }
3159:                    break;
3160:                }
3161:
3162:                case TDS_ENV_CHARSET: {
3163:                    final int clen = in.read();
3164:                    final String charset = in.readString(clen);
3165:                    if (tdsVersion >= Driver.TDS70) {
3166:                        in.skip(len - 2 - clen * 2);
3167:                    } else {
3168:                        in.skip(len - 2 - clen);
3169:                    }
3170:                    connection.setServerCharset(charset);
3171:                    break;
3172:                }
3173:
3174:                case TDS_ENV_PACKSIZE: {
3175:                    final int blocksize;
3176:                    final int clen = in.read();
3177:                    blocksize = Integer.parseInt(in.readString(clen));
3178:                    if (tdsVersion >= Driver.TDS70) {
3179:                        in.skip(len - 2 - clen * 2);
3180:                    } else {
3181:                        in.skip(len - 2 - clen);
3182:                    }
3183:                    this .connection.setNetPacketSize(blocksize);
3184:                    out.setBufferSize(blocksize);
3185:                    if (Logger.isActive()) {
3186:                        Logger.println("Changed blocksize to " + blocksize);
3187:                    }
3188:                }
3189:                    break;
3190:
3191:                case TDS_ENV_LCID:
3192:                    // Only sent by TDS 7
3193:                    // In TDS 8 replaced by column specific collation info.
3194:                    // TODO Make use of this for character set conversions?
3195:                    in.skip(len - 1);
3196:                    break;
3197:
3198:                case TDS_ENV_SQLCOLLATION: {
3199:                    int clen = in.read();
3200:                    byte collation[] = new byte[5];
3201:                    if (clen == 5) {
3202:                        in.read(collation);
3203:                        connection.setCollation(collation);
3204:                    } else {
3205:                        in.skip(clen);
3206:                    }
3207:                    clen = in.read();
3208:                    in.skip(clen);
3209:                    break;
3210:                }
3211:
3212:                default: {
3213:                    if (Logger.isActive()) {
3214:                        Logger.println("Unknown environment change type 0x"
3215:                                + Integer.toHexString(type));
3216:                    }
3217:                    in.skip(len - 1);
3218:                    break;
3219:                }
3220:                }
3221:            }
3222:
3223:            /**
3224:             * Process a TDS 5 error or informational message.
3225:             *
3226:             * @throws IOException
3227:             */
3228:            private void tds5ErrorToken() throws IOException {
3229:                int pktLen = in.readShort(); // Packet length
3230:                int sizeSoFar = 6;
3231:                int number = in.readInt();
3232:                int state = in.read();
3233:                int severity = in.read();
3234:                // Discard text state
3235:                int stateLen = in.read();
3236:                in.readNonUnicodeString(stateLen);
3237:                in.read(); // == 1 if extended error data follows
3238:                // Discard status and transaction state
3239:                in.readShort();
3240:                sizeSoFar += 4 + stateLen;
3241:
3242:                int msgLen = in.readShort();
3243:                String message = in.readNonUnicodeString(msgLen);
3244:                sizeSoFar += 2 + msgLen;
3245:                final int srvNameLen = in.read();
3246:                String server = in.readNonUnicodeString(srvNameLen);
3247:                sizeSoFar += 1 + srvNameLen;
3248:
3249:                final int procNameLen = in.read();
3250:                String procName = in.readNonUnicodeString(procNameLen);
3251:                sizeSoFar += 1 + procNameLen;
3252:
3253:                int line = in.readShort();
3254:                sizeSoFar += 2;
3255:                // Skip any EED information to read rest of packet
3256:                if (pktLen - sizeSoFar > 0)
3257:                    in.skip(pktLen - sizeSoFar);
3258:
3259:                if (severity > 10) {
3260:                    messages.addDiagnostic(number, state, severity, message,
3261:                            server, procName, line);
3262:                } else {
3263:                    messages.addDiagnostic(number, state, severity, message,
3264:                            server, procName, line);
3265:                }
3266:            }
3267:
3268:            /**
3269:             * Process TDS5 dynamic SQL aknowledgements.
3270:             *
3271:             * @throws IOException
3272:             */
3273:            private void tds5DynamicToken() throws IOException {
3274:                int pktLen = in.readShort();
3275:                byte type = (byte) in.read();
3276:                /*byte status = (byte)*/in.read();
3277:                pktLen -= 2;
3278:                if (type == (byte) 0x20) {
3279:                    // Only handle aknowledgements for now
3280:                    int len = in.read();
3281:                    in.skip(len);
3282:                    pktLen -= len + 1;
3283:                }
3284:                in.skip(pktLen);
3285:            }
3286:
3287:            /**
3288:             * Process TDS 5 Dynamic results parameter descriptors.
3289:             * <p>
3290:             * With Sybase 12+ this has been superseded by the TDS5_PARAMFMT2_TOKEN
3291:             * except when used to return extended error information.
3292:             *
3293:             * @throws IOException
3294:             * @throws ProtocolException
3295:             */
3296:            private void tds5ParamFmtToken() throws IOException,
3297:                    ProtocolException {
3298:                in.readShort(); // Packet length
3299:                int paramCnt = in.readShort();
3300:                ColInfo[] params = new ColInfo[paramCnt];
3301:                for (int i = 0; i < paramCnt; i++) {
3302:                    //
3303:                    // Get the parameter details using the
3304:                    // ColInfo class as the server format is the same.
3305:                    //
3306:                    ColInfo col = new ColInfo();
3307:                    int colNameLen = in.read();
3308:                    col.realName = in.readNonUnicodeString(colNameLen);
3309:                    int column_flags = in.read(); /*  Flags */
3310:                    col.isCaseSensitive = false;
3311:                    col.nullable = ((column_flags & 0x20) != 0) ? ResultSetMetaData.columnNullable
3312:                            : ResultSetMetaData.columnNoNulls;
3313:                    col.isWriteable = (column_flags & 0x10) != 0;
3314:                    col.isIdentity = (column_flags & 0x40) != 0;
3315:                    col.isKey = (column_flags & 0x02) != 0;
3316:                    col.isHidden = (column_flags & 0x01) != 0;
3317:
3318:                    col.userType = in.readInt();
3319:                    if ((byte) in.peek() == TDS_DONE_TOKEN) {
3320:                        // Sybase 11.92 bug data type missing!
3321:                        currentToken.dynamParamInfo = null;
3322:                        currentToken.dynamParamData = null;
3323:                        // error trapped in sybasePrepare();
3324:                        messages.addDiagnostic(9999, 0, 16, "Prepare failed",
3325:                                "", "", 0);
3326:
3327:                        return; // Give up
3328:                    }
3329:                    TdsData.readType(in, col);
3330:                    // Skip locale information
3331:                    in.skip(1);
3332:                    params[i] = col;
3333:                }
3334:                currentToken.dynamParamInfo = params;
3335:                currentToken.dynamParamData = new Object[paramCnt];
3336:            }
3337:
3338:            /**
3339:             * Process a NTLM Authentication challenge.
3340:             *
3341:             * @throws IOException
3342:             * @throws ProtocolException
3343:             */
3344:            private void tdsNtlmAuthToken() throws IOException,
3345:                    ProtocolException {
3346:                int pktLen = in.readShort(); // Packet length
3347:
3348:                int hdrLen = 40;
3349:
3350:                if (pktLen < hdrLen)
3351:                    throw new ProtocolException(
3352:                            "NTLM challenge: packet is too small:" + pktLen);
3353:
3354:                byte[] ntlmMessage = new byte[pktLen];
3355:                in.read(ntlmMessage);
3356:
3357:                final int seq = getIntFromBuffer(ntlmMessage, 8);
3358:                if (seq != 2)
3359:                    throw new ProtocolException(
3360:                            "NTLM challenge: got unexpected sequence number:"
3361:                                    + seq);
3362:
3363:                final int flags = getIntFromBuffer(ntlmMessage, 20);
3364:                //NOTE: the context is always included; if not local, then it is just
3365:                //      set to all zeros.
3366:                //boolean hasContext = ((flags &   0x4000) != 0);
3367:                //final boolean hasContext = true;
3368:                //NOTE: even if target is omitted, the length will be zero.
3369:                //final boolean hasTarget  = ((flags & 0x800000) != 0);
3370:
3371:                //extract the target, if present. This will be used for ntlmv2 auth.
3372:                final int headerOffset = 40; // The assumes the context is always there, which appears to be the case.
3373:                //header has: 2 byte lenght, 2 byte allocated space, and four-byte offset.
3374:                int size = getShortFromBuffer(ntlmMessage, headerOffset);
3375:                int offset = getIntFromBuffer(ntlmMessage, headerOffset + 4);
3376:                currentToken.ntlmTarget = new byte[size];
3377:                System.arraycopy(ntlmMessage, offset, currentToken.ntlmTarget,
3378:                        0, size);
3379:
3380:                currentToken.nonce = new byte[8];
3381:                currentToken.ntlmMessage = ntlmMessage;
3382:                System.arraycopy(ntlmMessage, 24, currentToken.nonce, 0, 8);
3383:            }
3384:
3385:            private static int getIntFromBuffer(byte[] buf, int offset) {
3386:                int b1 = ((int) buf[offset] & 0xff);
3387:                int b2 = ((int) buf[offset + 1] & 0xff) << 8;
3388:                int b3 = ((int) buf[offset + 2] & 0xff) << 16;
3389:                int b4 = ((int) buf[offset + 3] & 0xff) << 24;
3390:                return b4 | b3 | b2 | b1;
3391:            }
3392:
3393:            private static int getShortFromBuffer(byte[] buf, int offset) {
3394:                int b1 = ((int) buf[offset] & 0xff);
3395:                int b2 = ((int) buf[offset + 1] & 0xff) << 8;
3396:                return b2 | b1;
3397:            }
3398:
3399:            /**
3400:             * Process a TDS 5.0 result set packet.
3401:             *
3402:             * @throws IOException
3403:             * @throws ProtocolException
3404:             */
3405:            private void tds5ResultToken() throws IOException,
3406:                    ProtocolException {
3407:                in.readShort(); // Packet length
3408:                int colCnt = in.readShort();
3409:                this .columns = new ColInfo[colCnt];
3410:                this .rowData = new Object[colCnt];
3411:                this .tables = null;
3412:
3413:                for (int colNum = 0; colNum < colCnt; ++colNum) {
3414:                    //
3415:                    // Get the column name
3416:                    //
3417:                    ColInfo col = new ColInfo();
3418:                    int colNameLen = in.read();
3419:                    col.realName = in.readNonUnicodeString(colNameLen);
3420:                    col.name = col.realName;
3421:                    int column_flags = in.read(); /*  Flags */
3422:                    col.isCaseSensitive = false;
3423:                    col.nullable = ((column_flags & 0x20) != 0) ? ResultSetMetaData.columnNullable
3424:                            : ResultSetMetaData.columnNoNulls;
3425:                    col.isWriteable = (column_flags & 0x10) != 0;
3426:                    col.isIdentity = (column_flags & 0x40) != 0;
3427:                    col.isKey = (column_flags & 0x02) != 0;
3428:                    col.isHidden = (column_flags & 0x01) != 0;
3429:
3430:                    col.userType = in.readInt();
3431:                    TdsData.readType(in, col);
3432:                    // Skip locale information
3433:                    in.skip(1);
3434:                    columns[colNum] = col;
3435:                }
3436:                endOfResults = false;
3437:            }
3438:
3439:            /**
3440:             * Process a DONE, DONEINPROC or DONEPROC token.
3441:             *
3442:             * @throws IOException
3443:             */
3444:            private void tdsDoneToken() throws IOException {
3445:                currentToken.status = (byte) in.read();
3446:                in.skip(1);
3447:                currentToken.operation = (byte) in.read();
3448:                in.skip(1);
3449:                currentToken.updateCount = in.readInt();
3450:
3451:                if (!endOfResults) {
3452:                    // This will eliminate the select row count for sybase
3453:                    currentToken.status &= ~DONE_ROW_COUNT;
3454:                    endOfResults = true;
3455:                }
3456:
3457:                //
3458:                // Check for cancel ack
3459:                //
3460:                if ((currentToken.status & DONE_CANCEL) != 0) {
3461:                    // Synchronize resetting of the cancelPending flag to ensure it
3462:                    // doesn't happen during the sending of a cancel request
3463:                    synchronized (cancelMonitor) {
3464:                        cancelPending = false;
3465:                        // Only throw an exception if this was a cancel() call
3466:                        if (cancelMonitor[0] == ASYNC_CANCEL) {
3467:                            messages.addException(new SQLException(
3468:                                    Messages.get("error.generic.cancelled",
3469:                                            "Statement"), "HY008"));
3470:                        }
3471:                    }
3472:                }
3473:
3474:                if ((currentToken.status & DONE_MORE_RESULTS) == 0) {
3475:                    //
3476:                    // There are no more results or pending cancel packets
3477:                    // to process.
3478:                    //
3479:                    endOfResponse = !cancelPending;
3480:
3481:                    if (fatalError) {
3482:                        // A fatal error has occured, the server has closed the
3483:                        // connection
3484:                        connection.setClosed();
3485:                    }
3486:                }
3487:
3488:                if (serverType == Driver.SQLSERVER) {
3489:                    //
3490:                    // MS SQL Server provides additional information we
3491:                    // can use to return special row counts for DDL etc.
3492:                    //
3493:                    if (currentToken.operation == (byte) 0xC1) {
3494:                        currentToken.status &= ~DONE_ROW_COUNT;
3495:                    }
3496:                }
3497:            }
3498:
3499:            /**
3500:             * Execute SQL using TDS 4.2 protocol.
3501:             *
3502:             * @param sql The SQL statement to execute.
3503:             * @param procName Stored procedure to execute or null.
3504:             * @param parameters Parameters for call or null.
3505:             * @param noMetaData Suppress meta data for cursor calls.
3506:             * @throws SQLException
3507:             */
3508:            private void executeSQL42(String sql, String procName,
3509:                    ParamInfo[] parameters, boolean noMetaData, boolean sendNow)
3510:                    throws IOException, SQLException {
3511:                if (procName != null) {
3512:                    // RPC call
3513:                    out.setPacketType(RPC_PKT);
3514:                    byte[] buf = Support.encodeString(connection.getCharset(),
3515:                            procName);
3516:
3517:                    out.write((byte) buf.length);
3518:                    out.write(buf);
3519:                    out.write((short) (noMetaData ? 2 : 0));
3520:
3521:                    if (parameters != null) {
3522:                        for (int i = nextParam + 1; i < parameters.length; i++) {
3523:                            if (parameters[i].name != null) {
3524:                                buf = Support.encodeString(connection
3525:                                        .getCharset(), parameters[i].name);
3526:                                out.write((byte) buf.length);
3527:                                out.write(buf);
3528:                            } else {
3529:                                out.write((byte) 0);
3530:                            }
3531:
3532:                            out.write((byte) (parameters[i].isOutput ? 1 : 0));
3533:                            TdsData.writeParam(out,
3534:                                    connection.getCharsetInfo(), null,
3535:                                    parameters[i]);
3536:                        }
3537:                    }
3538:                    if (!sendNow) {
3539:                        // Send end of packet byte to batch RPC
3540:                        out.write((byte) DONE_END_OF_RESPONSE);
3541:                    }
3542:                } else if (sql.length() > 0) {
3543:                    if (parameters != null) {
3544:                        sql = Support.substituteParameters(sql, parameters,
3545:                                connection);
3546:                    }
3547:
3548:                    out.setPacketType(QUERY_PKT);
3549:                    out.write(sql);
3550:                    if (!sendNow) {
3551:                        // Batch SQL statements
3552:                        out.write(" ");
3553:                    }
3554:                }
3555:            }
3556:
3557:            /**
3558:             * Execute SQL using TDS 5.0 protocol.
3559:             *
3560:             * @param sql The SQL statement to execute.
3561:             * @param procName Stored procedure to execute or null.
3562:             * @param parameters Parameters for call or null.
3563:             * @throws SQLException
3564:             */
3565:            private void executeSQL50(String sql, String procName,
3566:                    ParamInfo[] parameters) throws IOException, SQLException {
3567:                boolean haveParams = parameters != null;
3568:                boolean useParamNames = false;
3569:                currentToken.dynamParamInfo = null;
3570:                currentToken.dynamParamData = null;
3571:                //
3572:                // Sybase does not allow text or image parameters as parameters
3573:                // to statements or stored procedures. With Sybase 12.5 it is
3574:                // possible to use a new TDS data type to send long data as
3575:                // parameters to statements (but not procedures). This usage
3576:                // replaces the writetext command that had to be used in the past.
3577:                // As we do not support writetext, with older versions of Sybase
3578:                // we just give up and embed all text/image data in the SQL statement.
3579:                //
3580:                for (int i = 0; haveParams && i < parameters.length; i++) {
3581:                    if ("text".equals(parameters[i].sqlType)
3582:                            || "image".equals(parameters[i].sqlType)
3583:                            || "unitext".equals(parameters[i].sqlType)) {
3584:                        if (procName != null && procName.length() > 0) {
3585:                            // Call to store proc nothing we can do
3586:                            if ("text".equals(parameters[i].sqlType)
3587:                                    || "unitext".equals(parameters[i].sqlType)) {
3588:                                throw new SQLException(Messages
3589:                                        .get("error.chartoolong"), "HY000");
3590:                            }
3591:
3592:                            throw new SQLException(Messages
3593:                                    .get("error.bintoolong"), "HY000");
3594:                        }
3595:                        if (parameters[i].tdsType != TdsData.SYBLONGDATA) {
3596:                            // prepared statement substitute parameters into SQL
3597:                            sql = Support.substituteParameters(sql, parameters,
3598:                                    connection);
3599:                            haveParams = false;
3600:                            procName = null;
3601:                            break;
3602:                        }
3603:                    }
3604:                }
3605:
3606:                out.setPacketType(SYBQUERY_PKT);
3607:
3608:                if (procName == null) {
3609:                    // Use TDS_LANGUAGE TOKEN with optional parameters
3610:                    out.write((byte) TDS_LANG_TOKEN);
3611:
3612:                    if (haveParams) {
3613:                        sql = Support.substituteParamMarkers(sql, parameters);
3614:                    }
3615:
3616:                    if (connection.isWideChar()) {
3617:                        // Need to preconvert string to get correct length
3618:                        byte[] buf = Support.encodeString(connection
3619:                                .getCharset(), sql);
3620:
3621:                        out.write((int) buf.length + 1);
3622:                        out.write((byte) (haveParams ? 1 : 0));
3623:                        out.write(buf);
3624:                    } else {
3625:                        out.write((int) sql.length() + 1);
3626:                        out.write((byte) (haveParams ? 1 : 0));
3627:                        out.write(sql);
3628:                    }
3629:                } else if (procName.startsWith("#jtds")) {
3630:                    // Dynamic light weight procedure call
3631:                    out.write((byte) TDS5_DYNAMIC_TOKEN);
3632:                    out.write((short) (procName.length() + 4));
3633:                    out.write((byte) 2);
3634:                    out.write((byte) (haveParams ? 1 : 0));
3635:                    out.write((byte) (procName.length() - 1));
3636:                    out.write(procName.substring(1));
3637:                    out.write((short) 0);
3638:                } else {
3639:                    byte buf[] = Support.encodeString(connection.getCharset(),
3640:                            procName);
3641:
3642:                    // RPC call
3643:                    out.write((byte) TDS_DBRPC_TOKEN);
3644:                    out.write((short) (buf.length + 3));
3645:                    out.write((byte) buf.length);
3646:                    out.write(buf);
3647:                    out.write((short) (haveParams ? 2 : 0));
3648:                    useParamNames = true;
3649:                }
3650:
3651:                //
3652:                // Output any parameters
3653:                //
3654:                if (haveParams) {
3655:                    // First write parameter descriptors
3656:                    out.write((byte) TDS5_PARAMFMT_TOKEN);
3657:
3658:                    int len = 2;
3659:
3660:                    for (int i = nextParam + 1; i < parameters.length; i++) {
3661:                        len += TdsData.getTds5ParamSize(
3662:                                connection.getCharset(), connection
3663:                                        .isWideChar(), parameters[i],
3664:                                useParamNames);
3665:                    }
3666:
3667:                    out.write((short) len);
3668:                    out.write((short) ((nextParam < 0) ? parameters.length
3669:                            : parameters.length - 1));
3670:
3671:                    for (int i = nextParam + 1; i < parameters.length; i++) {
3672:                        TdsData.writeTds5ParamFmt(out, connection.getCharset(),
3673:                                connection.isWideChar(), parameters[i],
3674:                                useParamNames);
3675:                    }
3676:
3677:                    // Now write the actual data
3678:                    out.write((byte) TDS5_PARAMS_TOKEN);
3679:
3680:                    for (int i = nextParam + 1; i < parameters.length; i++) {
3681:                        TdsData.writeTds5Param(out,
3682:                                connection.getCharsetInfo(), parameters[i]);
3683:                    }
3684:                }
3685:            }
3686:
3687:            /**
3688:             * Returns <code>true</code> if the specified <code>procName</code>
3689:             * is a sp_prepare or sp_prepexec handle; returns <code>false</code>
3690:             * otherwise.
3691:             *
3692:             * @param procName Stored procedure to execute or <code>null</code>.
3693:             * @return <code>true</code> if the specified <code>procName</code>
3694:             *   is a sp_prepare or sp_prepexec handle; <code>false</code>
3695:             *   otherwise.
3696:             */
3697:            public static boolean isPreparedProcedureName(final String procName) {
3698:                return procName != null && procName.length() > 0
3699:                        && Character.isDigit(procName.charAt(0));
3700:            }
3701:
3702:            /**
3703:             * Execute SQL using TDS 7.0 protocol.
3704:             *
3705:             * @param sql The SQL statement to execute.
3706:             * @param procName Stored procedure to execute or <code>null</code>.
3707:             * @param parameters Parameters for call or <code>null</code>.
3708:             * @param noMetaData Suppress meta data for cursor calls.
3709:             * @throws SQLException
3710:             */
3711:            private void executeSQL70(String sql, String procName,
3712:                    ParamInfo[] parameters, boolean noMetaData, boolean sendNow)
3713:                    throws IOException, SQLException {
3714:                int prepareSql = connection.getPrepareSql();
3715:
3716:                if (parameters == null && prepareSql == EXECUTE_SQL) {
3717:                    // Downgrade EXECUTE_SQL to UNPREPARED
3718:                    // if there are no parameters.
3719:                    //
3720:                    // Should we downgrade TEMPORARY_STORED_PROCEDURES and PREPARE as well?
3721:                    // No it may be a complex select with no parameters but costly to
3722:                    // evaluate for each execution.
3723:                    prepareSql = UNPREPARED;
3724:                }
3725:
3726:                if (inBatch) {
3727:                    // For batch execution with parameters
3728:                    // we need to be consistant and use
3729:                    // execute SQL
3730:                    prepareSql = EXECUTE_SQL;
3731:                }
3732:
3733:                if (procName == null) {
3734:                    // No procedure name so not a callable statement and also
3735:                    // not a temporary stored procedure call.
3736:                    if (parameters != null) {
3737:                        if (prepareSql == TdsCore.UNPREPARED) {
3738:                            // Low tech approach just substitute parameter data into the
3739:                            // SQL statement.
3740:                            sql = Support.substituteParameters(sql, parameters,
3741:                                    connection);
3742:                        } else {
3743:                            // If we have parameters then we need to use sp_executesql to
3744:                            // parameterise the statement unless the user has specified
3745:                            ParamInfo[] params;
3746:
3747:                            params = new ParamInfo[2 + parameters.length];
3748:                            System.arraycopy(parameters, 0, params, 2,
3749:                                    parameters.length);
3750:
3751:                            params[0] = new ParamInfo(Types.LONGVARCHAR,
3752:                                    Support.substituteParamMarkers(sql,
3753:                                            parameters), ParamInfo.UNICODE);
3754:                            TdsData.getNativeType(connection, params[0]);
3755:
3756:                            params[1] = new ParamInfo(
3757:                                    Types.LONGVARCHAR,
3758:                                    Support.getParameterDefinitions(parameters),
3759:                                    ParamInfo.UNICODE);
3760:                            TdsData.getNativeType(connection, params[1]);
3761:
3762:                            parameters = params;
3763:
3764:                            // Use sp_executesql approach
3765:                            procName = "sp_executesql";
3766:                        }
3767:                    }
3768:                } else {
3769:                    // Either a stored procedure name has been supplied or this
3770:                    // statement should executed using a prepared statement handle
3771:                    if (isPreparedProcedureName(procName)) {
3772:                        // If the procedure is a prepared handle then redefine the
3773:                        // procedure name as sp_execute with the handle as a parameter.
3774:                        ParamInfo params[];
3775:
3776:                        if (parameters != null) {
3777:                            params = new ParamInfo[1 + parameters.length];
3778:                            System.arraycopy(parameters, 0, params, 1,
3779:                                    parameters.length);
3780:                        } else {
3781:                            params = new ParamInfo[1];
3782:                        }
3783:
3784:                        params[0] = new ParamInfo(Types.INTEGER, new Integer(
3785:                                procName), ParamInfo.INPUT);
3786:                        TdsData.getNativeType(connection, params[0]);
3787:
3788:                        parameters = params;
3789:
3790:                        // Use sp_execute approach
3791:                        procName = "sp_execute";
3792:                    }
3793:                }
3794:
3795:                if (procName != null) {
3796:                    // RPC call
3797:                    out.setPacketType(RPC_PKT);
3798:                    Integer shortcut;
3799:
3800:                    if (tdsVersion >= Driver.TDS80
3801:                            && (shortcut = (Integer) tds8SpNames.get(procName)) != null) {
3802:                        // Use the shortcut form of procedure name for TDS8
3803:                        out.write((short) -1);
3804:                        out.write((short) shortcut.shortValue());
3805:                    } else {
3806:                        out.write((short) procName.length());
3807:                        out.write(procName);
3808:                    }
3809:                    //
3810:                    // If noMetaData is true then column meta data will be supressed.
3811:                    // This option is used by sp_cursorfetch or optionally by sp_execute
3812:                    // provided that the required meta data has been cached.
3813:                    //
3814:                    out.write((short) (noMetaData ? 2 : 0));
3815:
3816:                    if (parameters != null) {
3817:                        // Send the required parameter data
3818:                        for (int i = nextParam + 1; i < parameters.length; i++) {
3819:                            if (parameters[i].name != null) {
3820:                                out.write((byte) parameters[i].name.length());
3821:                                out.write(parameters[i].name);
3822:                            } else {
3823:                                out.write((byte) 0);
3824:                            }
3825:
3826:                            out.write((byte) (parameters[i].isOutput ? 1 : 0));
3827:
3828:                            TdsData.writeParam(out,
3829:                                    connection.getCharsetInfo(), connection
3830:                                            .getCollation(), parameters[i]);
3831:                        }
3832:                    }
3833:                    if (!sendNow) {
3834:                        // Append RPC packets
3835:                        out.write((byte) DONE_END_OF_RESPONSE);
3836:                    }
3837:                } else if (sql.length() > 0) {
3838:                    // Simple SQL query with no parameters
3839:                    out.setPacketType(QUERY_PKT);
3840:                    out.write(sql);
3841:                    if (!sendNow) {
3842:                        // Append SQL packets
3843:                        out.write(" ");
3844:                    }
3845:                }
3846:            }
3847:
3848:            /**
3849:             * Sets the server row count (to limit the number of rows in a result set)
3850:             * and text size (to limit the size of returned TEXT/NTEXT fields).
3851:             *
3852:             * @param rowCount the number of rows to return or 0 for no limit or -1 to
3853:             *                 leave as is
3854:             * @param textSize the maximum number of bytes in a TEXT column to return
3855:             *                 or -1 to leave as is
3856:             * @throws SQLException if an error is returned by the server
3857:             */
3858:            private void setRowCountAndTextSize(int rowCount, int textSize)
3859:                    throws SQLException {
3860:                boolean newRowCount = rowCount >= 0
3861:                        && rowCount != connection.getRowCount();
3862:                boolean newTextSize = textSize >= 0
3863:                        && textSize != connection.getTextSize();
3864:                if (newRowCount || newTextSize) {
3865:                    try {
3866:                        StringBuffer query = new StringBuffer(64);
3867:                        if (newRowCount) {
3868:                            query.append("SET ROWCOUNT ").append(rowCount);
3869:                        }
3870:                        if (newTextSize) {
3871:                            query.append(" SET TEXTSIZE ").append(
3872:                                    textSize == 0 ? 2147483647 : textSize);
3873:                        }
3874:                        out.setPacketType(QUERY_PKT);
3875:                        out.write(query.toString());
3876:                        out.flush();
3877:                        endOfResponse = false;
3878:                        endOfResults = true;
3879:                        wait(0);
3880:                        clearResponseQueue();
3881:                        messages.checkErrors();
3882:                        // Update the values stored in the Connection
3883:                        connection.setRowCount(rowCount);
3884:                        connection.setTextSize(textSize);
3885:                    } catch (IOException ioe) {
3886:                        throw new SQLException(Messages.get(
3887:                                "error.generic.ioerror", ioe.getMessage()),
3888:                                "08S01");
3889:                    }
3890:                }
3891:            }
3892:
3893:            /**
3894:             * Waits for the first byte of the server response.
3895:             *
3896:             * @param timeOut the timeout period in seconds or 0
3897:             */
3898:            private void wait(int timeOut) throws IOException, SQLException {
3899:                Object timer = null;
3900:                try {
3901:                    if (timeOut > 0) {
3902:                        // Start a query timeout timer
3903:                        timer = TimerThread.getInstance().setTimer(
3904:                                timeOut * 1000,
3905:                                new TimerThread.TimerListener() {
3906:                                    public void timerExpired() {
3907:                                        TdsCore.this .cancel(true);
3908:                                    }
3909:                                });
3910:                    }
3911:                    in.peek();
3912:                } finally {
3913:                    if (timer != null) {
3914:                        if (!TimerThread.getInstance().cancelTimer(timer)) {
3915:                            throw new SQLException(Messages
3916:                                    .get("error.generic.timeout"), "HYT00");
3917:                        }
3918:                    }
3919:                }
3920:            }
3921:
3922:            /**
3923:             * Releases parameter and result set data and metadata to free up memory.
3924:             * <p/>
3925:             * This is useful before the <code>TdsCore</code> is cached for reuse.
3926:             */
3927:            public void cleanUp() {
3928:                if (endOfResponse) {
3929:                    // Clean up parameters
3930:                    returnParam = null;
3931:                    parameters = null;
3932:                    // Clean up result data and meta data
3933:                    columns = null;
3934:                    rowData = null;
3935:                    tables = null;
3936:                    // Clean up warnings; any exceptions will be cleared when thrown
3937:                    messages.clearWarnings();
3938:                }
3939:            }
3940:
3941:            /**
3942:             * Returns the diagnostic chain for this instance.
3943:             */
3944:            public SQLDiagnostic getMessages() {
3945:                return messages;
3946:            }
3947:
3948:            /**
3949:             * Converts a user supplied MAC address into a byte array.
3950:             *
3951:             * @param macString the MAC address as a hex string
3952:             * @return the MAC address as a <code>byte[]</code>
3953:             */
3954:            private static byte[] getMACAddress(String macString) {
3955:                byte[] mac = new byte[6];
3956:                boolean ok = false;
3957:
3958:                if (macString != null && macString.length() == 12) {
3959:                    try {
3960:                        for (int i = 0, j = 0; i < 6; i++, j += 2) {
3961:                            mac[i] = (byte) Integer.parseInt(macString
3962:                                    .substring(j, j + 2), 16);
3963:                        }
3964:
3965:                        ok = true;
3966:                    } catch (Exception ex) {
3967:                        // Ignore it. ok will be false.
3968:                    }
3969:                }
3970:
3971:                if (!ok) {
3972:                    Arrays.fill(mac, (byte) 0);
3973:                }
3974:
3975:                return mac;
3976:            }
3977:
3978:            /**
3979:             * Tries to figure out what client name we should identify ourselves as.
3980:             * Gets the hostname of this machine,
3981:             *
3982:             * @return name to use as the client
3983:             */
3984:            private static String getHostName() {
3985:                if (hostName != null) {
3986:                    return hostName;
3987:                }
3988:
3989:                String name;
3990:
3991:                try {
3992:                    name = java.net.InetAddress.getLocalHost().getHostName()
3993:                            .toUpperCase();
3994:                } catch (java.net.UnknownHostException e) {
3995:                    hostName = "UNKNOWN";
3996:                    return hostName;
3997:                }
3998:
3999:                int pos = name.indexOf('.');
4000:
4001:                if (pos >= 0) {
4002:                    name = name.substring(0, pos);
4003:                }
4004:
4005:                if (name.length() == 0) {
4006:                    hostName = "UNKNOWN";
4007:                    return hostName;
4008:                }
4009:
4010:                try {
4011:                    Integer.parseInt(name);
4012:                    // All numbers probably an IP address
4013:                    hostName = "UNKNOWN";
4014:                    return hostName;
4015:                } catch (NumberFormatException e) {
4016:                    // Bit tacky but simple check for all numbers
4017:                }
4018:
4019:                hostName = name;
4020:                return name;
4021:            }
4022:
4023:            /**
4024:             * A <B>very</B> poor man's "encryption".
4025:             *
4026:             * @param pw password to encrypt
4027:             * @return encrypted password
4028:             */
4029:            private static String tds7CryptPass(final String pw) {
4030:                final int xormask = 0x5A5A;
4031:                final int len = pw.length();
4032:                final char[] chars = new char[len];
4033:
4034:                for (int i = 0; i < len; ++i) {
4035:                    final int c = (int) (pw.charAt(i)) ^ xormask;
4036:                    final int m1 = (c >> 4) & 0x0F0F;
4037:                    final int m2 = (c << 4) & 0xF0F0;
4038:
4039:                    chars[i] = (char) (m1 | m2);
4040:                }
4041:
4042:                return new String(chars);
4043:            }
4044:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.