KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > internetcds > jdbc > tds > Tds


1 //
2
// Copyright 1998, 1999 CDS Networks, Inc., Medford Oregon
3
//
4
// All rights reserved.
5
//
6
// Redistribution and use in source and binary forms, with or without
7
// modification, are permitted provided that the following conditions are met:
8
// 1. Redistributions of source code must retain the above copyright
9
// notice, this list of conditions and the following disclaimer.
10
// 2. Redistributions in binary form must reproduce the above copyright
11
// notice, this list of conditions and the following disclaimer in the
12
// documentation and/or other materials provided with the distribution.
13
// 3. All advertising materials mentioning features or use of this software
14
// must display the following acknowledgement:
15
// This product includes software developed by CDS Networks, Inc.
16
// 4. The name of CDS Networks, Inc. may not be used to endorse or promote
17
// products derived from this software without specific prior
18
// written permission.
19
//
20
// THIS SOFTWARE IS PROVIDED BY CDS NETWORKS, INC. ``AS IS'' AND
21
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
// ARE DISCLAIMED. IN NO EVENT SHALL CDS NETWORKS, INC. BE LIABLE
24
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
// SUCH DAMAGE.
31
//
32

33
34
35
36 package com.internetcds.jdbc.tds;
37
38 import java.net.Socket JavaDoc;
39 import java.util.Vector JavaDoc;
40 import java.lang.Thread JavaDoc;
41 import java.util.StringTokenizer JavaDoc;
42 import java.sql.*;
43 import com.internetcds.jdbc.tds.TdsComm;
44 import com.internetcds.util.Logger;
45 import java.math.BigInteger JavaDoc;
46 import java.math.BigDecimal JavaDoc;
47 import java.util.Calendar JavaDoc;
48 import java.util.Properties JavaDoc;
49 import java.util.TimeZone JavaDoc;
50 import java.util.Locale JavaDoc;
51
52
53
54 /**
55  * Cancel the current SQL if the timeout expires.
56  *
57  * @version $Id: Tds.java,v 1.1 2006/06/23 10:39:30 sinisa Exp $
58  * @author Craig Spannring
59  */

60 class TimeoutHandler extends Thread JavaDoc
61 {
62    public static final String JavaDoc cvsVersion = "$Id: Tds.java,v 1.1 2006/06/23 10:39:30 sinisa Exp $";
63
64    java.sql.Statement JavaDoc stmt;
65    int timeout;
66
67
68    public TimeoutHandler(
69       java.sql.Statement JavaDoc stmt_,
70       int timeout_)
71    {
72       stmt = stmt_;
73       timeout = timeout_;
74    }
75
76
77    public void run()
78    {
79       try
80       {
81          sleep(timeout * 1000);
82          stmt.cancel();
83       }
84       catch(SQLException e)
85       {
86          // nop
87
}
88       catch(java.lang.InterruptedException JavaDoc e)
89       {
90          // nop
91
}
92    }
93 }
94
95
96
97 /**
98  * Implement the TDS protocol.
99  *
100  * @version $Id: Tds.java,v 1.1 2006/06/23 10:39:30 sinisa Exp $
101  * @author Craig Spannring
102  * @author Igor Petrovski
103  * @author The FreeTDS project
104  */

