bin/pastebin.js
'use strict';
/*
* pastebin-js
* https://github.com/j3lte/pastebin-js
*
* Copyright (c) 2013-2019 Jelte Lagendijk
* Licensed under the MIT license.
*/
var _ = require('underscore');
var fs = require('fs');
var xml2js = require('xml2js');
var Q = require('q');
var method = require('../lib/methods');
var conf = require('../lib/config');
/**
* Pastebin constructor
*/
function Pastebin(config) {
if (typeof config === 'string') {
config = { api_dev_key : config };
}
this.config = _.extend(conf.defaults, config);
}
/**
* Get a paste
* @param {String} id Id of the paste
* @param {Boolean} isPrivate Is this a private paste? Optional
* @return {Object} Promise
*/
Pastebin.prototype.getPaste = function (id, isPrivate) {
if (!id) {
var deferred = Q.defer();
deferred.reject(new Error('No paste id!'));
return deferred.promise;
}
if (isPrivate) {
var p = {
api_option : 'show_paste',
api_dev_key : this.config.api_dev_key,
api_paste_key : id,
};
var deferred = Q.defer();
if (this.config.api_user_key) {
p.api_user_key = this.config.api_user_key;
} else if (this.config.api_user_name !== null && this.config.api_user_password !== null) {
this
.createAPIuserKey()
.then(function () {
this.getPaste(id, isPrivate)
.then(deferred.resolve)
.catch(deferred.reject);
return deferred.promise;
}.bind(this))
.catch(deferred.reject);
return deferred.promise;
} else {
deferred.reject(new Error('Error! For private pastes you need to be logged in! Provide username and password!'));
return deferred.promise;
}
return this._postApi(conf.net.protocol + conf.net.base + conf.net.endpoint.apiraw, p);
}
return this._getApi(conf.net.protocol + conf.net.base + conf.net.endpoint.raw + id, null);
};
/**
* Create a paste
* @param {String} text Text to be pasted
* @param {String} title Title of the paste (optional)
* @param {String} format Format of the paste, for syntax highlighting. See /lib/config.js (optional)
* @param {Number} privacy Privacylevel of the paste (0 = public, 1 = unlisted and 2 = private) (optional, default = 0)
* @param {String} expiration Expiration time of the paste See /lib/config.js
* @return {Object} Promise
*/
Pastebin.prototype.createPaste = function (text, title, format, privacy, expiration) {
if (_.isObject(text) && typeof title === 'undefined') {
// assume the first parameter is an object with the information
expiration = text.expiration;
privacy = text.privacy;
format = text.format;
title = text.title;
text = text.text;
}
var deferred = Q.defer();
var p = {
api_option : 'paste',
api_dev_key : this.config.api_dev_key,
api_paste_code : text,
};
var noKeyNeeded = true;
if (typeof text !== 'string') {
deferred.reject(new Error('Error! Paste can only be a text!'));
return deferred.promise;
}
if (typeof title !== 'undefined' && title !== null) {
p.api_paste_name = title;
}
if (typeof format !== 'undefined' && format !== null) {
if (conf.formats[format]) {
p.api_paste_format = format;
} else {
deferred.reject(new Error('Error! Paste format ' + format + ' is unknown.'));
return deferred.promise;
}
}
if (typeof privacy !== 'undefined' && privacy !== null) {
p.api_paste_private = privacy;
if (privacy === 0 || privacy === 1) {
p.api_paste_private = privacy;
} else if (privacy === 2 || privacy === 3) {
if (this.config.api_user_key) {
p.api_user_key = this.config.api_user_key;
} else if (this.config.api_user_name !== null && this.config.api_user_password !== null) {
noKeyNeeded = false;
this.createAPIuserKey()
.then(function () {
this.createPaste(text, title, format, privacy, expiration)
.then(deferred.resolve)
.catch(deferred.reject);
return deferred.promise;
}.bind(this))
.catch(deferred.reject);
} else {
deferred.reject(new Error('Error! For this privacy level you need to be logged in! Provide username and password!'));
return deferred.promise;
}
} else {
deferred.reject(new Error('Error! Privacy level is unknown!'));
return deferred.promise;
}
}
if (typeof expiration !== 'undefined' && expiration !== null) {
if (!conf.expiration[expiration]) {
deferred.reject(new Error('Error! Paste expiration ' + expiration + ' is unknown.'));
return deferred.promise;
}
p.api_paste_expire_date = expiration;
}
if (noKeyNeeded) {
// Paste privacy 3 = Public, under user
if (p.api_paste_private === 3) {
p.api_paste_private = 0;
}
return this._postApi(conf.net.protocol + conf.net.base + conf.net.endpoint.post, p);
}
return deferred.promise;
};
/**
* Create a paste from a file
* @param {String} filename Location of the filename
* @param {String} title Title of the paste (optional)
* @param {String} format Format of the paste, for syntax highlighting. See /lib/config.js (optional)
* @param {Number} privacy Privacylevel of the paste (0 = public, 1 = unlisted and 2 = private) (optional, default = 0)
* @param {String} expiration Expiration time of the paste See /lib/config.js
* @return {Object} Promise
*/
Pastebin.prototype.createPasteFromFile = function (filename, title, format, privacy, expiration) {
if (_.isObject(filename) && typeof title === 'undefined') {
// assume the first parameter is an object with the information
expiration = filename.expiration;
privacy = filename.privacy;
format = filename.format;
title = filename.title;
filename = filename.filename;
}
var deferred = Q.defer();
var self = this;
if ('undefined' === typeof filename || !filename) {
deferred.reject(new Error('Filename not provided'));
} else {
fs.readFile(filename, 'UTF8', function (err, data) {
if (err) {
deferred.reject(new Error('Readfile error: ' + err));
}
self.createPaste(data, title, format, privacy, expiration)
.then(deferred.resolve)
.catch(deferred.reject);
});
}
return deferred.promise;
};
/**
* Delete a paste that is created by the user
* @param {String} pasteID The id of the userpaste (http://pastebin.com/[id])
* @return {Object} Promise
*/
Pastebin.prototype.deletePaste = function (pasteID) {
var deferred = Q.defer();
if (!pasteID) {
deferred.reject(new Error('Please provide a paste ID to delete'));
return deferred.promise;
}
var params = {
api_option : 'delete',
api_dev_key : this.config.api_dev_key,
api_paste_key : pasteID
};
if (this.config.api_user_key) {
params.api_user_key = this.config.api_user_key;
this._postApi(conf.net.protocol + conf.net.base + conf.net.endpoint.post, params)
.then(deferred.resolve)
.catch(deferred.reject);
} else if (this.config.api_user_name !== null && this.config.api_user_password !== null) {
this.createAPIuserKey()
.then(function () {
this.deletePaste(pasteID)
.then(deferred.resolve)
.catch(deferred.reject);
}.bind(this));
} else {
deferred.reject(new Error('Error! Deleting a paste created by the user needs username and password'));
}
return deferred.promise;
};
Pastebin.prototype._postAndParse = function (params, parseFunc, deferred) {
this._postApi(conf.net.protocol + conf.net.base + conf.net.endpoint.post, params)
.then(function (data) {
parseFunc(data)
.then(deferred.resolve)
.catch(deferred.reject);
}.bind(this))
.catch(deferred.reject);
};
/**
* Create userkey. Saved in config.api_user_key
* @return {Object} Promise
*/
Pastebin.prototype.createAPIuserKey = function () {
var deferred = Q.defer();
this._getRequired(['api_dev_key', 'api_user_name', 'api_user_password'])
.then(function (params) {
this._postApi(conf.net.protocol + conf.net.base + conf.net.endpoint.login, params)
.then(function (data) {
if (data.length !== 32) {
deferred.reject(new Error('Error in createAPIuserKey! ' + data));
} else {
this.config.api_user_key = data;
deferred.resolve(true);
}
}.bind(this))
.catch(deferred.reject);
}.bind(this))
.catch(deferred.reject);
return deferred.promise;
};
/**
* List the pastes that are created by the user
* @param {Number} limit Set the limit of pastes
* @return {Object} Promise
*/
Pastebin.prototype.listUserPastes = function (limit) {
var deferred = Q.defer();
var l = limit || 50;
if (!this.config.api_user_key) {
this.createAPIuserKey()
.then(function () {
this.listUserPastes(limit)
.then(deferred.resolve)
.catch(deferred.reject);
}.bind(this))
.catch(deferred.reject);
} else {
var params = {
api_dev_key : this.config.api_dev_key,
api_user_key : this.config.api_user_key,
api_results_limit : l,
api_option : 'list'
};
this._postAndParse(params, this._parsePastes.bind(this), deferred);
}
return deferred.promise;
};
/**
* Lists the trending pastes
* @return {Object} Promise
*/
Pastebin.prototype.listTrendingPastes = function () {
var deferred = Q.defer();
var params = {
api_option : 'trends',
api_dev_key : this.config.api_dev_key
};
this._postAndParse(params, this._parsePastes.bind(this), deferred);
return deferred.promise;
};
/**
* Gets the info of the user
* @return {Object} Promise
*/
Pastebin.prototype.getUserInfo = function () {
var deferred = Q.defer();
var params = {
api_option : 'userdetails',
api_dev_key : this.config.api_dev_key
};
if (!this.config.api_user_key) {
this.createAPIuserKey()
.then(function () {
this.getUserInfo()
.then(deferred.resolve)
.catch(deferred.reject);
}.bind(this))
.catch(deferred.reject);
} else {
params.api_user_key = this.config.api_user_key;
this._postAndParse(params, this._parseUser.bind(this), deferred);
}
return deferred.promise;
};
/**
* Parse an XML file containing pastes
* @param {String} xml XML
* @return {Object} Promise
*/
Pastebin.prototype._parsePastes = function (xml) {
var deferred = Q.defer();
this._parseXML(xml)
.then(function (data) {
if (data) {
var rootObj = data.paste;
var normalize = _.map(rootObj, function (paste) {
var obj = {};
_.map(_.keys(paste), function (key) {
obj[key] = paste[key][0];
});
return obj;
});
deferred.resolve(normalize);
} else {
deferred.reject(new Error('No data returned to _parsePastes!'));
}
})
.catch(deferred.reject);
return deferred.promise;
};
/**
* Parse an XML file containing userdata
* @param {String} xml XML
* @return {Object} Promise
*/
Pastebin.prototype._parseUser = function (xml) {
var deferred = Q.defer();
this._parseXML(xml)
.then(function (data) {
if (data) {
var rootObj = data.user[0];
var normalize = {};
_.each(Object.keys(rootObj), function (key) { normalize[key] = rootObj[key][0]; });
deferred.resolve(normalize);
} else {
deferred.reject(new Error('No data returned to _parseUser!'));
}
})
.catch(deferred.reject);
return deferred.promise;
};
/**
* Parse an XML file
* @param {String} xml XML
* @return {Object} Promise
*/
Pastebin.prototype._parseXML = function (xml) {
var deferred = Q.defer();
var xmlString = '';
var parser = new xml2js.Parser({
'trim' : true,
'explicitRoot' : false
});
if (!xml) {
deferred.reject(new Error('No xml provided!'));
return deferred.promise;
}
xmlString = '<root>\n' + xml + '</root>\n';
parser.parseString(xmlString, function (err, data) {
if (err) {
deferred.reject(new Error('Error in parsing XML: ' + err));
} else {
deferred.resolve(data);
}
});
return deferred.promise;
};
/**
* Returns a list with the required parameters from config
* @param {Array} paramlist List of parameters
*/
Pastebin.prototype._getRequired = function (paramlist) {
var deferred = Q.defer();
var ret = _.filter(paramlist, function(param) {
return this.config[param] !== null;
}.bind(this));
if (Object.keys(ret).length !== paramlist.length) {
deferred.reject(new Error('Missing parameters! ' + _.difference(paramlist, ret)));
} else {
ret = _.pick(this.config, paramlist);
deferred.resolve(ret);
}
return deferred.promise;
};
/**
* Higher lever method for get requests
* @param {String} path Path
* @param {Object} params Parameters
*/
Pastebin.prototype._getApi = function (path, params) {
return method.get(path, params);
};
/**
* Higher level method for post requests
* @param {String} path Path
* @param {Object} params Parameters
*/
Pastebin.prototype._postApi = function (path, params) {
return method.post(path, params);
};
/***************************************************
* Synchronous methods
***************************************************/
function runWithCallback(promiseFunc, callback) {
if (!_.isFunction(callback)) { throw new Error('This function requires a callback!'); }
promiseFunc.then(function (data) {
callback(null, data);
}).catch(function (err) {
callback(err, null);
});
}
Pastebin.prototype.getPasteSync = function (id, callback) {
runWithCallback(this.getPaste(id), callback);
};
Pastebin.prototype.createPasteSync = function (text, title, format, privacy, expiration, callback) {
if (_.isObject(text) && _.isFunction(title)) {
callback = title;
runWithCallback(this.createPaste(text), callback);
} else {
runWithCallback(this.createPaste(text, title, format, privacy, expiration), callback);
}
};
Pastebin.prototype.createPasteFromFileSync = function (filename, title, format, privacy, expiration, callback) {
if (_.isObject(filename) && _.isFunction(title)) {
callback = title;
runWithCallback(this.createPasteFromFile(filename), callback);
} else {
runWithCallback(this.createPasteFromFile(filename, title, format, privacy, expiration), callback);
}
};
Pastebin.prototype.deletePasteSync = function (pasteID, callback) {
runWithCallback(this.deletePaste(pasteID), callback);
};
Pastebin.prototype.listUserPastesSync = function (limit, callback) {
runWithCallback(this.listUserPastes(limit), callback);
};
Pastebin.prototype.listTrendingPastesSync = function (callback) {
runWithCallback(this.listTrendingPastes(), callback);
};
Pastebin.prototype.getUserInfoSync = function (callback) {
runWithCallback(this.getUserInfo(), callback);
};
module.exports = Pastebin;