diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f2b64e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# OS File +.DS_Store + +# Npm +node_modules diff --git a/DeserializePHP.tableplusplugin/deserializephp.js b/DeserializePHP.tableplusplugin/deserializephp.js new file mode 100755 index 0000000..6c099e8 --- /dev/null +++ b/DeserializePHP.tableplusplugin/deserializephp.js @@ -0,0 +1,8 @@ +!function(r){function e(t){if(n[t])return n[t].exports;var o=n[t]={i:t,l:!1,exports:{}};return r[t].call(o.exports,o,o.exports,e),o.l=!0,o.exports}var n={};e.m=r,e.c=n,e.d=function(r,n,t){e.o(r,n)||Object.defineProperty(r,n,{configurable:!1,enumerable:!0,get:t})},e.n=function(r){var n=r&&r.__esModule?function(){return r.default}:function(){return r};return e.d(n,"a",n),n},e.o=function(r,e){return Object.prototype.hasOwnProperty.call(r,e)},e.p="",e(e.s=1)}([function(r,e,n){"use strict";var t,o="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(r){return typeof r}:function(r){return r&&"function"==typeof Symbol&&r.constructor===Symbol&&r!==Symbol.prototype?"symbol":typeof r};t=function(){return this}();try{t=t||Function("return this")()||(0,eval)("this")}catch(r){"object"===("undefined"==typeof window?"undefined":o(window))&&(t=window)}r.exports=t},function(r,e,n){"use strict";(function(r){var e=n(2),t=function(r){var n=r.clickedRowValue();if(null==n)return void r.alert("Error","Data is null");var t=(0,e.unserialize)(n);if(null==t)return void r.alert("Error","Data is not serialized.");SystemService.insertToClipboard((0,e.print_r)(t)),SystemService.notify("Deserialize PHP","Your data has been deserialized!")};r.onRun=t}).call(e,n(0))},function(r,e,n){"use strict";(function(r){!function(r,e){function n(e){var n=function(r){var e=r.charCodeAt(0);return e<128?0:e<2048?1:2},t=function(e,n,t,o){throw new r[e](n,t,o)},o=function(r,e,n){for(var o=2,i=[],a=r.slice(e,e+1);a!=n;)o+e>r.length&&t("Error","Invalid"),i.push(a),a=r.slice(e+(o-1),e+o),o+=1;return[i.length,i.join("")]},i=function(r,e,t){var o,i,a;for(a=[],o=0;o + * @license http://www.opensource.org/licenses/mit-license.php + * @link http://gist.github.com/879208 + */ +var n=function r(e,n){var t=n||"",o="[object Array]"===Object.prototype.toString.call(e),i=o?"Array\n"+t+"[\n":"Object\n"+t+"{\n";for(var a in e)if(e.hasOwnProperty(a)){var c=e[a],u="",s=Object.prototype.toString.call(c);switch(s){case"[object Array]":case"[object Object]":u=r(c,t+"\t");break;case"[object String]":u="'"+c+"'";break;default:u=c}i+=t+"\t"+a+" => "+u+",\n"}return i=i.substring(0,i.length-2)+"\n"+t,o?i+"]":i+"}"};e.print_r=n}).call(e,n(0))}]); \ No newline at end of file diff --git a/DeserializePHP.tableplusplugin/library/helper.js b/DeserializePHP.tableplusplugin/library/helper.js new file mode 100755 index 0000000..37a8593 --- /dev/null +++ b/DeserializePHP.tableplusplugin/library/helper.js @@ -0,0 +1,272 @@ +// Wrapper for nodejs/browser compat +(function (window, exports) { + + // Public API + exports.unserialize = unserialize; + exports.unserializeSession = unserializeSession; + + /** + * Unserialize data taken from PHP's serialize() output + * + * Taken from https://github.com/kvz/phpjs/blob/master/functions/var/unserialize.js + * Fixed window reference to make it nodejs-compatible + * + * @param string serialized data + * @return unserialized data + * @throws + */ + function unserialize (data) { + // http://kevin.vanzonneveld.net + // + original by: Arpad Ray (mailto:arpad@php.net) + // + improved by: Pedro Tainha (http://www.pedrotainha.com) + // + bugfixed by: dptr1988 + // + revised by: d3x + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: Brett Zamir (http://brett-zamir.me) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: Chris + // + improved by: James + // + input by: Martin (http://www.erlenwiese.de/) + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: Le Torbi + // + input by: kilops + // + bugfixed by: Brett Zamir (http://brett-zamir.me) + // + input by: Jaroslaw Czarniak + // % note: We feel the main purpose of this function should be to ease the transport of data between php & js + // % note: Aiming for PHP-compatibility, we have to translate objects to arrays + // * example 1: unserialize('a:3:{i:0;s:5:"Kevin";i:1;s:3:"van";i:2;s:9:"Zonneveld";}'); + // * returns 1: ['Kevin', 'van', 'Zonneveld'] + // * example 2: unserialize('a:3:{s:9:"firstName";s:5:"Kevin";s:7:"midName";s:3:"van";s:7:"surName";s:9:"Zonneveld";}'); + // * returns 2: {firstName: 'Kevin', midName: 'van', surName: 'Zonneveld'} + var that = this, + utf8Overhead = function (chr) { + // http://phpjs.org/functions/unserialize:571#comment_95906 + var code = chr.charCodeAt(0); + if (code < 0x0080) { + return 0; + } + if (code < 0x0800) { + return 1; + } + return 2; + }, + error = function (type, msg, filename, line) { + throw new window[type](msg, filename, line); + }, + read_until = function (data, offset, stopchr) { + var i = 2, buf = [], chr = data.slice(offset, offset + 1); + + while (chr != stopchr) { + if ((i + offset) > data.length) { + error('Error', 'Invalid'); + } + buf.push(chr); + chr = data.slice(offset + (i - 1), offset + i); + i += 1; + } + return [buf.length, buf.join('')]; + }, + read_chrs = function (data, offset, length) { + var i, chr, buf; + + buf = []; + for (i = 0; i < length; i++) { + chr = data.slice(offset + (i - 1), offset + i); + buf.push(chr); + length -= utf8Overhead(chr); + } + return [buf.length, buf.join('')]; + }, + _unserialize = function (data, offset) { + var dtype, dataoffset, keyandchrs, keys, + readdata, readData, ccount, stringlength, + i, key, kprops, kchrs, vprops, vchrs, value, + chrs = 0, + typeconvert = function (x) { + return x; + }, + readArray = function () { + readdata = {}; + + keyandchrs = read_until(data, dataoffset, ':'); + chrs = keyandchrs[0]; + keys = keyandchrs[1]; + dataoffset += chrs + 2; + + for (i = 0; i < parseInt(keys, 10); i++) { + kprops = _unserialize(data, dataoffset); + kchrs = kprops[1]; + key = kprops[2]; + dataoffset += kchrs; + + vprops = _unserialize(data, dataoffset); + vchrs = vprops[1]; + value = vprops[2]; + dataoffset += vchrs; + + readdata[key] = value; + } + }; + + if (!offset) { + offset = 0; + } + dtype = (data.slice(offset, offset + 1)).toLowerCase(); + + dataoffset = offset + 2; + + switch (dtype) { + case 'i': + typeconvert = function (x) { + return parseInt(x, 10); + }; + readData = read_until(data, dataoffset, ';'); + chrs = readData[0]; + readdata = readData[1]; + dataoffset += chrs + 1; + break; + case 'b': + typeconvert = function (x) { + return parseInt(x, 10) !== 0; + }; + readData = read_until(data, dataoffset, ';'); + chrs = readData[0]; + readdata = readData[1]; + dataoffset += chrs + 1; + break; + case 'd': + typeconvert = function (x) { + return parseFloat(x); + }; + readData = read_until(data, dataoffset, ';'); + chrs = readData[0]; + readdata = readData[1]; + dataoffset += chrs + 1; + break; + case 'n': + readdata = null; + break; + case 's': + ccount = read_until(data, dataoffset, ':'); + chrs = ccount[0]; + stringlength = ccount[1]; + dataoffset += chrs + 2; + + readData = read_chrs(data, dataoffset + 1, parseInt(stringlength, 10)); + chrs = readData[0]; + readdata = readData[1]; + dataoffset += chrs + 2; + if (chrs != parseInt(stringlength, 10) && chrs != readdata.length) { + error('SyntaxError', 'String length mismatch'); + } + break; + case 'a': + readArray(); + dataoffset += 1; + break; + case 'o': + ccount = read_until(data, dataoffset, ':'); + dataoffset += ccount[0] + 2; + + ccount = read_until(data, dataoffset, '"'); + dataoffset += ccount[0] + 2; + + readArray(); + dataoffset += 1; + break; + default: + error('SyntaxError', 'Unknown / Unhandled data type(s): ' + dtype); + break; + } + return [dtype, dataoffset - offset, typeconvert(readdata)]; + } + ; + + return _unserialize((data + ''), 0)[2]; + } + + /** + * Parse PHP-serialized session data + * + * @param string serialized session + * @return unserialized data + * @throws + */ + function unserializeSession (input) { + return input.split(/\|/).reduce(function (output, part, index, parts) { + // First part = $key + if (index === 0) { + output._currKey = part; + } + // Last part = $someSerializedStuff + else if (index === parts.length - 1) { + output[output._currKey] = unserialize(part); + delete output._currKey; + } + // Other output = $someSerializedStuff$key + else { + var repper = part.replace(/(\n|\r)/g," "); + var match = repper.match(/^((?:.*?[;\}])+)([^;\}]+?)$/); + if (match) { + output[output._currKey] = unserialize(match[1]); + output._currKey = match[2]; + } else { + throw new Error('Parse error on part "' + part + '"'); + } + } + return output; + }, {}); + } + + // /Wrapper + })((typeof window === 'undefined') ? global : window, (typeof exports === 'undefined') ? (window.PHPUnserialize = {}) : exports); + +/** + * PHP-like print_r() & var_dump() equivalent for JavaScript Object + * + * @author Faisalman + * @license http://www.opensource.org/licenses/mit-license.php + * @link http://gist.github.com/879208 + */ +var print_r = function(obj,t){ + + // define tab spacing + var tab = t || ''; + + // check if it's array + var isArr = Object.prototype.toString.call(obj) === '[object Array]' ? true : false; + + // use {} for object, [] for array + var str = isArr ? ('Array\n' + tab + '[\n') : ('Object\n' + tab + '{\n'); + + // walk through it's properties + for(var prop in obj){ + if (obj.hasOwnProperty(prop)) { + var val1 = obj[prop]; + var val2 = ''; + var type = Object.prototype.toString.call(val1); + switch(type){ + + // recursive if object/array + case '[object Array]': + case '[object Object]': + val2 = print_r(val1, (tab + '\t')); + break; + + case '[object String]': + val2 = '\'' + val1 + '\''; + break; + + default: + val2 = val1; + } + str += tab + '\t' + prop + ' => ' + val2 + ',\n'; + } + } + + // remove extra comma for last property + str = str.substring(0, str.length-2) + '\n' + tab; + + return isArr ? (str + ']') : (str + '}'); +}; +exports.print_r = print_r; diff --git a/DeserializePHP.tableplusplugin/main.js b/DeserializePHP.tableplusplugin/main.js new file mode 100755 index 0000000..8205b14 --- /dev/null +++ b/DeserializePHP.tableplusplugin/main.js @@ -0,0 +1,24 @@ +'use strict'; + +import { unserialize, print_r } from './library/helper'; + +var onRun = function(context) { + // Get table in opening tab + var value = context.clickedRowValue(); + if (value == null) { + context.alert('Error', 'Data is null'); + return; + } + let output = unserialize(value); + if (output == null) { + context.alert('Error', 'Data is not serialized.'); + return; + } + SystemService.insertToClipboard(print_r(output)); + SystemService.notify( + "Deserialize PHP", + "Your data has been copied to your clipboard!" + ); +}; + +global.onRun = onRun; diff --git a/DeserializePHP.tableplusplugin/manifest.json b/DeserializePHP.tableplusplugin/manifest.json new file mode 100755 index 0000000..2f3075a --- /dev/null +++ b/DeserializePHP.tableplusplugin/manifest.json @@ -0,0 +1,20 @@ +{ + "name": "Deserialize PHP", + "identifier": "com.pixeljar.DeserializePHP", + "version": "1.0", + "detail": "This plugin helps you view PHP serialized data", + "author": "Pixel Jar", + "authorEmail": "info@pixeljar.com", + "scripts": [ + { + "location": "data", + "type": "separator" + }, + { + "location": "data", + "type": "action", + "script": "deserializephp.js", + "name": "Deserialize PHP" + } + ] +} diff --git a/DeserializePHP.tableplusplugin/package.json b/DeserializePHP.tableplusplugin/package.json new file mode 100755 index 0000000..5e76d5a --- /dev/null +++ b/DeserializePHP.tableplusplugin/package.json @@ -0,0 +1,21 @@ +{ + "name": "com.pixeljar.DeserializePHP.tableplusplugin", + "version": "1.0.0", + "description": "This plugin helps you view PHP serialized data", + "main": "main.js", + "dependencies": { + "webpack": "^3.4.1" + }, + "devDependencies": { + "babel-core": "^6.25.0", + "babel-loader": "^7.1.1", + "babel-preset-es2015": "^6.24.1", + "webpack": "^3.4.1" + }, + "scripts": { + "build": "webpack" + }, + "keywords": [], + "author": "https://pixeljar.com", + "license": "MIT" +} diff --git a/DeserializePHP.tableplusplugin/webpack.config.js b/DeserializePHP.tableplusplugin/webpack.config.js new file mode 100755 index 0000000..8f4c222 --- /dev/null +++ b/DeserializePHP.tableplusplugin/webpack.config.js @@ -0,0 +1,26 @@ +const webpack = require('webpack'); +const path = require('path'); + +const config = { + entry: './main.js', + output: { + filename: 'deserializephp.js' + }, + module: { + loaders: [{ + test: /\.js$/, + loader: 'babel-loader', + query: { + presets: ['es2015'] + } + }] + }, + stats: { + colors: true + }, + plugins: [ + new webpack.optimize.UglifyJsPlugin() + ] +}; + +module.exports = config; diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..32ea50e --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019 Pixel Jar (https://www.pixeljar.com/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100755 index 0000000..eb9b9df --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# What is this + +This is a TablePlus Plugin, install DeserializePHP you will have a menu `Deserialize PHP` in context menu. When selecting this menu item from the contextual menu, it will copy the deserialized PHP to your clipboard. Paste it wherever you like to view the results. + +![menu](https://github.com/pixeljar/DeserializePHP/blob/master/Resource/deserialize.png "menu") + +# Support + +TablePlus version 2.7 and above. + +# Install + +### From release + +Download [release](https://github.com/pixeljar/DeserializePHP/releases), unzip and double click on file plugin to install. + +### Build from source + +``` +git clone git@github.com:pixeljar/DeserializePHP.git +cd DeserializePHP/DeserializePHP.tableplusplugin +npm install +npm run build +open . +``` + +# How to use + +1. Open a connection. +2. Open a table. +3. Click on a cell. +4. Click on `Go to URL` in menu. + +# License + +DeserializePHP is released under the MIT license. See [LICENSE](https://github.com/pixeljar/DeserializePHP/blob/master/LICENSE) for details. diff --git a/Resource/deserialize.png b/Resource/deserialize.png new file mode 100644 index 0000000..9118f37 Binary files /dev/null and b/Resource/deserialize.png differ