Commit 2c052c9f authored by Benjamin Chelli's avatar Benjamin Chelli

Add connection autoclose + socket error handling + API documentation

parent e22a8aa1
...@@ -10,36 +10,102 @@ The development is still at an experimental stage and should not be yet consider ...@@ -10,36 +10,102 @@ The development is still at an experimental stage and should not be yet consider
npm install smb2 npm install smb2
## Usage ## API
### var smb2Client = new SMB2 ( options )
The SMB2 class is the constructor of your SMB2 client.
the parameter ```options``` accepts this list of attributes:
- ```share``` (mandatory): the share you want to access
- ```domain``` (mandatory): the domain of which the user is registred
- ```username``` (mandatory): the username of the user that access the share
- ```password``` (mandatory): the password
- ```packetConcurrency``` (optional): default ```20```, the number of simulatanous packet when writting / reading data from the share
- ```autoCloseTimeout``` (optional): default ```10000```, the timeout in milliseconds before to close the SMB2 session and the socket, if setted to ```0``` the connection will never be closed unless you do it
Example:
```javascript ```javascript
// load the library // load the library
var SMB2 = require('smb2'); var SMB2 = require('smb2');
// create an SMB2 instance // create an SMB2 instance
var s = new SMB2({ var smb2Client = new SMB2({
share:'\\\\000.000.000.000\\c$' share:'\\\\000.000.000.000\\c$'
, domain:'DOMAIN' , domain:'DOMAIN'
, username:'username' , username:'username'
, password:'password!' , password:'password!'
}); });
```
// read the content of the folder ### smb2Client.readdir ( path, callback )
s.readDir('Windows\\System32', function(err, files){ Asynchronous readdir(3). Reads the contents of a directory. The callback gets two arguments (err, files) where files is an array of the names of the files in the directory excluding '.' and '..'.
// if an error happen => throw it Example:
```javascript
smb2Client.readdir('Windows\\System32', function(err, files){
if(err) throw err; if(err) throw err;
// display content in the console
console.log(files); console.log(files);
// close the connection
s.close();
}); });
```
### smb2Client.readFile ( filename, [options], callback )
- ```filename``` String
- ```options``` Object
- ```encoding``` String | Null default = null
- ```callback``` Function
Asynchronously reads the entire contents of a file. Example:
```javascript
smb2Client.readFile('path\\to\\my\\file.txt', function(err, data){
if(err) throw err;
console.log(data);
});
```
The callback is passed two arguments (err, data), where data is the contents of the file.
If no encoding is specified, then the raw buffer is returned.
### smb2Client.writeFile ( filename, data, [options], callback )
- ```filename``` String
- ```data``` String | Buffer
- ```options``` Object
- ```encoding``` String | Null default = 'utf8'
- ```callback``` Function
Asynchronously writes data to a file, replacing the file if it already exists. data can be a string or a buffer.
The encoding option is ignored if data is a buffer. It defaults to 'utf8'.
Example:
```javascript
smb2Client.writeFile('path\\to\\my\\file.txt', 'Hello Node', function (err) {
if (err) throw err;
console.log('It\'s saved!');
});
```
### smb2Client.exists ( path, callback )
Test whether or not the given path exists by checking with the file system. Then call the callback argument with either true or false. Example:
```javascript
smb2Client.exists('path\\to\\my\\file.txt', function (err, exists) {
if (err) throw err;
console.log(exists ? "it's there" : "it's not there!");
});
```
### smb2Client.unlinkFile ( path, callback )
Asynchronous unlink(2). No arguments other than a possible exception are given to the completion callback.
```javascript
smb2Client.unlink('path\\to\\my\\file.txt', function (err) {
if (err) throw err;
console.log("file has been deleted");
});
``` ```
### smb2Client.close ( )
This function will close the open connection if opened, it will be called automatically after ```autoCloseTimeout``` ms of no SMB2 call on the server.
## References ## References
The[MS-SMB2]: Server Message Block (SMB) Protocol Versions 2 and 3 The[MS-SMB2]: Server Message Block (SMB) Protocol Versions 2 and 3
......
...@@ -41,9 +41,12 @@ var SMB = module.exports = function(opt){ ...@@ -41,9 +41,12 @@ var SMB = module.exports = function(opt){
// save the full path // save the full path
this.fullPath = opt.share; this.fullPath = opt.share;
// packet concurrency // packet concurrency default 20
this.packetConcurrency = opt.packetConcurrency || 20; this.packetConcurrency = opt.packetConcurrency || 20;
// close timeout default 60s
this.autoCloseTimeout = opt.autoCloseTimeout || 10000;
// store authentification // store authentification
this.domain = opt.domain; this.domain = opt.domain;
this.username = opt.username; this.username = opt.username;
...@@ -53,6 +56,7 @@ var SMB = module.exports = function(opt){ ...@@ -53,6 +56,7 @@ var SMB = module.exports = function(opt){
this.SessionId = 0; this.SessionId = 0;
// create a socket // create a socket
this.connected = false;
this.socket = new net.Socket({ this.socket = new net.Socket({
allowHalfOpen:true allowHalfOpen:true
}); });
...@@ -60,7 +64,11 @@ var SMB = module.exports = function(opt){ ...@@ -60,7 +64,11 @@ var SMB = module.exports = function(opt){
// attach data events to socket // attach data events to socket
this.socket.on('data', parseResponse(this)); this.socket.on('data', parseResponse(this));
var connection = this; var connection = this;
this.socket.on('error', function(){ connection.errorHandler = [];
this.socket.on('error', function(err){
if(connection.errorHandler.length > 0){
connection.errorHandler[0].call(null, err)
}
if(connection.debug){ if(connection.debug){
console.log('-- error'); console.log('-- error');
console.log(arguments); console.log(arguments);
...@@ -86,7 +94,11 @@ var proto = SMB.prototype = {}; ...@@ -86,7 +94,11 @@ var proto = SMB.prototype = {};
* *
*/ */
proto.close = function(){ proto.close = function(){
this.socket.end(); clearAutoCloseTimeout(this);
if(this.connected){
this.connected = false;
this.socket.end();
}
} }
...@@ -107,6 +119,7 @@ proto.readdir = function(path, cb){ ...@@ -107,6 +119,7 @@ proto.readdir = function(path, cb){
var connection = this; var connection = this;
connect(connection, function(err){ connect(connection, function(err){
cb = scheduleAutoClose(connection, cb);
if(err) cb(err); if(err) cb(err);
// SMB2 open directory // SMB2 open directory
else SMB2Request('open', {path:path}, connection, function(err, file){ else SMB2Request('open', {path:path}, connection, function(err, file){
...@@ -152,6 +165,7 @@ proto.readFile = function(filename, options, cb){ ...@@ -152,6 +165,7 @@ proto.readFile = function(filename, options, cb){
} }
connect(connection, function(err){ connect(connection, function(err){
cb = scheduleAutoClose(connection, cb);
if(err) cb(err); if(err) cb(err);
// SMB2 open file // SMB2 open file
else SMB2Request('open', {path:filename}, connection, function(err, file){ else SMB2Request('open', {path:filename}, connection, function(err, file){
...@@ -189,7 +203,10 @@ proto.readFile = function(filename, options, cb){ ...@@ -189,7 +203,10 @@ proto.readFile = function(filename, options, cb){
createPackets(); createPackets();
if(nbRemainingPackets==0 && offset.ge(fileLength)) { if(nbRemainingPackets==0 && offset.ge(fileLength)) {
SMB2Request('close', file, connection, function(err){ SMB2Request('close', file, connection, function(err){
cb && cb(err, result.toString()); if(options.encoding){
result = result.toString(options.encoding);
}
cb && cb(err, result);
}) })
} }
} }
...@@ -238,9 +255,11 @@ proto.writeFile = function(filename, data, options, cb){ ...@@ -238,9 +255,11 @@ proto.writeFile = function(filename, data, options, cb){
options = {}; options = {};
} }
options.encoding = options.encoding || 'utf8';
var connection = this var connection = this
, file , file
, fileContent = new Buffer(data) , fileContent = Buffer.isBuffer(data) ? data : new Buffer(data, options.encoding)
, fileLength = new bigint(8, fileContent.length) , fileLength = new bigint(8, fileContent.length)
; ;
...@@ -320,27 +339,14 @@ proto.writeFile = function(filename, data, options, cb){ ...@@ -320,27 +339,14 @@ proto.writeFile = function(filename, data, options, cb){
connect(connection, function(err){ connect(connection, function(err){
cb = scheduleAutoClose(connection, cb);
if(err) cb(err); if(err) cb(err);
else connection.exists(filename, function(err, exists){ else createFile(function(){
setFileSize(function(){
if(err) cb && cb(err); writeFile(function(){
closeFile(cb);
else if(!exists){
createFile(function(){
setFileSize(function(){
writeFile(function(){
closeFile(cb);
});
});
}); });
});
} else {
cb(new Error('File already exists'));
}
}); });
}); });
...@@ -363,6 +369,7 @@ proto.exists = function(path, cb){ ...@@ -363,6 +369,7 @@ proto.exists = function(path, cb){
var connection = this; var connection = this;
connect(connection, function(err){ connect(connection, function(err){
cb = scheduleAutoClose(connection, cb);
if(err) cb(err); if(err) cb(err);
else SMB2Request('open', {path:path}, connection, function(err, file){ else SMB2Request('open', {path:path}, connection, function(err, file){
if(err) cb && cb(null, false); if(err) cb && cb(null, false);
...@@ -376,8 +383,8 @@ proto.exists = function(path, cb){ ...@@ -376,8 +383,8 @@ proto.exists = function(path, cb){
/* /*
* unlinkFile * unlink
* ========== * ======
* *
* remove file: * remove file:
* *
...@@ -388,10 +395,11 @@ proto.exists = function(path, cb){ ...@@ -388,10 +395,11 @@ proto.exists = function(path, cb){
* - close the file * - close the file
* *
*/ */
proto.unlinkFile = function(path, cb){ proto.unlink = function(path, cb){
var connection = this; var connection = this;
connect(connection, function(err){ connect(connection, function(err){
cb = scheduleAutoClose(connection, cb);
if(err) cb(err); if(err) cb(err);
else connection.exists(path, function(err, exists){ else connection.exists(path, function(err, exists){
...@@ -427,13 +435,15 @@ proto.unlinkFile = function(path, cb){ ...@@ -427,13 +435,15 @@ proto.unlinkFile = function(path, cb){
/* /*
* PRIVATE FUNCTION TO HANDLE CONNECTION * PRIVATE FUNCTION TO HANDLE CONNECTION
*/ */
connect = function(connection, cb){ function connect(connection, cb){
if(connection.connected){ if(connection.connected){
cb && cb(null); cb && cb(null);
return; return;
} }
cb = scheduleAutoClose(connection, cb);
// open TCP socket // open TCP socket
connection.socket.connect(port, connection.ip); connection.socket.connect(port, connection.ip);
...@@ -460,6 +470,45 @@ connect = function(connection, cb){ ...@@ -460,6 +470,45 @@ connect = function(connection, cb){
} }
/*
* PRIVATE FUNCTION TO HANDLE CLOSING THE CONNECTION
*/
function clearAutoCloseTimeout(connection){
if(connection.scheduledAutoClose){
clearTimeout(connection.scheduledAutoClose);
connection.scheduledAutoClose = null;
}
}
function setAutoCloseTimeout(connection){
clearAutoCloseTimeout(connection);
if(connection.autoCloseTimeout != 0){
connection.scheduledAutoClose = setTimeout(function(){
connection.close();
}, connection.autoCloseTimeout);
}
}
function scheduleAutoClose(connection, cb){
addErrorListener(connection, cb);
clearAutoCloseTimeout(connection);
return function(){
removeErrorListener(connection);
setAutoCloseTimeout(connection);
cb.apply(null, arguments);
}
}
/*
* PRIVATE FUNCTIONS TO HANDLE ERRORS
*/
function addErrorListener(connection, callback){
connection.errorHandler.unshift(callback);
}
function removeErrorListener(connection){
connection.errorHandler.shift();
}
/* /*
* MESSAGE MANAGMENT * MESSAGE MANAGMENT
*/ */
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
, "description":"SMB2 Client" , "description":"SMB2 Client"
, "homepage": "https://github.com/bchelli/node-smb2" , "homepage": "https://github.com/bchelli/node-smb2"
, "version":"0.2.0" , "version":"0.2.1"
, "engines": [ , "engines": [
"node" "node"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment