node-firebird 0.9.6 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -11,7 +11,9 @@ var
11
11
  BitSet = serialize.BitSet,
12
12
  messages = require('./messages.js'),
13
13
  crypt = require('./unix-crypt.js'),
14
- path = require('path');
14
+ path = require('path'),
15
+ srp = require('./srp'),
16
+ BigInt = require('big-integer');
15
17
 
16
18
  if (typeof(setImmediate) === 'undefined') {
17
19
  global.setImmediate = function(cb) {
@@ -274,7 +276,9 @@ const
274
276
  ptype_rpc = 2, // Simple remote procedure call
275
277
  ptype_batch_send = 3, // Batch sends, no asynchrony
276
278
  ptype_out_of_band = 4, // Batch sends w/ out of band notification
277
- ptype_lazy_send = 5; // Deferred packets delivery;
279
+ ptype_lazy_send = 5, // Deferred packets delivery;
280
+ ptype_mask = 0xFF, // Mask - up to 255 types of protocol
281
+ pflag_compress = 0x100; // Turn on compression if possible
278
282
 
279
283
  const SUPPORTED_PROTOCOL = [
280
284
  [PROTOCOL_VERSION10, ARCHITECTURE_GENERIC, ptype_rpc, ptype_batch_send, 1],
@@ -790,11 +794,25 @@ const
790
794
  DEFAULT_PAGE_SIZE = 4096,
791
795
  DEFAULT_SVC_NAME = 'service_mgr';
792
796
 
793
- const AUTH_PLUGIN_LEGACY = 'Legacy_Auth';
797
+ const AUTH_PLUGIN_LEGACY = 'Legacy_Auth',
798
+ AUTH_PLUGIN_SRP = 'Srp';
799
+ // AUTH_PLUGIN_SRP256 = 'Srp256';
794
800
 
795
801
  const
796
- AUTH_PLUGIN_LIST = [AUTH_PLUGIN_LEGACY],
797
- LEGACY_AUTH_SALT = '9z';
802
+ // AUTH_PLUGIN_LIST = [AUTH_PLUGIN_SRP256, AUTH_PLUGIN_SRP, AUTH_PLUGIN_LEGACY],
803
+ AUTH_PLUGIN_LIST = [AUTH_PLUGIN_SRP, AUTH_PLUGIN_LEGACY],
804
+ // AUTH_PLUGIN_SRP_LIST = [AUTH_PLUGIN_SRP256, AUTH_PLUGIN_SRP],
805
+ AUTH_PLUGIN_SRP_LIST = [AUTH_PLUGIN_SRP],
806
+ LEGACY_AUTH_SALT = '9z',
807
+ WIRE_CRYPT_DISABLE = 0,
808
+ WIRE_CRYPT_ENABLE = 1;
809
+
810
+ exports.AUTH_PLUGIN_LEGACY = AUTH_PLUGIN_LEGACY;
811
+ exports.AUTH_PLUGIN_SRP = AUTH_PLUGIN_SRP;
812
+ // exports.AUTH_PLUGIN_SRP256 = AUTH_PLUGIN_SRP256;
813
+
814
+ exports.WIRE_CRYPT_DISABLE = WIRE_CRYPT_DISABLE;
815
+ exports.WIRE_CRYPT_ENABLE = WIRE_CRYPT_ENABLE;
798
816
 
799
817
  exports.ISOLATION_READ_UNCOMMITTED = ISOLATION_READ_UNCOMMITTED;
800
818
  exports.ISOLATION_READ_COMMITED = ISOLATION_READ_COMMITED;
@@ -812,16 +830,20 @@ if (!String.prototype.padLeft) {
812
830
  /**
813
831
  * Escape value
814
832
  * @param {Object} value
833
+ * @param {Number} protocolVersion (optional, default: PROTOCOL_VERSION13)
815
834
  * @return {String}
816
835
  */
817
- exports.escape = function(value) {
836
+ exports.escape = function(value, protocolVersion) {
818
837
 
819
838
  if (value === null || value === undefined)
820
839
  return 'NULL';
821
840
 
822
841
  switch (typeof(value)) {
823
842
  case 'boolean':
824
- return value ? '1' : '0';
843
+ if ((protocolVersion || PROTOCOL_VERSION13) >= PROTOCOL_VERSION13)
844
+ return value ? 'true' : 'false';
845
+ else
846
+ return value ? '1' : '0';
825
847
  case 'number':
826
848
  return value.toString();
827
849
  case 'string':
@@ -1280,7 +1302,9 @@ SQLParamBool.prototype.calcBlr = function(blr) {
1280
1302
  ***************************************/
1281
1303
 
1282
1304
  function isError(obj) {
1283
- return (obj instanceof Object && obj.status);
1305
+ return Boolean(
1306
+ obj != null && typeof obj === "object" && !Array.isArray(obj) && obj.status
1307
+ );
1284
1308
  }
1285
1309
 
1286
1310
  function doCallback(obj, callback) {
@@ -1294,7 +1318,11 @@ function doCallback(obj, callback) {
1294
1318
  }
1295
1319
 
1296
1320
  if (isError(obj)) {
1297
- callback(new Error(obj.message));
1321
+ var error = new Error(obj.message);
1322
+ var status = obj.status && obj.status.length && obj.status[0] || {};
1323
+ error.gdscode = status.gdscode; // main error gds code
1324
+ error.gdsparams = status.params; // parameters (constraint name, table, etc.)
1325
+ callback(error);
1298
1326
  return;
1299
1327
  }
1300
1328
 
@@ -1379,6 +1407,7 @@ Transaction.prototype.newStatement = function(query, callback) {
1379
1407
  Transaction.prototype.execute = function(query, params, callback, custom) {
1380
1408
 
1381
1409
  if (params instanceof Function) {
1410
+ custom = callback;
1382
1411
  callback = params;
1383
1412
  params = undefined;
1384
1413
  }
@@ -1454,6 +1483,29 @@ Transaction.prototype.execute = function(query, params, callback, custom) {
1454
1483
  });
1455
1484
  };
1456
1485
 
1486
+ Transaction.prototype.sequentially = function (query, params, on, callback, asArray) {
1487
+
1488
+ if (params instanceof Function) {
1489
+ asArray = callback;
1490
+ callback = on;
1491
+ on = params;
1492
+ params = undefined;
1493
+ }
1494
+
1495
+ if (on === undefined){
1496
+ throw new Error('Expected "on" delegate.');
1497
+ }
1498
+
1499
+ if (callback instanceof Boolean) {
1500
+ asArray = callback;
1501
+ callback = undefined;
1502
+ }
1503
+
1504
+ var self = this;
1505
+ self.execute(query, params, callback, { asObject: !asArray, asStream: true, on: on });
1506
+ return self;
1507
+ };
1508
+
1457
1509
  Transaction.prototype.query = function(query, params, callback) {
1458
1510
 
1459
1511
  if (params instanceof Function) {
@@ -1503,7 +1555,7 @@ Database.prototype.__proto__ = Object.create(Events.EventEmitter.prototype, {
1503
1555
  });
1504
1556
 
1505
1557
  Database.prototype.escape = function(value) {
1506
- return exports.escape(value);
1558
+ return exports.escape(value, this.connection.accept.protocolVersion);
1507
1559
  };
1508
1560
 
1509
1561
  Database.prototype.detach = function(callback, force) {
@@ -1572,6 +1624,7 @@ Database.prototype.newStatement = function (query, callback) {
1572
1624
  Database.prototype.execute = function(query, params, callback, custom) {
1573
1625
 
1574
1626
  if (params instanceof Function) {
1627
+ custom = callback;
1575
1628
  callback = params;
1576
1629
  params = undefined;
1577
1630
  }
@@ -1606,19 +1659,25 @@ Database.prototype.execute = function(query, params, callback, custom) {
1606
1659
 
1607
1660
  Database.prototype.sequentially = function(query, params, on, callback, asArray) {
1608
1661
 
1609
- if (params instanceof Function) {
1610
- asArray = callback;
1611
- callback = on;
1612
- on = params;
1613
- params = undefined;
1614
- }
1662
+ if (params instanceof Function) {
1663
+ asArray = callback;
1664
+ callback = on;
1665
+ on = params;
1666
+ params = undefined;
1667
+ }
1615
1668
 
1616
- if (on === undefined)
1617
- throw new Error('Expected "on" delegate.');
1669
+ if (on === undefined){
1670
+ throw new Error('Expected "on" delegate.');
1671
+ }
1618
1672
 
1619
- var self = this;
1620
- self.execute(query, params, callback, { asObject: !asArray, asStream: true, on: on });
1621
- return self;
1673
+ if (callback instanceof Boolean) {
1674
+ asArray = callback;
1675
+ callback = undefined;
1676
+ }
1677
+
1678
+ var self = this;
1679
+ self.execute(query, params, callback, { asObject: !asArray, asStream: true, on: on });
1680
+ return self;
1622
1681
  };
1623
1682
 
1624
1683
  Database.prototype.query = function(query, params, callback) {
@@ -1793,7 +1852,7 @@ ServiceManager.prototype._infosmapping = {
1793
1852
  "63"/*isc_info_svc_to_eof*/ : "",
1794
1853
  "64"/*isc_info_svc_timeout*/ : "",
1795
1854
  "65"/*isc_info_svc_get_licensed_users*/ : "",
1796
- "66"/*isc_info_svc_limbo_trans*/ : "",
1855
+ "66"/*isc_info_svc_limbo_trans*/ : "limbotrans",
1797
1856
  "67"/*isc_info_svc_running*/ : "",
1798
1857
  "68"/*isc_info_svc_get_users*/ : "fbusers",
1799
1858
  "78"/*isc_info_svc_stdin*/ : ""
@@ -2175,11 +2234,11 @@ ServiceManager.prototype._fixpropertie = function (options, callback) {
2175
2234
  var online = options.bringonline || false;
2176
2235
  var shutdown = options.shutdown != null ? options.shutdown : null; // 0 Forced, 1 deny transaction, 2 deny attachment
2177
2236
  var shutdowndelay = options.shutdowndelay || 0;
2178
- var shutdownmode = options.shutdownmode != null ? options.shutdownmode : null; // 0 normal 1 multi 2 single 3 full
2237
+ var shutdownmode = options.shutdownmode; // 0 normal 1 multi 2 single 3 full
2179
2238
  var shadow = options.activateshadow || false;
2180
- var forcewrite = options.forcewrite!=null?options.forcewrite:null;
2181
- var reservespace = options.reservespace!=null?options.reservespace:null;
2182
- var accessmode = options.accessmode!=null?options.accessmode:null; // 0 readonly 1 readwrite
2239
+ var forcewrite = options.forcewrite;
2240
+ var reservespace = options.reservespace;
2241
+ var accessmode = options.accessmode; // 0 readonly 1 readwrite
2183
2242
 
2184
2243
  if (dbpath == null || dbpath.length === 0) {
2185
2244
  doError(new Error('No database specified'), callback);
@@ -2213,9 +2272,9 @@ ServiceManager.prototype._fixpropertie = function (options, callback) {
2213
2272
  }
2214
2273
  }
2215
2274
  if (forcewrite) blr.addBytes([isc_spb_prp_write_mode, isc_spb_prp_wm_sync]);
2216
- if (forcewrite != null && !forcewrite) blr.addBytes([isc_spb_prp_write_mode, isc_spb_prp_wm_async]);
2217
- if (accessmode) blr.addBytes([isc_spb_prp_access_mode, isc_spb_prp_am_readwrite]);
2218
- if (accessmode != null && !accessmode) blr.addBytes([isc_spb_prp_access_mode, isc_spb_prp_am_readonly]);
2275
+ if (forcewrite === false) blr.addBytes([isc_spb_prp_write_mode, isc_spb_prp_wm_async]);
2276
+ if (accessmode === 1) blr.addBytes([isc_spb_prp_access_mode, isc_spb_prp_am_readwrite]);
2277
+ if (accessmode === 0) blr.addBytes([isc_spb_prp_access_mode, isc_spb_prp_am_readonly]);
2219
2278
  if (reservespace) blr.addBytes([isc_spb_prp_reserve_space, isc_spb_prp_res]);
2220
2279
  if (reservespace != null && !reservespace) blr.addBytes([isc_spb_prp_reserve_space, isc_spb_prp_res_use_full]);
2221
2280
  var opts = 0;
@@ -2265,6 +2324,11 @@ const SHUTDOWNEX_MODE = {
2265
2324
  2: isc_spb_prp_sm_single,
2266
2325
  3: isc_spb_prp_sm_full
2267
2326
  };
2327
+ const ShutdownMode = { NORMAL: 0, MULTI: 1, SINGLE: 2, FULL: 3 };
2328
+ const ShutdownKind = { FORCED: 0, DENY_TRANSACTION: 1, DENY_ATTACHMENT: 2 };
2329
+ exports.ShutdownMode = ShutdownMode;
2330
+ exports.ShutdownKind = ShutdownKind;
2331
+
2268
2332
  ServiceManager.prototype.Shutdown = function (db, kind, delay, mode, callback) {
2269
2333
  // mode parameter is for server version >= 2.0
2270
2334
  if (mode instanceof Function) {
@@ -2355,7 +2419,7 @@ ServiceManager.prototype.commit = function(db, transactid, callback) {
2355
2419
  doError(new Error(err), callback);
2356
2420
  return;
2357
2421
  }
2358
- self._createOutputStream(options.optread, options.buffersize, callback);
2422
+ self._createOutputStream(null, null, callback);
2359
2423
  });
2360
2424
  }
2361
2425
 
@@ -2376,7 +2440,7 @@ ServiceManager.prototype.rollback = function (db, transactid, callback) {
2376
2440
  doError(new Error(err), callback);
2377
2441
  return;
2378
2442
  }
2379
- self._createOutputStream(options.optread, options.buffersize, callback);
2443
+ self._createOutputStream(null, null, callback);
2380
2444
  });
2381
2445
  }
2382
2446
 
@@ -2397,7 +2461,7 @@ ServiceManager.prototype.recover = function (db, transactid, callback) {
2397
2461
  doError(new Error(err), callback);
2398
2462
  return;
2399
2463
  }
2400
- self._createOutputStream(options.optread, options.buffersize, callback);
2464
+ self._createOutputStream(null, null, callback);
2401
2465
  });
2402
2466
  }
2403
2467
 
@@ -2576,12 +2640,11 @@ ServiceManager.prototype.getFbserverInfos = function (infos, options, callback)
2576
2640
  };
2577
2641
  // if infos is empty all options are asked to the service
2578
2642
 
2579
- var tops = [];
2643
+ var tops = [], empty = isEmpty(infos);
2580
2644
  for (popts in opts)
2581
- if (infos[popts] || infos.length == 0)
2645
+ if (empty || infos[popts])
2582
2646
  tops.push(opts[popts]);
2583
2647
 
2584
-
2585
2648
  var self = this;
2586
2649
  this.connection.svcquery(tops, buffersize, timeout, function (err, data) {
2587
2650
  if (err || !data.buffer) {
@@ -2592,6 +2655,11 @@ ServiceManager.prototype.getFbserverInfos = function (infos, options, callback)
2592
2655
  });
2593
2656
  }
2594
2657
 
2658
+ function isEmpty(obj){
2659
+ for(var p in obj) return false;
2660
+ return true;
2661
+ }
2662
+
2595
2663
  ServiceManager.prototype.startTrace = function (options, callback) {
2596
2664
  var self = this;
2597
2665
  var blr = this.connection._blr;
@@ -2689,7 +2757,6 @@ ServiceManager.prototype.stopTrace = function (options, callback) {
2689
2757
  ServiceManager.prototype.getTraceList = function (options, callback) {
2690
2758
  var self = this;
2691
2759
  var blr = this.connection._blr;
2692
- var optread = options.optread || 'byline';
2693
2760
  blr.pos = 0;
2694
2761
  blr.addByte(isc_action_svc_trace_list);
2695
2762
  this.connection.svcstart(blr, function (err, data) {
@@ -2767,7 +2834,7 @@ ServiceManager.prototype.readlimbo = function (options, callback) {
2767
2834
  }
2768
2835
 
2769
2836
  // Pooling
2770
- exports.pool = function(max, options, callback) {
2837
+ exports.pool = function(max, options) {
2771
2838
  return new Pool(max, Object.assign({}, options, { isPool: true }));
2772
2839
  };
2773
2840
 
@@ -2838,17 +2905,40 @@ Pool.prototype.check = function() {
2838
2905
  return self;
2839
2906
  };
2840
2907
 
2841
- Pool.prototype.destroy = function() {
2908
+ Pool.prototype.destroy = function(callback) {
2842
2909
  var self = this;
2910
+
2911
+ var connectionCount = this.internaldb.length;
2912
+
2913
+ if (connectionCount === 0 && callback) {
2914
+ callback();
2915
+ }
2916
+
2917
+ function detachCallback(err) {
2918
+ if (err) {
2919
+ if (callback) {
2920
+ callback(err);
2921
+ }
2922
+ return;
2923
+ }
2924
+
2925
+ connectionCount--;
2926
+ if (connectionCount === 0 && callback) {
2927
+ callback();
2928
+ }
2929
+ }
2930
+
2843
2931
  this.internaldb.forEach(function(db) {
2844
- if (db.connection._pooled === false)
2932
+ if (db.connection._pooled === false) {
2933
+ detachCallback();
2845
2934
  return;
2935
+ }
2846
2936
  // check if the db is not free into the pool otherwise user should manual detach it
2847
2937
  var _db_in_pool = self.pooldb.indexOf(db);
2848
2938
  if (_db_in_pool !== -1) {
2849
2939
  self.pooldb.splice(_db_in_pool, 1);
2850
2940
  db.connection._pooled = false;
2851
- db.detach();
2941
+ db.detach(detachCallback);
2852
2942
  }
2853
2943
  });
2854
2944
  };
@@ -2871,6 +2961,7 @@ var Connection = exports.Connection = function (host, port, callback, options, d
2871
2961
  this._detachAuto;
2872
2962
  this._socket = net.createConnection(port, host);
2873
2963
  this._pending = [];
2964
+ this._isOpened = false;
2874
2965
  this._isClosed = false;
2875
2966
  this._isDetach = false;
2876
2967
  this._isUsed = false;
@@ -2878,6 +2969,8 @@ var Connection = exports.Connection = function (host, port, callback, options, d
2878
2969
  this.options = options;
2879
2970
  this._bind_events(host, port, callback);
2880
2971
  this.error;
2972
+ this._retry_connection_id;
2973
+ this._retry_connection_interval = options.retryConnectionInterval || 1000;
2881
2974
  this._max_cached_query = options.maxCachedQuery || -1;
2882
2975
  this._cache_query = options.cacheQuery?{}:null;
2883
2976
  this._messageFile = options.messageFile || path.join(__dirname, 'firebird.msg');
@@ -2903,10 +2996,11 @@ exports.Connection.prototype._bind_events = function(host, port, callback) {
2903
2996
 
2904
2997
  self._socket.on('close', function() {
2905
2998
 
2906
- self._isClosed = true;
2907
-
2908
- if (self._isDetach)
2999
+ if (!self._isOpened || self._isDetach) {
2909
3000
  return;
3001
+ }
3002
+
3003
+ self._isOpened = false;
2910
3004
 
2911
3005
  if (!self.db) {
2912
3006
  if (callback)
@@ -2914,10 +3008,9 @@ exports.Connection.prototype._bind_events = function(host, port, callback) {
2914
3008
  return;
2915
3009
  }
2916
3010
 
2917
- setImmediate(function() {
3011
+ self._retry_connection_id = setTimeout(function() {
3012
+ self._socket.removeAllListeners();
2918
3013
  self._socket = null;
2919
- self._msg = null;
2920
- self._blr = null;
2921
3014
 
2922
3015
  var ctx = new Connection(host, port, function(err) {
2923
3016
  ctx.connect(self.options, function(err) {
@@ -2941,8 +3034,10 @@ exports.Connection.prototype._bind_events = function(host, port, callback) {
2941
3034
  }, self.db);
2942
3035
  });
2943
3036
 
3037
+ Object.assign(self, ctx);
3038
+
2944
3039
  }, self.options, self.db);
2945
- });
3040
+ }, self._retry_connection_interval);
2946
3041
 
2947
3042
  });
2948
3043
 
@@ -3165,14 +3260,7 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3165
3260
 
3166
3261
  data.fcolumn = 0;
3167
3262
  statement.connection.db.emit('row', data.frow, statement.nbrowsfetched, custom.asObject);
3168
-
3169
- if (!custom.asStream) {
3170
- data.frows.push(data.frow);
3171
- }
3172
-
3173
- if (custom.on) {
3174
- custom.on(data.frow, statement.nbrowsfetched);
3175
- }
3263
+ data.frows.push(data.frow);
3176
3264
  data.frow = custom.asObject ? {} : new Array(output.length);
3177
3265
 
3178
3266
  try {
@@ -3215,22 +3303,77 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3215
3303
  protocolArchitecture: data.readInt(),
3216
3304
  protocolMinimumType: data.readInt(),
3217
3305
  pluginName: '',
3218
- authData: ''
3306
+ authData: '',
3307
+ sessionKey: ''
3219
3308
  };
3220
3309
 
3221
3310
  accept.protocolMinimumType = accept.protocolMinimumType & 0xFF;
3311
+ //accept.compress = (accept.acceptType & pflag_compress) !== 0; // TODO Handle zlib compression
3222
3312
  if (accept.protocolVersion < 0) {
3223
3313
  accept.protocolVersion = (accept.protocolVersion & FB_PROTOCOL_MASK) | FB_PROTOCOL_FLAG;
3224
3314
  }
3225
3315
 
3226
3316
  if (r === op_cond_accept || r === op_accept_data) {
3227
- var d = data.readString(DEFAULT_ENCODING);
3317
+ var d = new BlrReader(data.readArray());
3228
3318
  accept.pluginName = data.readString(DEFAULT_ENCODING);
3229
3319
  var is_authenticated = data.readInt();
3230
3320
  var keys = data.readString(DEFAULT_ENCODING); // keys
3231
3321
 
3232
3322
  if (is_authenticated === 0) {
3233
- if (accept.pluginName === AUTH_PLUGIN_LEGACY) {
3323
+ if (cnx.options.pluginName && cnx.options.pluginName !== accept.pluginName) {
3324
+ doError(new Error('Server don\'t accept plugin : ' + cnx.options.pluginName + ', but support : ' + accept.pluginName), callback);
3325
+ }
3326
+
3327
+ if (AUTH_PLUGIN_SRP_LIST.indexOf(accept.pluginName) !== -1) {
3328
+ var crypto = {
3329
+ Srp: 'sha1',
3330
+ Srp256: 'sha256'
3331
+ };
3332
+ accept.srpAlgo = crypto[accept.pluginName];
3333
+
3334
+ // TODO : Fallback Srp256 to Srp ?
3335
+ /*if (!d.buffer) {
3336
+ cnx.sendOpContAuth(
3337
+ cnx.clientKeys.public.toString(16),
3338
+ DEFAULT_ENCODING,
3339
+ accept.pluginName
3340
+ );
3341
+
3342
+ return cb(new Error('login'));
3343
+ }*/
3344
+
3345
+ // Check buffer contains salt
3346
+ var saltLen = d.buffer.readUInt16LE(0);
3347
+ if (saltLen > 32 * 2) {
3348
+ console.log('salt to long'); // TODO : Throw error
3349
+ }
3350
+
3351
+ // Check buffer contains key
3352
+ var keyLen = d.buffer.readUInt16LE(saltLen + 2);
3353
+ var keyStart = saltLen + 4;
3354
+ if (d.buffer.length - keyStart !== keyLen) {
3355
+ console.log('key error'); // TODO : Throw error
3356
+ }
3357
+
3358
+ // Server keys
3359
+ cnx.serverKeys = {
3360
+ salt: d.buffer.slice(2, saltLen + 2).toString('utf8'),
3361
+ public: BigInt(d.buffer.slice(keyStart, d.buffer.length).toString('utf8'), 16)
3362
+ };
3363
+
3364
+ var proof = srp.clientProof(
3365
+ cnx.options.user.toUpperCase(),
3366
+ cnx.options.password,
3367
+ cnx.serverKeys.salt,
3368
+ cnx.clientKeys.public,
3369
+ cnx.serverKeys.public,
3370
+ cnx.clientKeys.private,
3371
+ accept.srpAlgo
3372
+ );
3373
+
3374
+ accept.authData = proof.authData.toString(16);
3375
+ accept.sessionKey = proof.clientSessionKey;
3376
+ } else if (accept.pluginName === AUTH_PLUGIN_LEGACY) {
3234
3377
  accept.authData = crypt.crypt(cnx.options.password, LEGACY_AUTH_SALT).substring(2);
3235
3378
  } else {
3236
3379
  return cb(new Error('Unknow auth plugin : ' + accept.pluginName));
@@ -3240,7 +3383,35 @@ function decodeResponse(data, callback, cnx, lowercase_keys, cb) {
3240
3383
  accept.sessionKey = '';
3241
3384
  }
3242
3385
  }
3386
+
3243
3387
  return cb(undefined, accept);
3388
+ case op_cont_auth:
3389
+ var d = new BlrReader(data.readArray());
3390
+ var pluginName = data.readString(DEFAULT_ENCODING);
3391
+ data.readString(DEFAULT_ENCODING); // plist
3392
+ data.readString(DEFAULT_ENCODING); // pkey
3393
+
3394
+ if (!cnx.options.pluginName) {
3395
+ if (cnx.accept.pluginName === pluginName) {
3396
+ // Erreur plugin not able to connect
3397
+ return cb(new Error("Unable to connect with plugin " + cnx.accept.pluginName));
3398
+ }
3399
+
3400
+ if (pluginName === AUTH_PLUGIN_LEGACY) { // Fallback to LegacyAuth
3401
+ cnx.accept.pluginName = pluginName;
3402
+ cnx.accept.authData = crypt.crypt(cnx.options.password, LEGACY_AUTH_SALT).substring(2);
3403
+
3404
+ cnx.sendOpContAuth(
3405
+ cnx.accept.authData,
3406
+ DEFAULT_ENCODING,
3407
+ pluginName
3408
+ );
3409
+
3410
+ return {error: new Error('login')};
3411
+ }
3412
+ }
3413
+
3414
+ return data.accept;
3244
3415
  default:
3245
3416
  return cb(new Error('Unexpected:' + r));
3246
3417
  }
@@ -3326,6 +3497,20 @@ function parseOpResponse(data, response, cb) {
3326
3497
  }
3327
3498
  }
3328
3499
 
3500
+ Connection.prototype.sendOpContAuth = function(authData, authDataEnc, pluginName) {
3501
+ var msg = this._msg;
3502
+ msg.pos = 0;
3503
+
3504
+ msg.addInt(op_cont_auth);
3505
+ msg.addString(authData, authDataEnc);
3506
+ msg.addString(pluginName, DEFAULT_ENCODING)
3507
+ msg.addString(AUTH_PLUGIN_LIST.join(','), DEFAULT_ENCODING);
3508
+ // msg.addInt(0); // p_list
3509
+ msg.addInt(0); // keys
3510
+
3511
+ this._socket.write(msg.getData());
3512
+ }
3513
+
3329
3514
  Connection.prototype._queueEvent = function(callback){
3330
3515
  var self = this;
3331
3516
 
@@ -3340,7 +3525,7 @@ Connection.prototype._queueEvent = function(callback){
3340
3525
  };
3341
3526
 
3342
3527
  Connection.prototype.connect = function (options, callback) {
3343
- var pluginName = AUTH_PLUGIN_LEGACY;
3528
+ var pluginName = options.manager ? AUTH_PLUGIN_LEGACY : options.pluginName || AUTH_PLUGIN_LIST[0]; // TODO Srp for service
3344
3529
  var msg = this._msg;
3345
3530
  var blr = this._blr;
3346
3531
 
@@ -3353,13 +3538,19 @@ Connection.prototype.connect = function (options, callback) {
3353
3538
  blr.addString(CNCT_plugin_name, pluginName, DEFAULT_ENCODING);
3354
3539
  blr.addString(CNCT_plugin_list, AUTH_PLUGIN_LIST.join(','), DEFAULT_ENCODING);
3355
3540
 
3356
- if (pluginName === AUTH_PLUGIN_LEGACY) {
3357
- var specificData = crypt.crypt(options.password, LEGACY_AUTH_SALT).substring(2);
3541
+ var specificData = '';
3542
+ if (AUTH_PLUGIN_SRP_LIST.indexOf(pluginName) > -1) {
3543
+ this.clientKeys = srp.clientSeed();
3544
+ specificData = this.clientKeys.public.toString(16);
3545
+ blr.addMultiblockPart(CNCT_specific_data, specificData, DEFAULT_ENCODING);
3546
+ } else if (pluginName === AUTH_PLUGIN_LEGACY) {
3547
+ specificData = crypt.crypt(options.password, LEGACY_AUTH_SALT).substring(2);
3358
3548
  blr.addMultiblockPart(CNCT_specific_data, specificData, DEFAULT_ENCODING);
3359
3549
  } else {
3360
- throw new Error('Invalide auth plugin');
3550
+ doError(new Error('Invalide auth plugin \'' + pluginName + '\''), callback);
3551
+ return;
3361
3552
  }
3362
- blr.addBytes([CNCT_client_crypt, 4, 0, 0, 0, 0]); // WireCrypt = Disabled
3553
+ blr.addBytes([CNCT_client_crypt, 4, WIRE_CRYPT_DISABLE, 0, 0, 0]); // WireCrypt = Disabled
3363
3554
  blr.addString(CNCT_user, os.userInfo().username || 'Unknown', DEFAULT_ENCODING);
3364
3555
  blr.addString(CNCT_host, os.hostname(), DEFAULT_ENCODING);
3365
3556
  blr.addBytes([CNCT_user_verification, 0]);
@@ -3416,19 +3607,25 @@ Connection.prototype.attach = function (options, callback, db) {
3416
3607
  blr.addByte(isc_dpb_version1);
3417
3608
  blr.addString(isc_dpb_lc_ctype, 'UTF8', DEFAULT_ENCODING);
3418
3609
  blr.addString(isc_dpb_user_name, user, DEFAULT_ENCODING);
3419
- if (this.accept.protocolVersion < PROTOCOL_VERSION13) {
3420
- if (this.accept.protocolVersion === PROTOCOL_VERSION10) {
3421
- blr.addString(isc_dpb_password, password, DEFAULT_ENCODING);
3422
- } else {
3423
- blr.addString(isc_dpb_password_enc, crypt.crypt(password, LEGACY_AUTH_SALT).substring(2), DEFAULT_ENCODING);
3610
+ if (options.password && !this.accept.authData) {
3611
+ if (this.accept.protocolVersion < PROTOCOL_VERSION13) {
3612
+ if (this.accept.protocolVersion === PROTOCOL_VERSION10) {
3613
+ blr.addString(isc_dpb_password, password, DEFAULT_ENCODING);
3614
+ } else {
3615
+ blr.addString(isc_dpb_password_enc, crypt.crypt(password, LEGACY_AUTH_SALT).substring(2), DEFAULT_ENCODING);
3616
+ }
3424
3617
  }
3425
3618
  }
3619
+
3426
3620
  if (role)
3427
3621
  blr.addString(isc_dpb_sql_role_name, role, DEFAULT_ENCODING);
3428
3622
 
3429
3623
  blr.addBytes([isc_dpb_process_id, 4]);
3430
3624
  blr.addInt32(process.pid);
3431
- blr.addString(isc_dpb_process_name, process.title, DEFAULT_ENCODING);
3625
+
3626
+ let processName = process.title || "";
3627
+ blr.addString(isc_dpb_process_name, processName.length > 255 ? processName.substring(processName.length - 255, processName.length) : processName, DEFAULT_ENCODING);
3628
+
3432
3629
  if (this.accept.authData) {
3433
3630
  blr.addString(isc_dpb_specific_auth_data, this.accept.authData, DEFAULT_ENCODING);
3434
3631
  }
@@ -3479,6 +3676,7 @@ Connection.prototype.detach = function (callback) {
3479
3676
  msg.addInt(0); // Database Object ID
3480
3677
 
3481
3678
  self._queueEvent(function(err, ret) {
3679
+ clearTimeout(self._retry_connection_id);
3482
3680
  delete(self.dbhandle);
3483
3681
  if (callback)
3484
3682
  callback(err, ret);
@@ -3515,7 +3713,10 @@ Connection.prototype.createDatabase = function (options, callback) {
3515
3713
 
3516
3714
  blr.addBytes([isc_dpb_process_id, 4]);
3517
3715
  blr.addInt32(process.pid);
3518
- blr.addString(isc_dpb_process_name, process.title, DEFAULT_ENCODING);
3716
+
3717
+ let processName = process.title || "";
3718
+ blr.addString(isc_dpb_process_name, processName.length > 255 ? processName.substring(processName.length - 255, processName.length) : processName, DEFAULT_ENCODING);
3719
+
3519
3720
  if (this.accept.authData) {
3520
3721
  blr.addString(isc_dpb_specific_auth_data, this.accept.authData, DEFAULT_ENCODING);
3521
3722
  }
@@ -3598,11 +3799,6 @@ Connection.prototype.startTransaction = function(isolation, callback) {
3598
3799
  blr.pos = 0;
3599
3800
  msg.pos = 0;
3600
3801
 
3601
- if (isolation instanceof Function) {
3602
- callback = isolation;
3603
- isolation = null;
3604
- }
3605
-
3606
3802
  blr.addBytes(isolation || ISOLATION_REPEATABLE_READ);
3607
3803
  msg.addInt(op_transaction);
3608
3804
  msg.addInt(this.dbhandle);
@@ -4313,43 +4509,40 @@ Connection.prototype.fetch = function(statement, transaction, count, callback) {
4313
4509
  msg.addInt(0); // message number
4314
4510
  msg.addInt(count || DEFAULT_FETCHSIZE); // fetch count
4315
4511
 
4316
- if (!transaction) {
4317
- callback.statement = statement;
4318
- this._queueEvent(callback);
4319
- return;
4320
- }
4321
-
4322
4512
  callback.statement = statement;
4323
4513
  this._queueEvent(callback);
4324
4514
  };
4325
4515
 
4326
- Connection.prototype.fetchAll = function(statement, transaction, callback) {
4327
-
4328
- var self = this;
4329
- var data;
4330
- var loop = function(err, ret) {
4331
-
4332
- if (err) {
4333
- callback(err);
4334
- return;
4335
- }
4336
-
4337
- if (!data) {
4338
- data = ret.data;
4339
- } else {
4340
- for (var i = 0, length = ret.data.length; i < length; i++)
4341
- data.push(ret.data[i]);
4342
- }
4343
-
4344
- if (ret.fetched)
4345
- callback(undefined, data);
4346
- else
4347
- self.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop);
4348
- }
4516
+ Connection.prototype.fetchAll = function (statement, transaction, callback) {
4517
+ var self = this, data = [];
4518
+ var loop = function (err, ret) {
4519
+ if (err) {
4520
+ return callback(err);
4521
+ } else if (ret && ret.data && ret.data.length) {
4522
+ for (var i = 0; i < ret.data.length; i++) {
4523
+ var pos = data.push(ret.data[i]);
4524
+ if (statement.custom && statement.custom.asStream && statement.custom.on) {
4525
+ statement.custom.on(ret.data[i], pos - 1);
4526
+ }
4527
+ if (i === ret.data.length - 1) {
4528
+ if (ret.fetched) {
4529
+ return callback(undefined, data);
4530
+ } else {
4531
+ self.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop);
4532
+ }
4533
+ }
4534
+ }
4535
+ } else if (ret.fetched) {
4536
+ callback(undefined, data);
4537
+ } else {
4538
+ self.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop);
4539
+ }
4540
+ }
4349
4541
 
4350
- this.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop);
4542
+ this.fetch(statement, transaction, DEFAULT_FETCHSIZE, loop);
4351
4543
  };
4352
4544
 
4545
+
4353
4546
  Connection.prototype.openBlob = function(blob, transaction, callback) {
4354
4547
  var msg = this._msg;
4355
4548
  msg.pos = 0;