105 public class Tds implements TdsDefinitions
106 {
107    public static final String JavaDoc cvsVersion = "$Id: Tds.java,v 1.1 2006/06/23 10:39:30 sinisa Exp $";
108
109
110    //
111
// If the following variable is false we will consider calling
112
// unimplemented methods to be an error and will raise an exception.
113
// If you want to ignore any unimplemented methods, set it to
114
// true. Only do this if you know what you are doing and can tolerate
115
// bogus results from the unimplemented methods.
116
//
117
static boolean ignoreNotImplemented = false;
118
119
120    Socket JavaDoc sock = null;
121    TdsComm comm = null;
122
123    String JavaDoc databaseProductName;
124    String JavaDoc databaseProductVersion;
125
126    java.sql.Connection JavaDoc connection;
127    String JavaDoc host;
128    int serverType = -1; // either Tds.SYBASE or Tds.SQLSERVER
129
int port; // Port numbers are _unsigned_ 16 bit, short is too small
130
String JavaDoc database;
131    String JavaDoc user;
132    String JavaDoc password;
133    String JavaDoc appName;
134    String JavaDoc serverName;
135    String JavaDoc progName;
136    byte progMajorVersion;
137    byte progMinorVersion;
138
139    boolean haveProcNameTable = false;
140    String JavaDoc procNameGeneratorName = null;
141    String JavaDoc procNameTableName = null;
142
143    String JavaDoc initialSettings = null;
144
145    private Properties JavaDoc initialProps = null;
146    private EncodingHelper encoder = null;
147    private String JavaDoc charset = null;
148
149    // int rowCount = -1; // number of rows selected or updated
150
private boolean moreResults = false; // Is there another result set?
151

152    // Jens Jakobsen 1999-01-10
153
// Until TDS_END_TOKEN is reached we assume that there are outstanding
154
// UpdateCounts or ResultSets
155
private boolean moreResults2 = true;
156
157    CancelController cancelController = null;
158
159    SqlMessage lastServerMessage = null;
160
161    // Added 2000-06-07. Used to control TDS version-specific behavior.
162
private int tdsVer = Tds.TDS42;
163
164    // RMK 2000-06-08.
165
private boolean showWarnings = false;
166
167    // RMK 2000-06-12. Time zone offset on client (disregarding DST).
168
private int zoneOffset = Calendar.getInstance().get(Calendar.ZONE_OFFSET);
169
170    public Tds(
171       java.sql.Connection JavaDoc connection_,
172       Properties JavaDoc props_,
173       String JavaDoc initialSettings)
174       throws java.io.IOException JavaDoc, java.net.UnknownHostException JavaDoc,
175       java.sql.SQLException JavaDoc, com.internetcds.jdbc.tds.TdsException
176    {
177       connection = (java.sql.Connection JavaDoc)connection_;
178       initialProps = props_;
179
180       host = props_.getProperty("HOST");
181       serverType = Integer.parseInt(props_.getProperty("SERVERTYPE"));
182       port = Integer.parseInt(props_.getProperty("PORT"));
183       database = props_.getProperty("DBNAME");
184       user = props_.getProperty("user");
185       password = props_.getProperty("password");
186       appName = props_.getProperty("APPNAME", "jdbclib");
187       serverName = props_.getProperty("SERVERNAME", host);
188       progName = props_.getProperty("PROGNAME", "java_app");
189       progMajorVersion = (byte)DriverVersion.getDriverMajorVersion();
190       progMinorVersion = (byte)DriverVersion.getDriverMinorVersion();
191 // String verString = props_.getProperty("TDS", "42");
192
String JavaDoc verString = props_.getProperty("TDS", "7.0");
193
194       // XXX This driver doesn't properly support TDS 5.0, AFAIK.
195
// Added 2000-06-07.
196

197
198       tdsVer = TDS42;
199       if (verString.equals("5.0"))
200       {
201          tdsVer = Tds.TDS50;
202       }
203       else if (verString.equals("7.0"))
204       {
205          tdsVer = Tds.TDS70;
206       }
207       // RMK 2000-06-08
208
if (System.getProperty("TDS_SHOW_WARNINGS") != null
209           ||
210           props_.getProperty("TDS_SHOW_WARNINGS") != null)
211       {
212          showWarnings = true;
213       }
214
215       cancelController = new CancelController();
216
217       // Send the logon packet to the server
218
sock = new Socket JavaDoc(host, port);
219       sock.setTcpNoDelay(true);
220       comm = new TdsComm(sock, tdsVer);
221
222       setCharset(props_.getProperty("CHARSET"));
223
224       if (logon())
225       {
226          // everything is hunky-dory.
227
}
228       else
229       {
230          throw new SQLException("Logon failed. " + lastServerMessage);
231       }
232    }
233    
234    private void setCharset(String JavaDoc charset)
235    {
236       try
237       {
238          Logger.println("Trying to change charset to " + charset);
239       }
240       catch(java.io.IOException JavaDoc e)
241       {
242          // nop
243
}
244
245       if (charset == null || charset.length() > 30)
246       {
247          charset = "iso_1";
248       }
249          
250       if (!charset.equals(this.charset))
251       {
252          encoder = EncodingHelper.getHelper(charset);
253          if (encoder == null)
254          {
255             charset = "iso_1";
256             encoder = EncodingHelper.getHelper(charset);
257          }
258          this.charset = charset;
259       }
260    }
261    
262    EncodingHelper getEncoder() {
263       return encoder;
264    }
265    
266    public void close()
267    {
268       comm.close();
269       try
270       {
271          sock.close();
272       }
273       catch(java.io.IOException JavaDoc e)
274       {
275          // XXX should do something here
276
}
277    }
278
279    static private int toUInt(byte b)
280    {
281       int result = ((int)b) & 0x00ff;
282       return result;
283    }
284
285    public String JavaDoc toString()
286    {
287       return ""
288          + database + ", "
289          + sock.getLocalAddress() + ":" + sock.getLocalPort()
290          + " -> " + sock.getInetAddress() + ":" + sock.getPort();
291    }
292
293
294    /**
295     * Convert a JDBC escaped SQL string into the native SQL
296     *
297     * @param input escaped string to convert
298     *
299     * @return native SQL string
300     */

301    static public String JavaDoc toNativeSql(String JavaDoc input, int serverType)
302       throws SQLException
303    {
304       EscapeProcessor escape;
305       if (serverType==TdsDefinitions.SYBASE)
306       {
307          escape = new SybaseEscapeProcessor(input);
308       }
309       else
310       {
311          escape = new MSSqlServerEscapeProcessor(input);
312       }
313
314       return escape.nativeString();
315    }
316
317
318    /**
319     * Convert a JDBC java.sql.Types identifier to a SQLServer type identifier
320     *
321     * @author Craig Spannring
322     *
323     * @param jdbcType JDBC type to convert. Should be one of the
324     * constants from java.sql.Types.
325     *
326     * @return The corresponding SQLServer type identifier.
327     */

328    public static byte cvtJdbcTypeToNativeType(int jdbcType)
329       throws TdsNotImplemented
330    {
331       // This function is thread safe.
332
byte result = 0;
333       switch(jdbcType)
334       {
335          case java.sql.Types.CHAR:
336 // case java.sql.Types.VARCHAR:
337
// case java.sql.Types.LONGVARCHAR:
338
{
339             result = SYBCHAR;
340             break;
341          }
342 //Sinisa
343
//Add java.sql.types VARCHAR & LONGVARCHAR as a native type SYBVARCHAR
344
case java.sql.Types.VARCHAR:
345          case java.sql.Types.LONGVARCHAR:
346          {
347             result = SYBVARCHAR;
348             break;
349          }
350 //Sinisa
351
//Add java.sql.types VARCHAR & LONGVARCHAR as a native type SYBFLT8
352
case java.sql.Types.DECIMAL:
353             {
354                result = SYBFLT8;
355             break;
356          }
357          case java.sql.Types.INTEGER:
358          case java.sql.Types.SMALLINT:
359          case java.sql.Types.BIGINT:
360             {
361             result = SYBINT4;
362             break;
363          }
364          case java.sql.Types.REAL:
365          case java.sql.Types.DOUBLE:
366          {
367             result = SYBFLT8;
368             break;
369          }
370          case java.sql.Types.DATE:
371          case java.sql.Types.TIMESTAMP:
372          case java.sql.Types.TIME:
373          {
374             result = SYBDATETIMN;
375             break;
376          }
377          case java.sql.Types.VARBINARY:
378          case java.sql.Types.LONGVARBINARY:
379          {
380             result = SYBIMAGE;
381             break;
382          }
383          //Dusan
384
case java.sql.Types.BIT:
385          {
386             result = SYBBIT;
387             break;
388          }
389          default:
390          {
391             throw new TdsNotImplemented("cvtJdbcTypeToNativeType ("
392                                         + TdsUtil.javaSqlTypeToString(jdbcType) + ")");
393          }
394       }
395       
396       return result;
397    }
398
399
400    /**
401     * Convert a JDBC java.sql.Types identifier to a
402     * SQLServer type identifier
403     *
404     * @author Craig Spannring
405     *
406     * @param nativeType SQLServer type to convert.
407     * @param size Maximum size of data coming back from server.
408     *
409     * @return The corresponding JDBC type identifier.
410     */

411    public static int cvtNativeTypeToJdbcType(int nativeType,
412                                              int size)
413       throws TdsException
414    {
415       
416       // This function is thread safe.
417

418       int result = java.sql.Types.OTHER;
419       switch(nativeType)
420       {
421          // XXX We need to figure out how to map _all_ of these types
422
case SYBBINARY: result = java.sql.Types.BINARY; break;
423          case SYBBIT: result = java.sql.Types.BIT; break;
424          case SYBBITN: result = java.sql.Types.BIT; break;
425          case SYBCHAR: result = java.sql.Types.CHAR; break;
426          case SYBNCHAR: result = java.sql.Types.CHAR; break;
427          case SYBDATETIME4: result = java.sql.Types.TIMESTAMP; break;
428          case SYBDATETIME: result = java.sql.Types.TIMESTAMP; break;
429          case SYBDATETIMN: result = java.sql.Types.TIMESTAMP; break;
430          case SYBDECIMAL: result = java.sql.Types.DECIMAL; break;
431          case SYBNUMERIC: result = java.sql.Types.NUMERIC; break;
432          case SYBFLT8: result = java.sql.Types.DOUBLE; break;
433          case SYBFLTN: result = java.sql.Types.DOUBLE; break;
434          case SYBINT1: result = java.sql.Types.TINYINT; break;
435          case SYBINT2: result = java.sql.Types.SMALLINT; break;
436          case SYBINT4: result = java.sql.Types.INTEGER; break;
437          case SYBINTN:
438          {
439             switch (size)
440             {
441                case 1: result = java.sql.Types.TINYINT; break;
442                case 2: result = java.sql.Types.SMALLINT; break;
443                case 4: result = java.sql.Types.INTEGER; break;
444                default: throw new TdsException("Bad size of SYBINTN");
445             }
446             break;
447          }
448          // XXX Should money types by NUMERIC or OTHER?
449
case SYBSMALLMONEY: result = java.sql.Types.NUMERIC; break;
450          case SYBMONEY4: result = java.sql.Types.NUMERIC; break;
451          case SYBMONEY: result = java.sql.Types.NUMERIC; break;
452          case SYBMONEYN: result = java.sql.Types.NUMERIC; break;
453 // case SYBNUMERIC: result = java.sql.Types.NUMERIC; break;
454
case SYBREAL: result = java.sql.Types.REAL; break;
455          case SYBTEXT: result = java.sql.Types.LONGVARCHAR; break;
456          case SYBNTEXT: result = java.sql.Types.LONGVARCHAR; break;
457          case SYBIMAGE: result = java.sql.Types.VARBINARY; break;
458          case SYBVARBINARY: result = java.sql.Types.VARBINARY; break;
459          case SYBVARCHAR: result = java.sql.Types.VARCHAR; break;
460          case SYBNVARCHAR: result = java.sql.Types.VARCHAR; break;
461 // case SYBVOID: result = java.sql.Types. ; break;
462
default: throw new TdsException("Unknown native data type "
463                                          + Integer.toHexString(
464                                             nativeType&0xff));
465       }
466       return result;
467    } /* cvtNativeTypeToJdbcType() */
468
469
470    /**
471     * Return the type of server that we attempted to connect to.
472     *
473     * @return TdsDefinitions.SYBASE or TdsDefinitions.SQLSERVER
474     */

475    public int getServerType()
476    {
477       return serverType;
478    }
479    
480       
481    /**
482     * Try to figure out what client name we should identify
483     * ourselves as. Get the hostname of this machine,
484     *
485     * @return name we will use as the client.
486     */

487    private String JavaDoc getClientName()
488    {
489       // This method is thread safe.
490
String JavaDoc tmp;
491       try
492       {
493          tmp = java.net.InetAddress.getLocalHost().getHostName();
494       }
495       catch(java.net.UnknownHostException JavaDoc e)
496       {
497          tmp = "";
498       }
499       StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(tmp, ".");
500
501
502
503       if (!st.hasMoreTokens())
504       {
505          // This means hostname wasn't found for this machine.
506
return "JOHNDOE";
507       }
508
509       // Look at the first (and possibly only) word in the name.
510
tmp = st.nextToken();
511       if (tmp.length()==0)
512       {
513          // This means the hostname had no leading component.
514
// (This case would be strange.)
515
return "JANEDOE";
516       }
517       else if (Character.isDigit(tmp.charAt(0)))
518       {
519          // This probably means that the name was a quad-decimal
520
// number. We don't want to send that as our name,
521
// so make one up.
522
return "BABYDOE";
523       }
524       else
525       {
526          // Ah, Life is good. We have a name. All other
527
// applications I've seen have upper case client names,
528
// and so shall we.
529
return tmp.toUpperCase();
530       }
531    }
532
533
534    /**
535     * Log onto the SQLServer
536     * <p>
537     *
538     * This method is not synchronized and does not need to be so long
539     * as it can only be called from the constructor.
540     *
541     * <p>
542     * <U>Login Packet</U>
543     * <P>
544     * Packet type (first byte) is 2. The following is from tds.h the numbers
545     * on the left are offsets <I>not including</I> the packet header.
546     * <br>
547     * Note: The logical logon packet is split into two physical
548     * packets. Each physical packet has its own header.
549     * <br>
550     * <PRE>
551     * -- 0 -- DBCHAR host_name[30];
552     * -- 30 -- DBTINYINT host_name_length;
553     * -- 31 -- DBCHAR user_name[30];
554     * -- 61 -- DBTINYINT user_name_length;
555     * -- 62 -- DBCHAR password[30];
556     * -- 92 -- DBTINYINT password_length;
557     * -- 93 -- DBCHAR host_process[30];
558     * -- 123 -- DBTINYINT host_process_length;
559     * -- 124 -- DBCHAR magic1[6]; -- here were most of the mystery stuff is --
560     * -- 130 -- DBTINYINT bulk_copy;
561     * -- 131 -- DBCHAR magic2[9]; -- here were most of the mystery stuff is --
562     * -- 140 -- DBCHAR app_name[30];
563     * -- 170 -- DBTINYINT app_name_length;
564     * -- 171 -- DBCHAR server_name[30];
565     * -- 201 -- DBTINYINT server_name_length;
566     * -- 202 -- DBCHAR magic3; -- 0, dont know this one either --
567     * -- 203 -- DBTINYINT password2_length;
568     * -- 204 -- DBCHAR password2[30];
569     * -- 234 -- DBCHAR magic4[223];
570     * -- 457 -- DBTINYINT password2_length_plus2;
571     * -- 458 -- DBSMALLINT major_version; -- TDS version --
572     * -- 460 -- DBSMALLINT minor_version; -- TDS version --
573     * -- 462 -- DBCHAR library_name[10]; -- Ct-Library or DB-Library --
574     * -- 472 -- DBTINYINT library_length; -- Ct-Library or DB-Library --
575     * -- 473 -- DBSMALLINT major_version2; -- program version --
576     * -- 475 -- DBSMALLINT minor_version2; -- program version --
577     * -- 477 -- DBCHAR magic6[3]; -- ? last two octets are 13 and 17 --
578     * -- bdw reports last two as 12 and 16 here --
579     * -- possibly a bitset flag --
580     * -- 480 -- DBCHAR language[30]; -- ie us-english --
581     * -- second packet --
582     * -- 524 -- DBTINYINT language_length; -- 10 in this case --
583     * -- 525 -- DBCHAR magic7; -- no clue... has 1 in the first octet --
584     * -- bdw reports 0x0 --
585     * -- 526 -- DBSMALLINT old_secure; -- explaination? --
586     * -- 528 -- DBTINYINT encrypted; -- 1 means encrypted all password fields blank --
587     * -- 529 -- DBCHAR magic8; -- no clue... zeros --
588     * -- 530 -- DBCHAR sec_spare[9]; -- explaination --
589     * -- 539 -- DBCHAR char_set[30]; -- ie iso_1 --
590     * -- 569 -- DBTINYINT char_set_length; -- 5 --
591     * -- 570 -- DBTINYINT magic9; -- 1 --
592     * -- 571 -- DBCHAR block_size[6]; -- in text --
593     * -- 577 -- DBTINYINT block_size_length;
594     * -- 578 -- DBCHAR magic10[25]; -- lots of stuff here...no clue --
595     *
596     * </PRE>
597     *
598     * This routine will basically eat all of the data returned from the
599     * SQLServer.
600     *
601     * @author Craig Spannring
602     *
603     * @exception TdsUnknownPacketSubType
604     * @exception com.internetcds.jdbc.tds.TdsException
605     * @exception java.io.IOException
606     * @exception java.sql.SQLException
607     */

608    private boolean logon()
609       throws java.sql.SQLException JavaDoc,
610       TdsUnknownPacketSubType, java.io.IOException JavaDoc,
611       com.internetcds.jdbc.tds.TdsException
612    {
613       boolean isOkay = true;
614       byte pad = (byte) 0;
615       byte[] empty = new byte[0];
616
617       // Added 2000-06-07.
618
if (tdsVer == Tds.TDS70)
619          send70Login();
620       else {
621
622          comm.startPacket(TdsComm.LOGON);
623
624          // hostname (offset0)
625
// comm.appendString("TOLEDO", 30, (byte)0);
626
byte[] tmp = encoder.getBytes(getClientName());
627          comm.appendBytes(tmp, 30, pad);
628          comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30));
629
630          // username (offset 31 0x1f)
631
tmp = encoder.getBytes(user);
632          comm.appendBytes(tmp, 30, pad);
633          comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30));
634
635          // password (offset 62 0x3e)
636
tmp = encoder.getBytes(password);
637          comm.appendBytes(tmp, 30, pad);
638          comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30));
639
640          // hostproc (offset 93 0x5d)
641
tmp = encoder.getBytes("00000116");
642          comm.appendBytes(tmp, 8, pad);
643
644          // unused (offset 109 0x6d)
645
comm.appendBytes(empty, (30-14), pad);
646
647          // apptype (offset )
648
comm.appendByte((byte)0x0);
649          comm.appendByte((byte)0xA0);
650          comm.appendByte((byte)0x24);
651          comm.appendByte((byte)0xCC);
652          comm.appendByte((byte)0x50);
653          comm.appendByte((byte)0x12);
654          
655          // hostproc length (offset )
656
comm.appendByte((byte)8);
657
658          // type of int2
659
comm.appendByte((byte)3);
660
661          // type of int4
662
comm.appendByte((byte)1);
663
664          // type of char
665
comm.appendByte((byte)6);
666
667          // type of flt
668
comm.appendByte((byte)10);
669
670          // type of date
671
comm.appendByte((byte)9);
672
673          // notify of use db
674
comm.appendByte((byte)1);
675
676          // disallow dump/load and bulk insert
677
comm.appendByte((byte)1);
678
679          // sql interface type
680
comm.appendByte((byte)0);
681
682          // type of network connection
683
comm.appendByte((byte)0);
684
685          // spare[7]
686
comm.appendBytes(empty, 7, pad);
687
688          // appname
689
tmp = encoder.getBytes(appName);
690          comm.appendBytes(tmp, 30, pad);
691          comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30));
692
693          // server name
694
tmp = encoder.getBytes(serverName);
695          comm.appendBytes(tmp, 30, pad);
696          comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30));
697
698          // remote passwords
699
comm.appendBytes(empty, 2, pad);
700          tmp = encoder.getBytes(password);
701          comm.appendBytes(tmp, 253, pad);
702          comm.appendByte((byte)(tmp.length < 253 ? tmp.length+2 : 253+2));
703
704          // tds version
705
comm.appendByte((byte)4);
706          comm.appendByte((byte)2);
707          comm.appendByte((byte)0);
708          comm.appendByte((byte)0);
709
710          // prog name
711
tmp = encoder.getBytes(progName);
712          comm.appendBytes(tmp, 10, pad);
713          comm.appendByte((byte)(tmp.length < 10 ? tmp.length : 10));
714
715          // prog version
716
comm.appendByte((byte) 6); // Tell the server we can handle SQLServer version 6
717
comm.appendByte((byte) 0); // Send zero to tell the server we can't handle any other version
718
comm.appendByte((byte) 0);
719          comm.appendByte((byte) 0);
720
721          // auto convert short
722
comm.appendByte((byte)0);
723
724          // type of flt4
725
comm.appendByte((byte)0x0D);
726
727          // type of date4
728
comm.appendByte((byte)0x11);
729
730          // language
731
tmp = encoder.getBytes("us_english");
732          comm.appendBytes(tmp, 30, pad);
733          comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30));
734
735          // notify on lang change
736
comm.appendByte((byte)1);
737
738          // security label hierachy
739
comm.appendShort((short)0);
740
741          // security components
742
comm.appendBytes(empty, 8, pad);
743
744          // security spare
745
comm.appendShort((short)0);
746
747          // security login role
748
comm.appendByte((byte)0);
749
750          // charset
751
tmp = encoder.getBytes(charset);
752          comm.appendBytes(tmp, 30, pad);
753          comm.appendByte((byte)(tmp.length < 30 ? tmp.length : 30));
754
755          // notify on charset change
756
comm.appendByte((byte)1);
757
758          // length of tds packets
759
tmp = encoder.getBytes("512");
760          comm.appendBytes(tmp, 6, pad);
761          comm.appendByte((byte)3);
762
763          // pad out to a longword
764
comm.appendBytes(empty, 8, pad);
765
766          moreResults2=true; //JJ 1999-01-10
767
}
768
769       comm.sendPacket();
770
771       // Get the reply to the logon packet.
772
PacketResult result;
773
774       while (! ((result = processSubPacket()) instanceof PacketEndTokenResult))
775       {
776          if (result instanceof PacketErrorResult)
777          {
778             isOkay = false;
779          }
780          // XXX Should really process some more types of packets.
781
}
782
783
784       if (isOkay)
785       {
786          // XXX Should we move this to the Connection class?
787
isOkay = changeSettings(database, initialSettings);
788       }
789
790       // XXX Possible bug. What happend if this is cancelled before the logon
791
// takes place? Should isOkay be false?
792
return isOkay;
793    }
794
795
796    /*
797     * New code added to handle TDS 7.0 login, which uses a completely
798     * different packet layout. Logic taken directly from freetds C
799     * code in tds/login.c. Lots of magic values: I don't pretend
800     * to have any idea what most of this means.
801     *
802     * Added 2000-06-05.
803     */

804    private void send70Login() throws java.io.IOException JavaDoc {
805
806       byte[] magic1 = {(byte)0006, (byte)0203, (byte)0362, (byte)0370,
807                         (byte)0377, (byte)0000, (byte)0000, (byte)0000,
808                         (byte)0000, (byte)0340, (byte)0003, (byte)0000,
809                         (byte)0000, (byte)0210, (byte)0377, (byte)0377,
810                         (byte)0377, (byte)0066, (byte)0004, (byte)0000,
811                         (byte)0000};
812       byte[] magic2 = {(byte)0000, (byte)0100, (byte)0063, (byte)0232,
813                         (byte)0153, (byte)0120};
814       byte[] magic3 = encoder.getBytes("NTLMSSP");
815       String JavaDoc libName = "DB-Library";
816       byte pad = (byte)0;
817       byte[] empty = new byte[0];
818       String JavaDoc appName = "CDR";
819       short len = (short)(86 + 2 * (user.length() +
820                                          password.length() +
821                                          appName.length() +
822                                          serverName.length() +
823                                          libName.length()));
824       short packSize = (short)(len + 48);
825       comm.startPacket(TdsComm.LOGON70);
826       comm.appendTdsShort(packSize);
827       comm.appendBytes(empty, 5, pad);
828       comm.appendByte((byte)0x70);
829       comm.appendBytes(empty, 7, pad);
830       comm.appendBytes(magic1, 21, pad);
831
832       // Pack up value lengths, positions.
833
short curPos = 86;
834
835       // Unknown
836
comm.appendTdsShort(curPos);
837       comm.appendTdsShort((short)0);
838
839       // Username
840
comm.appendTdsShort(curPos);
841       comm.appendTdsShort((short)user.length());
842       curPos += user.length() * 2;
843
844       // Password
845
comm.appendTdsShort(curPos);
846       comm.appendTdsShort((short)password.length());
847       curPos += password.length() * 2;
848
849       // App name
850
comm.appendTdsShort(curPos);
851       comm.appendTdsShort((short)appName.length());
852       curPos += appName.length() * 2;
853       
854       // Server name
855
comm.appendTdsShort(curPos);
856       comm.appendTdsShort((short)serverName.length());
857       curPos += serverName.length() * 2;
858
859       // Another unknown value
860
comm.appendTdsShort((short)0);
861       comm.appendTdsShort((short)0);
862
863       // Library name
864
comm.appendTdsShort(curPos);
865       comm.appendTdsShort((short)libName.length());
866       curPos += libName.length() * 2;
867
868       // Two more unknowns
869
comm.appendTdsShort(curPos);
870       comm.appendTdsShort((short)0);
871       comm.appendTdsShort(curPos);
872       comm.appendTdsShort((short)0);
873
874       // More magic.
875
comm.appendBytes(magic2, 6, pad);
876       comm.appendTdsShort(len);
877       comm.appendTdsShort((short)0x30);
878       comm.appendTdsShort(packSize);
879       comm.appendTdsShort((short)0);
880
881       // Pack up the login values.
882
String JavaDoc scrambledPw = tds7CryptPass(password);
883       comm.appendChars(user);
884       comm.appendChars(scrambledPw);
885       comm.appendChars(appName);
886       comm.appendChars(serverName);
887       comm.appendChars(libName);
888
889       // Still more magic!
890
comm.appendBytes(magic3, 7, pad);
891       comm.appendByte((byte)0);
892       comm.appendByte((byte)1);
893       comm.appendBytes(empty, 3, pad);
894       comm.appendByte((byte)6);
895       comm.appendByte((byte)130);
896       comm.appendBytes(empty, 22, pad);
897       comm.appendByte((byte)48);
898       comm.appendBytes(empty, 7, pad);
899       comm.appendByte((byte)48);
900       comm.appendBytes(empty, 3, pad);
901    }
902
903    /**
904     * This is a <B>very</B> poor man's "encryption."
905     */

906    private static String JavaDoc tds7CryptPass(String JavaDoc pw) {
907       int xormask = 0x5A5A;
908       int len = pw.length();
909       char[] chars = new char[len];
910       for (int i = 0; i < len; ++i) {
911          int c = (int)(pw.charAt(i)) ^ xormask;
912          int m1 = (c >> 4) & 0x0F0F;
913          int m2 = (c << 4) & 0xF0F0;
914          chars[i] = (char)(m1 | m2);
915       }
916       return new String JavaDoc(chars);
917    }
918
919    /**
920     * change the connection level settings for this connection
921     * stream to the database.
922     *
923     * @return true if the database accepted the changes, false if rejected.
924     */

925    synchronized public boolean changeSettings(
926       String JavaDoc database,
927       String JavaDoc settings)
928