├── .buildpath ├── .gitignore ├── .settings ├── org.eclipse.php.core.prefs └── org.eclipse.wst.common.project.facet.core.xml ├── BROWSER_README.md ├── INSTALL.md ├── LICENSE ├── MTS ├── Common │ ├── Data │ │ └── Computer │ │ │ ├── FileSystems │ │ │ ├── Directory.php │ │ │ ├── File.php │ │ │ └── ProcessPipe.php │ │ │ └── OperatingSystems │ │ │ ├── Base.php │ │ │ ├── Linux │ │ │ ├── ArchBase.php │ │ │ ├── CentOSBase.php │ │ │ ├── DebianBase.php │ │ │ ├── LinuxBase.php │ │ │ ├── RHELBase.php │ │ │ └── UbuntuBase.php │ │ │ ├── Microsoft │ │ │ ├── Windows.php │ │ │ └── WindowsBase.php │ │ │ └── Mikrotik │ │ │ ├── MikrotikBase.php │ │ │ └── RouterOSBase.php │ ├── Devices │ │ ├── Actions │ │ │ ├── Local │ │ │ │ ├── Base.php │ │ │ │ └── Host │ │ │ │ │ ├── ApplicationPaths.php │ │ │ │ │ ├── Browser.php │ │ │ │ │ ├── MtsSetup │ │ │ │ │ └── SetupMTS.php │ │ │ │ │ ├── OperatingSystem.php │ │ │ │ │ ├── PhpEnvironment.php │ │ │ │ │ ├── Processes.php │ │ │ │ │ ├── Shell.php │ │ │ │ │ └── Users.php │ │ │ └── Remote │ │ │ │ ├── Base.php │ │ │ │ ├── Connections │ │ │ │ └── Ssh.php │ │ │ │ └── Host │ │ │ │ ├── OperatingSystem.php │ │ │ │ └── Users.php │ │ ├── Browsers │ │ │ ├── Base.php │ │ │ ├── BrowserInterface.php │ │ │ ├── PhantomJS.php │ │ │ └── Window.php │ │ ├── Device.php │ │ ├── Shells │ │ │ ├── Base.php │ │ │ ├── Bash.php │ │ │ ├── Cmd.php │ │ │ ├── PowerShell.php │ │ │ └── RouterOS.php │ │ ├── Types │ │ │ ├── Localhost.php │ │ │ └── Remotehost.php │ │ └── VendorData │ │ │ ├── PowerShell │ │ │ └── mtsPsInit.ps1 │ │ │ └── phantomJS │ │ │ ├── PJSCtrl.js │ │ │ ├── PJSLinux32 │ │ │ ├── PJSLinux64 │ │ │ └── PJSWindows.exe │ └── Tools │ │ ├── FileSystems │ │ ├── Directories.php │ │ └── Files.php │ │ └── Time │ │ └── Epoch.php ├── EnableMTS.php ├── Factories.php ├── Factories │ ├── Actions.php │ ├── Devices.php │ ├── Files.php │ └── Time.php └── WorkDirectory │ └── placeHolder.php ├── MtsSetup.php ├── README.md ├── SHELL_README.md ├── Tests ├── Common │ └── Devices │ │ ├── Actions │ │ └── Host │ │ │ ├── ApplicationPathsTest.php │ │ │ ├── BrowserTest.php │ │ │ ├── OperatingSystemTest.php │ │ │ ├── ProcessesTest.php │ │ │ ├── ShellTest.php │ │ │ └── UsersTest.php │ │ ├── Browsers │ │ └── PhantomJSTest.php │ │ └── Types │ │ └── LocalhostTest.php ├── Factories │ ├── ActionsTest.php │ └── DevicesTest.php ├── MtsBootstrap.php ├── MtsPhpUnit.xml └── MtsUnitTestDevices.php └── composer.json /.buildpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.project 2 | -------------------------------------------------------------------------------- /.settings/org.eclipse.php.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | include_path=0;/MTS 3 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.project.facet.core.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BROWSER_README.md: -------------------------------------------------------------------------------- 1 | ## The Browser: 2 | Ever needed to login to a webpage, navigate to a specific menu and retrive content and the page relies on AJAX? 3 | I have, tons of times, let me give you one example. 4 | 5 | A major retailer in the US has an API for their vendors to retrive reports. 6 | The authentication is done by a token that can be renewed for upto one year, but every new years eve the token must be updated manually, 7 | and that process could only be done via a web page. Its a process that always causes a hickup in an otherwise automated reporting system. 8 | Its always the one off, once a year processes that do. 9 | 10 | With this browser that process has been automated. Three weeks before new years eve, the system does a dry run to make sure the portal webpage has not changed. 11 | If it changed (has not in the last 5 years) the staff gets an alert so they can update the process, otherwise the system automatically updates the token at 3am on new years while we are popping champagne. 12 | 13 | Not a huge problem, but 15 minutes of coding and the system is truely automated. 14 | 15 | This component would not be possible without the awesome work done by the people who built PhantomJS. 16 | This project simply wraps their work so it is easy for you to access using PHP. 17 | 18 | ### Basic use: 19 | 20 | This package creates a instance of PhantomJS, you can then open a website and execute standard functions against it through PHP. 21 | You start by following the installation instructions: Click Here, then instantiate a browser. 22 | 23 | ```php 24 | //Some websites are either far away or just slow, so it is a good idea to up the allowed execution time. 25 | ini_set('max_execution_time', 120); 26 | 27 | //Get a new browser window: 28 | $myUrl = "https://www.wikipedia.org/"; 29 | $windowObj = \MTS\Factories::getDevices()->getLocalHost()->getBrowser('phantomjs')->getNewWindow($myUrl); 30 | ``` 31 | 32 | $windowObj now contains a browser window with wikipedias website loaded. 33 | 34 | Lets do a search on wikipedia. Whenever we wish to manipulate an element we do so by using a css selector to identify it. Its really easy i.e.: 35 | if we want an element with id='mySearchBox' the selector would be: [id=mySearchBox]. Basically [attribute=value]. 36 | Another example of a selector: a.myClass:nth-of-type(2) this selector takes all hyperlinks in class "myClass", then picks the second one. 37 | For more information see this article. 38 | 39 | ```php 40 | //left click on the search input box (it has id=searchInput): 41 | $windowObj->mouseEventOnElement("[id=searchInput]", 'leftclick'); 42 | 43 | //Type the search string we want to perform: 44 | $windowObj->sendKeyPresses("Nikola Tesla"); 45 | 46 | //Press enter. Note special keys must be inputted as an array, while characters / numbers are inputted as a string 47 | $windowObj->sendKeyPresses(array("Enter")); 48 | ``` 49 | 50 | The page is now displaying the search result. Lets do a screen shot to make sure: 51 | 52 | ```php 53 | //perform a screenshot: 54 | $screenshotData = $windowObj->screenshot(); 55 | 56 | //render it: 57 | echo ''; 58 | ``` 59 | 60 | See "Window Methods" below for a complete list of examples 61 | 62 | ### How It Works: 63 | PhantomJS is executed and the stdIn / stdOut are used to send and receive JSON encoded commands. 64 | The JS file that is executed by PhantomJS is constantly checking its stdIn to see if any new commands have arrived. 65 | Once a command is received the action is completed and the result returned. 66 | 67 | You only have to worry about opening a page and manipulating it. 68 | 69 | ### Window Methods: 70 | 71 | Set the size of the window (in pixels): 72 | ```php 73 | $width = 640; 74 | $height = 480; 75 | $windowObj->setSize($width, $height); 76 | ``` 77 | 78 | Set the area of the window you want to screenshot (in pixels): 79 | ```php 80 | $top = 0; 81 | $left = 0; 82 | $width = 640; 83 | $height = 480; 84 | $windowObj->setRasterSize($top, $left, $width, $height); 85 | ``` 86 | 87 | Take a screenshot of the window: 88 | 89 | Accepts one argument which determines the image format. By default "png". All valid options: "png", "jpeg". 90 | ```php 91 | $imageData = $windowObj->screenshot(); 92 | ``` 93 | 94 | Close window: 95 | ```php 96 | $windowObj->close(); 97 | ``` 98 | Note: This will also close any child windows 99 | 100 | Get the DOM: 101 | ```php 102 | //get the HTML of the current page: 103 | $domData = $windowObj->getDom(); 104 | ``` 105 | 106 | Place cursor in a particular input element: 107 | ```php 108 | $selector = "[id=someElementId]"; 109 | $windowObj->focusElement($selector); 110 | ``` 111 | 112 | Does a particular selector exist: 113 | ```php 114 | $selector = "[id=someElementId]"; 115 | $exists = $windowObj->getSelectorExists($selector); 116 | //true if exists, else false 117 | ``` 118 | 119 | Type with the keyboard. Accepts two arguments: Keys to press and modifiers. 120 | 121 | Characters and numbers are accepted as a string. Special keys must be set as array, special keys: 122 | ```php 123 | $keys = array('Enter', 'Tab', 'Space', 'Backspace', 'Delete', 'Up', 'Down', 'Left', 'Right', 'Pageup', 'Pagedown', 'Numlock', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'F10', 'F11', 'F12', 'BraceLeft', 'BraceRight', 'BracketLeft', 'BracketRight'); 124 | ``` 125 | Modifiers must be set as array, modifier keys: 126 | ```php 127 | $modifiers = array('Alt', 'Shift', 'Ctrl', 'Meta', 'Keypad'); 128 | ``` 129 | Examples: 130 | ```php 131 | //Example 1, send a string of characters 132 | $keys = "My Search Key Words"; 133 | $windowObj->sendKeyPresses($keys); 134 | 135 | //Example 2, send a string of characters while holding down "shift". 136 | $keys = "My Search Key Words"; 137 | $modifiers = array("shift"); 138 | $windowObj->sendKeyPresses($keys, $modifiers); 139 | 140 | //Example 3, press enter 141 | $keys = array('Enter'); 142 | $windowObj->sendKeyPresses($keys); 143 | 144 | ``` 145 | 146 | Perform a mouse event on an element: 147 | 148 | Valid events: 149 | "up", "down", "move", "leftclick", "leftdoubleclick", "rightclick", "rightdoubleclick" 150 | ```php 151 | //left click an element 152 | $selector = "[id=someElementId]"; 153 | $event = "leftclick"; 154 | $windowObj->mouseEventOnElement($selector, $event); 155 | ``` 156 | Note: Do not use this method to click hyperlinks, use clickElement() instead. 157 | mouseEventOnElement() can only click on elements that have 2D size, a hyperlink is just a line and has no area to click. 158 | 159 | Click on an element: 160 | ```php 161 | $selector = "[id=someElementId]"; 162 | $windowObj->clickElement($selector); 163 | ``` 164 | 165 | Load some custom JavaScript in the window: 166 | ```php 167 | $scriptData = "function myHelloWorld() { 168 | return 'Hello World'; 169 | }"; 170 | 171 | $windowObj->loadJS($scriptData); 172 | ``` 173 | 174 | Call a JavaScript function: 175 | ```php 176 | //$funcReturn will contain a string with the return from the function. 177 | $funcReturn = $windowObj->callJSFunction("myHelloWorld"); 178 | ``` 179 | Note: Only content that can be serialize by Json can be returned (JSON.stringify(data), will help you), no objects. 180 | 181 | Get all cookies from a page: 182 | ```php 183 | //returns array of cookies 184 | $cookies = $windowObj->getCookies(); 185 | ``` 186 | 187 | Set a cookie for the current page 188 | ```php 189 | $name = "MyCookieName"; //String: mandetory cookie name. 190 | $value = "My Cookie Value"; //String: mandetory cookie value. 191 | $domain = null; //String: defaults to current domain in window URL 192 | $path = null; //String: defaults to "/"; 193 | $expireTime = null; //Int (epoch): default is 2147483647 194 | $serverOnly = false; //Bool: default false; if true cookie cannot be read by client scripts. 195 | $secureOnly = false; //Bool: default false; if true cookie can only be transmitted using secure (defined by the user agent, i.e. TLS) channel. 196 | 197 | $windowObj->setCookie($name, $value, $domain, $path, $expireTime, $serverOnly, $secureOnly); 198 | ``` 199 | Note: PhantomJS 2.1.1 have several issues regarding setting cookies: 200 | https://github.com/ariya/phantomjs/issues/13409 201 | https://github.com/ariya/phantomjs/issues/14047 202 | 203 | Get details of an element i.e. value: 204 | ```php 205 | //limited currently, will get more detail over time 206 | //returns array 207 | $selector = "[id=someElementId]"; 208 | $eleDetails = $windowObj->getElement($selector); 209 | ``` 210 | 211 | Get details of the document i.e. height and width: 212 | ```php 213 | //limited currently, will get more detail over time 214 | //returns array 215 | $docDetails = $windowObj->getDocument(); 216 | ``` 217 | 218 | Set the url in the window: 219 | ```php 220 | //Will load the URL in the window 221 | $myUrl = "http://www.google.com"; 222 | $windowObj->setURL($myUrl); 223 | ``` 224 | 225 | Get the current url in the window: 226 | ```php 227 | //returns the current URL as a string. 228 | $strUrl = $windowObj->getURL(); 229 | ``` 230 | Note: It is the CURRENT url that is returned, so if you submitted a form the URL may not be the same as you originally set. 231 | 232 | Set the User Agent: 233 | ```php 234 | $agentName = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0"; 235 | $windowObj->setUserAgent($agentName); 236 | ``` 237 | 238 | Set the scroll position in the window (in pixels): 239 | ```php 240 | //scroll down the page 500px 241 | $top = 500; 242 | $left = 0; 243 | $windowObj->setScrollPosition($top, $left); 244 | ``` 245 | 246 | Set if images should be loaded: 247 | ```php 248 | //default is true, but setting to false will speed up loading by omitting images 249 | $bool = false; 250 | $windowObj->setLoadImages($bool); 251 | ``` 252 | 253 | If the window spawned a popup or another window: 254 | ```php 255 | $childWindowObjs = $windowObj->getChildren(); 256 | if (count(childWindowObjs) > 0) { 257 | //the child window can be used just like a regular window 258 | $childWindowObj = current($childWindowObjs); 259 | //i.e. you can take a screen shot 260 | $childImageData = $childWindowObj->screenshot(); 261 | } 262 | ``` 263 | Note: If you execute setURL() on the parent window, all child windows will be closed automatically 264 | 265 | ##### Debugging: 266 | 267 | Figuring out what happens when a call fails can be a challenge, but if you enable debug you can catch the exception and see all reads and writes to help debug the issue. 268 | 269 | ```php 270 | $errMsg = null; 271 | try { 272 | 273 | $localHost = \MTS\Factories::getDevices()->getLocalHost(); 274 | $localHost->setDebug(true); 275 | 276 | $browserObj = $localHost->getBrowser('phantomjs'); 277 | 278 | $myUrl = "https://www.wikipedia.org/"; 279 | $windowObj = $browserObj->getNewWindow($myUrl); 280 | 281 | //execute the trouble command here i.e: 282 | $funcReturn = $windowObj->callJSFunction("myHelloWorld"); 283 | 284 | $browserObj->terminate(); 285 | 286 | } catch (\Exception $e) { 287 | switch($e->getCode()){ 288 | default; 289 | $errMsg = $e->getMessage(); 290 | } 291 | } 292 | 293 | echo "Start Debug<<<\n
 \n ";
294 | echo "Exception Message: " . $errMsg;
295 | print_r($browserObj->getDebugData());
296 | print_r($browserObj->getDebugFileContent());
297 | echo "\n 
\n >>>End Debug"; 298 | ``` 299 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installation: 2 | 3 | ### Requirements: 4 | Tested working against the following operating systems and versions. 5 | ```php 6 | Centos 6, 7. 7 | Windows 7 (Still Experimental). 8 | RedHat Enterprise 6. 9 | Debian 8. 10 | Ubuntu 16. 11 | Arch 2016-05-01 12 | 13 | ``` 14 | 15 | It should work against other Linux versions as long as they are the same flavor. 16 | 17 | ####Packages Linux: 18 | ```php 19 | Mandetory: 20 | 21 | php 5.3 (or newer) 22 | php must allow the "exec()" function 23 | python 24 | screen 25 | fontconfig 26 | 27 | Optional: 28 | sudo 29 | msttcore-fonts 30 | ``` 31 | 32 | ####Packages Windows: 33 | ```php 34 | Mandetory: 35 | 36 | php 5.3 (or newer) 37 | php must allow the "exec()" function 38 | php must allow the "popen()" function 39 | php must allow the "pclose()" function 40 | php must allow the "proc_open()" function 41 | 42 | ``` 43 | 44 | If browser screenshots are not rendering text on buttons you are most likely missing the correct fonts. 45 | 46 | 47 | ### Perform Install: 48 | 49 | #### Linux: 50 | You can run the setup in one of 3 ways: 51 | 52 | 1) Composer Install. 53 | This assumes you have composer installed already. 54 | Issue command "composer require merlinthemagic/mts" to make it part of your requirements 55 | After install you will need to execute the "MtsSetup.php" file in the root of the package, 56 | and follow the last installation steps (see option 2 or 3 for how to complete this step). 57 | This because Composer will not trigger the "post-install-cmd" of a dependency. 58 | 59 | 2-3) Manual Install: 60 | Download MTS from GitHub and upload the MTS directory to a location on your server. i.e. /var/www/tools/. 61 | You cannot only upload the content of the directory, you must upload the directory and maintain the directory name (MTS). 62 | Remember the location you uploaded to, you will need it later. 63 | Then complete install with option 2 OR 3 below. 64 | 65 | 2) Place the 'MtsSetup.php' file in a folder that is published by your webserver. 66 | Then Access the 'MtsSetup.php' file in a browser and follow the instructions. 67 | At the top of the page you will be asked to give 'Absolute Path to the directory that holds the MTS folder:'. 68 | In this example that path is '/var/www/tools/', because inside the tools directory is the MTS directory you uploaded. 69 | 70 | 3) 71 | Run the setup from the command line of the server. 72 | In this case you cannot move the 'MtsSetup.php' file, it must be located in the same directory as the 'MTS' directory. 73 | 74 | Once all dependencies have been resolved you will be provided a path that should be included in your 75 | project whenever you wish to call a function included in the MTS kit. 76 | 77 | #### Windows: 78 | You can run the setup in one of 2 ways: 79 | 80 | 1) Composer Install. 81 | This assumes you have composer installed already. 82 | Issue command "composer require merlinthemagic/mts" (dev version) to make it part of your requirements 83 | 84 | 2) Manual Install: 85 | Download MTS from GitHub and upload the MTS directory to a location on your server. i.e. C:\inet\wwwroot\tools\MTS\. 86 | You cannot only upload the content of the directory, you must upload the directory and maintain the directory name (MTS). 87 | 88 | Whenever you wish to use the MTS tools add: 89 | require_once "c:\path\to\mts\folder\EnableMTS.php"; 90 | 91 | (replace c:\path\to\mts\folder with whatever path you chose to place the package in i.e. C:\inet\wwwroot\tools\MTS) 92 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/FileSystems/Directory.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/FileSystems/Directory.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/FileSystems/File.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/FileSystems/File.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/FileSystems/ProcessPipe.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/FileSystems/ProcessPipe.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/OperatingSystems/Base.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/OperatingSystems/Base.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/OperatingSystems/Linux/ArchBase.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/OperatingSystems/Linux/ArchBase.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/OperatingSystems/Linux/CentOSBase.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/OperatingSystems/Linux/CentOSBase.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/OperatingSystems/Linux/DebianBase.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/OperatingSystems/Linux/DebianBase.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/OperatingSystems/Linux/LinuxBase.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/OperatingSystems/Linux/LinuxBase.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/OperatingSystems/Linux/RHELBase.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/OperatingSystems/Linux/RHELBase.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/OperatingSystems/Linux/UbuntuBase.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/OperatingSystems/Linux/UbuntuBase.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/OperatingSystems/Microsoft/Windows.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/OperatingSystems/Microsoft/Windows.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/OperatingSystems/Microsoft/WindowsBase.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/OperatingSystems/Microsoft/WindowsBase.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/OperatingSystems/Mikrotik/MikrotikBase.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/OperatingSystems/Mikrotik/MikrotikBase.php -------------------------------------------------------------------------------- /MTS/Common/Data/Computer/OperatingSystems/Mikrotik/RouterOSBase.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Data/Computer/OperatingSystems/Mikrotik/RouterOSBase.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Local/Base.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Actions/Local/Base.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Local/Host/ApplicationPaths.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Actions/Local/Host/ApplicationPaths.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Local/Host/Browser.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Actions/Local/Host/Browser.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Local/Host/MtsSetup/SetupMTS.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Actions/Local/Host/MtsSetup/SetupMTS.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Local/Host/OperatingSystem.php: -------------------------------------------------------------------------------- 1 | _classStore['requestType'] = __FUNCTION__; 11 | return $this->execute(); 12 | } 13 | public function getFreeRam() 14 | { 15 | $this->_classStore['requestType'] = __FUNCTION__; 16 | return $this->execute(); 17 | } 18 | private function execute() 19 | { 20 | $requestType = $this->_classStore['requestType']; 21 | $cacheId = $requestType . "_"; 22 | 23 | if ($requestType == 'getOsObj') { 24 | 25 | //the OS is constantly needed when other Actions want to determine what command syntax to use 26 | //it also does not change without a reboot, so it is safe to cache. 27 | if (isset($this->_classStore[$cacheId]) === false) { 28 | 29 | //we need 4 things to determine the correct class of OS 30 | $osArch = null; 31 | $osType = null; 32 | $osName = null; 33 | $osVersion = null; 34 | 35 | $osDetail = php_uname(); 36 | if (preg_match("/^Linux\s/i", $osDetail)) { 37 | $osType = 'linux'; 38 | if (preg_match("/(x86_64|i386|i686)/i", $osDetail, $rawArch) == 1) { 39 | $rawArch = strtolower($rawArch[1]); 40 | if ($rawArch == "x86_64") { 41 | $osArch = 64; 42 | } elseif ($rawArch == "i386" || $rawArch == "i686") { 43 | $osArch = 32; 44 | } 45 | } 46 | 47 | //systemd standardized the release file back in 2012 48 | //lets always try that file first 49 | $reFiles = array(); 50 | $reFiles[] = "/etc/os-release"; 51 | $reFiles = array_unique(array_merge($reFiles, glob('/etc/*-release'))); 52 | 53 | foreach ($reFiles as $reFile) { 54 | if (file_exists($reFile) === true) { 55 | $strCmd = "cat " . $reFile; 56 | $cReturn = $this->shellExec($strCmd); 57 | 58 | if ($osName === null) { 59 | if (preg_match("/(centos|debian|ubuntu|arch|red hat|rhel)/i", $cReturn, $rawName) == 1) { 60 | $osName = strtolower($rawName[1]); 61 | } 62 | } 63 | if ($osVersion === null) { 64 | if (preg_match("/VERSION_ID=\"([0-9]+)/", $cReturn, $rawVer) == 1) { 65 | $osVersion = $rawVer[1]; 66 | } elseif (preg_match("/release\s([0-9]+)/i", $cReturn, $rawVer) == 1) { 67 | $osVersion = $rawVer[1]; 68 | } elseif (preg_match("/DISTRIB_RELEASE=([0-9]+)/i", $cReturn, $rawVer) == 1) { 69 | $osVersion = $rawVer[1]; 70 | } elseif ($osName == 'arch') { 71 | //Arch dists have their version in a different location 72 | $archCmd = "cat /proc/version"; 73 | $archReturn = $this->shellExec($archCmd); 74 | if (preg_match("/([0-9]{8})/", $archReturn, $rawVer) == 1) { 75 | $osVersion = $rawVer[1]; 76 | } 77 | } 78 | } 79 | 80 | if ($osName !== null && $osVersion !== null) { 81 | break; 82 | } 83 | } 84 | } 85 | 86 | } elseif (preg_match("/^Windows\s/i", $osDetail)) { 87 | 88 | $osType = 'windows'; 89 | $osName = 'windows'; 90 | 91 | $cmdString = "wmic OS get Name"; 92 | $rawName = strtr($this->shellExec($cmdString), array("Microsoftr" => "Microsoft", "Serverr" => "Server")); 93 | if (preg_match("/Microsoft\s+Windows\s+(.+?)\|/i", $rawName, $raw) == 1) { 94 | $osVersion = strtolower(trim($raw[1])); 95 | } 96 | 97 | $cmdString = "wmic OS get OSArchitecture"; 98 | $cReturn = $this->shellExec($cmdString); 99 | preg_match("/(64-bit|32-bit|64 bits|32 bits)/i", $cReturn, $rawArch); 100 | if (isset($rawArch[1])) { 101 | $rawArch = strtolower($rawArch[1]); 102 | if ($rawArch == "64-bit" || $rawArch == "64 bits") { 103 | $osArch = 64; 104 | } elseif ($rawArch == "32-bit" || $rawArch == "32 bits") { 105 | $osArch = 32; 106 | } 107 | } 108 | } 109 | 110 | if ($osType === null) { 111 | throw new \Exception(__METHOD__ . ">> Could not determine OS Type"); 112 | } elseif ($osName === null) { 113 | throw new \Exception(__METHOD__ . ">> Could not determine OS distribution"); 114 | } elseif ($osVersion === null) { 115 | throw new \Exception(__METHOD__ . ">> Could not determine OS version"); 116 | } elseif ($osArch === null) { 117 | throw new \Exception(__METHOD__ . ">> Could not determine OS Architecture"); 118 | } 119 | 120 | $this->_classStore[$cacheId] = \MTS\Factories::getDevices()->getOsObj($osType, $osName, $osArch, $osVersion); 121 | } 122 | 123 | return $this->_classStore[$cacheId]; 124 | 125 | } else if ($requestType == 'getFreeRam') { 126 | 127 | $osObj = $this->getOsObj(); 128 | 129 | if ($osObj->getType() == "Linux") { 130 | $strCmd = "cat /proc/meminfo | grep -i MemAvailable"; 131 | $rData = trim($this->shellExec($strCmd)); 132 | if (preg_match("/([0-9]+)\s+(kB)/", $rData, $rawMem) == 1) { 133 | 134 | if ($rawMem[2] == "kB") { 135 | return $rawMem[1] * 1024; 136 | } 137 | } 138 | } 139 | } 140 | 141 | throw new \Exception(__METHOD__ . ">> Not Handled for Request Type: " . $requestType); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Local/Host/PhpEnvironment.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Actions/Local/Host/PhpEnvironment.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Local/Host/Processes.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Actions/Local/Host/Processes.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Local/Host/Shell.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Actions/Local/Host/Shell.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Local/Host/Users.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Actions/Local/Host/Users.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Remote/Base.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Actions/Remote/Base.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Remote/Connections/Ssh.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Actions/Remote/Connections/Ssh.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Remote/Host/OperatingSystem.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Actions/Remote/Host/OperatingSystem.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Actions/Remote/Host/Users.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Actions/Remote/Host/Users.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Browsers/Base.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Browsers/Base.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Browsers/BrowserInterface.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Browsers/BrowserInterface.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Browsers/PhantomJS.php: -------------------------------------------------------------------------------- 1 | _procPipe = $procPipeObj; 15 | } 16 | public function getPipes() 17 | { 18 | return $this->_procPipe; 19 | } 20 | private function getDebugFile() 21 | { 22 | return $this->_debugFile; 23 | } 24 | public function getDebugFileContent() 25 | { 26 | $content = ""; 27 | if ($this->getDebugFile() !== null) { 28 | $exist = \MTS\Factories::getFiles()->getFilesTool()->isFile($this->getDebugFile()); 29 | if ($exist === true) { 30 | \MTS\Factories::getFiles()->getFilesTool()->getContent($this->getDebugFile()); 31 | $content .= $this->getDebugFile()->getContent(); 32 | } 33 | } 34 | return $content; 35 | } 36 | public function setURL($windowObj, $url) 37 | { 38 | try { 39 | 40 | //by setting the url to a new value all children must be terminated 41 | $children = $windowObj->getChildren(); 42 | foreach ($children as $child) { 43 | $this->closeWindow($child); 44 | } 45 | 46 | $options = array(); 47 | $options['url'] = $url; 48 | 49 | $result = $this->getResultArray($this->browserExecute($windowObj, 'seturl', $options)); 50 | if ($result['code'] != 200) { 51 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 52 | } 53 | 54 | } catch (\Exception $e) { 55 | switch($e->getCode()){ 56 | default; 57 | throw $e; 58 | } 59 | } 60 | } 61 | public function getURL($windowObj) 62 | { 63 | try { 64 | 65 | $result = $this->getResultArray($this->browserExecute($windowObj, 'geturl')); 66 | if ($result['code'] != 200) { 67 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 68 | } else { 69 | return $result['data']['script']; 70 | } 71 | 72 | } catch (\Exception $e) { 73 | switch($e->getCode()){ 74 | default; 75 | throw $e; 76 | } 77 | } 78 | } 79 | public function screenshot($windowObj, $format) 80 | { 81 | try { 82 | 83 | //validate 84 | $format = strtolower($format); 85 | $options = array(); 86 | $options['imgFormat'] = strtolower($format); 87 | 88 | if (preg_match("/(png|jpeg)/", $format) == 0) { 89 | throw new \Exception(__METHOD__ . ">> Invalid image format: " . $format . ". Allowed: png|jpeg"); 90 | } 91 | 92 | $result = $this->getResultArray($this->browserExecute($windowObj, 'screenshot', $options)); 93 | if ($result['code'] != 200) { 94 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 95 | } else { 96 | //decode, we want to relay raw information 97 | return base64_decode($result['data']['image']); 98 | } 99 | 100 | } catch (\Exception $e) { 101 | switch($e->getCode()){ 102 | default; 103 | throw $e; 104 | } 105 | } 106 | } 107 | protected function browserCloseWindow($windowObj) 108 | { 109 | $result = $this->getResultArray($this->browserExecute($windowObj, 'closewindow')); 110 | if ($result['code'] != 200) { 111 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 112 | } 113 | } 114 | public function getDom($windowObj) 115 | { 116 | try { 117 | 118 | $result = $this->getResultArray($this->browserExecute($windowObj, 'getdom')); 119 | if ($result['code'] != 200) { 120 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 121 | } else { 122 | //decode, we want to relay raw information 123 | return urldecode($result['data']['dom']); 124 | } 125 | 126 | } catch (\Exception $e) { 127 | switch($e->getCode()){ 128 | default; 129 | throw $e; 130 | } 131 | } 132 | } 133 | public function mouseEventOnElement($windowObj, $selector, $event) 134 | { 135 | try { 136 | 137 | $event = strtolower($event); 138 | $options = array(); 139 | $options['selector'] = $selector; 140 | $options['mouseEvent'] = $event; 141 | 142 | if (preg_match("/(up|down|move|rightdoubleclick|rightclick|leftdoubleclick|leftclick)/", $event) == 0) { 143 | throw new \Exception(__METHOD__ . ">> Invalid mouse event: " . $event . ". Allowed: up|down|move|rightdoubleclick|rightclick|leftdoubleclick|leftclick"); 144 | } 145 | 146 | $result = $this->getResultArray($this->browserExecute($windowObj, 'mouseeventonelement', $options)); 147 | if ($result['code'] != 200) { 148 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 149 | } 150 | 151 | } catch (\Exception $e) { 152 | switch($e->getCode()){ 153 | default; 154 | throw $e; 155 | } 156 | } 157 | } 158 | public function focusElement($windowObj, $selector) 159 | { 160 | try { 161 | 162 | $options = array(); 163 | $options['selector'] = $selector; 164 | 165 | $result = $this->getResultArray($this->browserExecute($windowObj, 'focuselement', $options)); 166 | if ($result['code'] != 200) { 167 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 168 | } 169 | 170 | } catch (\Exception $e) { 171 | switch($e->getCode()){ 172 | default; 173 | throw $e; 174 | } 175 | } 176 | } 177 | public function getElement($windowObj, $selector) 178 | { 179 | try { 180 | 181 | $options = array(); 182 | $options['selector'] = $selector; 183 | 184 | $result = $this->getResultArray($this->browserExecute($windowObj, 'getelement', $options)); 185 | if ($result['code'] != 200) { 186 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 187 | } else { 188 | $rData = json_decode($result['data']['dom'], true); 189 | $rData["innerHTML"] = urldecode($rData["innerHTML"]); 190 | return $rData; 191 | } 192 | 193 | } catch (\Exception $e) { 194 | switch($e->getCode()){ 195 | default; 196 | throw $e; 197 | } 198 | } 199 | } 200 | public function getCookies($windowObj) 201 | { 202 | try { 203 | 204 | $result = $this->getResultArray($this->browserExecute($windowObj, 'getcookies')); 205 | if ($result['code'] != 200) { 206 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 207 | } else { 208 | return json_decode($result['data']['dom'], true); 209 | } 210 | 211 | } catch (\Exception $e) { 212 | switch($e->getCode()){ 213 | default; 214 | throw $e; 215 | } 216 | } 217 | } 218 | public function getSelectorExists($windowObj, $selector) 219 | { 220 | try { 221 | 222 | $options = array(); 223 | $options['selector'] = $selector; 224 | 225 | $result = $this->getResultArray($this->browserExecute($windowObj, 'getselectorexists', $options)); 226 | if ($result['code'] != 200) { 227 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 228 | } else { 229 | if ($result['data']['dom'] == 1) { 230 | return true; 231 | } else { 232 | return false; 233 | } 234 | } 235 | 236 | } catch (\Exception $e) { 237 | switch($e->getCode()){ 238 | default; 239 | throw $e; 240 | } 241 | } 242 | } 243 | public function getDocument($windowObj) 244 | { 245 | try { 246 | 247 | $result = $this->getResultArray($this->browserExecute($windowObj, 'getdocument')); 248 | if ($result['code'] != 200) { 249 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 250 | } else { 251 | return json_decode($result['data']['dom'], true); 252 | } 253 | 254 | } catch (\Exception $e) { 255 | switch($e->getCode()){ 256 | default; 257 | throw $e; 258 | } 259 | } 260 | } 261 | 262 | public function setCookie($windowObj, $name, $value, $domain, $path, $expireTime, $serverOnly, $secureOnly) 263 | { 264 | try { 265 | 266 | //validate 267 | if ($name == "") { 268 | throw new \Exception(__METHOD__ . ">> Name is required"); 269 | } elseif ($value == "") { 270 | throw new \Exception(__METHOD__ . ">> Value is required"); 271 | } elseif (is_bool($serverOnly) === false) { 272 | throw new \Exception(__METHOD__ . ">> Server only must be bool"); 273 | } elseif (is_bool($secureOnly) === false) { 274 | throw new \Exception(__METHOD__ . ">> Secure must be bool"); 275 | } 276 | 277 | if ($path == "") { 278 | $path = "/"; 279 | } 280 | if ($expireTime!= "") { 281 | 282 | if (ctype_digit((string) $expireTime) === false || $expireTime < 0 || $expireTime > 2147483647) { 283 | throw new \Exception(__METHOD__ . ">> Expiration must be integer between 0 and 2147483647"); 284 | } 285 | 286 | } else { 287 | $expireTime = 2147483647; 288 | } 289 | 290 | $curUrl = $windowObj->getURL(); 291 | if ($curUrl == "") { 292 | throw new \Exception(__METHOD__ . ">> Domain is required, current window does not have a URL"); 293 | } 294 | 295 | $urlParts = parse_url($curUrl); 296 | $urlhost = trim($urlParts["host"]); 297 | 298 | if ($domain == "") { 299 | $domain = $urlhost; 300 | } else { 301 | 302 | if (strpos($urlhost, $domain) === false) { 303 | throw new \Exception(__METHOD__ . ">> Cookie domain must match the current url domain"); 304 | } 305 | } 306 | 307 | $options = array(); 308 | $options['name'] = $name; 309 | $options['value'] = $value; 310 | $options['domain'] = $domain; 311 | $options['path'] = $path; 312 | $options['expiration'] = $expireTime; 313 | $options['httponly'] = $serverOnly; 314 | $options['secure'] = $secureOnly; 315 | 316 | $result = $this->getResultArray($this->browserExecute($windowObj, 'setcookie', $options)); 317 | if ($result['code'] != 200) { 318 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 319 | } 320 | 321 | } catch (\Exception $e) { 322 | switch($e->getCode()){ 323 | default; 324 | throw $e; 325 | } 326 | } 327 | } 328 | public function sendKeyPresses($windowObj, $keys, $modifiers) 329 | { 330 | try { 331 | 332 | if (is_array($keys) === false) { 333 | $keys = str_split($keys); 334 | } 335 | 336 | $options = array(); 337 | $options['keys'] = $keys; 338 | $options['modifiers'] = array(); 339 | 340 | foreach ($modifiers as $modifier) { 341 | $modKey = strtolower($modifier); 342 | if (preg_match("/(alt|shift|ctrl|meta|keypad)/", $modKey) == 0) { 343 | throw new \Exception(__METHOD__ . ">> Invalid modifier Key: " . $modKey . ". Allowed: alt|shift|ctrl|meta|keypad"); 344 | } else { 345 | $options['modifiers'][] = $modKey; 346 | } 347 | } 348 | 349 | $result = $this->getResultArray($this->browserExecute($windowObj, 'sendkeypresses', $options)); 350 | if ($result['code'] != 200) { 351 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 352 | } 353 | 354 | } catch (\Exception $e) { 355 | switch($e->getCode()){ 356 | default; 357 | throw $e; 358 | } 359 | } 360 | } 361 | public function clickElement($windowObj, $selector) 362 | { 363 | try { 364 | 365 | $options = array(); 366 | $options['selector'] = $selector; 367 | 368 | $result = $this->getResultArray($this->browserExecute($windowObj, 'clickelement', $options)); 369 | if ($result['code'] != 200) { 370 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 371 | } 372 | 373 | } catch (\Exception $e) { 374 | switch($e->getCode()){ 375 | default; 376 | throw $e; 377 | } 378 | } 379 | } 380 | public function loadJS($windowObj, $scriptData) 381 | { 382 | try { 383 | $scriptName = "JS_" . uniqid(); 384 | $scriptFile = \MTS\Factories::getFiles()->getFile($scriptName, $this->getPipes()->getOutputFile()->getDirectory()->getPathAsString()); 385 | $scriptFile->setContent($scriptData); 386 | \MTS\Factories::getFiles()->getFilesTool()->setContent($scriptFile); 387 | 388 | $options = array(); 389 | $options['scriptPath'] = $scriptFile->getPathAsString(); 390 | 391 | $result = $this->getResultArray($this->browserExecute($windowObj, 'loadjs', $options)); 392 | if ($result['code'] != 200) { 393 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 394 | } 395 | 396 | } catch (\Exception $e) { 397 | switch($e->getCode()){ 398 | default; 399 | throw $e; 400 | } 401 | } 402 | } 403 | public function callJSFunction($windowObj, $functionName) 404 | { 405 | try { 406 | 407 | $options = array(); 408 | $options['functionName'] = $functionName; 409 | 410 | $result = $this->getResultArray($this->browserExecute($windowObj, 'jscallfunction', $options)); 411 | if ($result['code'] != 200) { 412 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 413 | } else { 414 | return $result['data']['script']; 415 | } 416 | 417 | } catch (\Exception $e) { 418 | switch($e->getCode()){ 419 | default; 420 | throw $e; 421 | } 422 | } 423 | } 424 | protected function browserSetDebug() 425 | { 426 | try { 427 | $options = array(); 428 | $options['debug'] = 0; 429 | if ($this->getDebug() === true) { 430 | if ($this->getDebugFile() === null) { 431 | $this->_debugFile = \MTS\Factories::getFiles()->getFile("debug", $this->getPipes()->getOutputFile()->getDirectory()->getPathAsString()); 432 | \MTS\Factories::getFiles()->getFilesTool()->create($this->_debugFile); 433 | } 434 | 435 | $options['debug'] = 1; 436 | $options['debugPath'] = $this->getDebugFile()->getPathAsString(); 437 | } 438 | 439 | $result = $this->getResultArray($this->browserExecute(null, 'setdebug', $options)); 440 | if ($result['code'] != 200) { 441 | throw new \Exception(__METHOD__ . ">> Got result code: " . $result['code'] . ", EMsg: " . $result['error']['msg'] . ", ECode: " . $result['error']['code']); 442 | } else { 443 | //if we had a debug file delete it 444 | if ($this->getDebug() === false && $this->getDebugFile() !== null) { 445 | \MTS\Factories::getFiles()->getFilesTool()->delete($this->getDebugFile()); 446 | $this->_debugFile = null; 447 | } 448 | } 449 | 450 | } catch (\Exception $e) { 451 | switch($e->getCode()){ 452 | default; 453 | throw $e; 454 | } 455 | } 456 | } 457 | protected function browserInitialize() 458 | { 459 | if ($this->getInitialized() === null) { 460 | 461 | try { 462 | $this->_initialized = 'setup'; 463 | 464 | $options = array(); 465 | $options['stdInPath'] = $this->getPipes()->getInputFile()->getPathAsString(); 466 | $options['stdOutPath'] = $this->getPipes()->getOutputFile()->getPathAsString(); 467 | $options['stdErrPath'] = $this->getPipes()->getErrorFile()->getPathAsString(); 468 | 469 | //+ 5 so we can get return from successful termination if shutdown by execution time exceeded. 470 | $exeTimeout = \MTS\Factories::getActions()->getLocalPhpEnvironment()->getRemainingExecutionTime(); 471 | $options['terminationSecs'] = floor($exeTimeout + 5); 472 | 473 | //use the result 474 | $result = $this->getResultArray($this->browserExecute(null, 'initialize', $options)); 475 | if ($result['code'] != 200) { 476 | throw new \Exception(__METHOD__ . ">> Got code: " . $result['code']); 477 | } 478 | 479 | $this->_procPID = $result['PID']; 480 | $this->_initialized = true; 481 | 482 | } catch (\Exception $e) { 483 | 484 | switch($e->getCode()){ 485 | default; 486 | $this->_initialized = false; 487 | throw $e; 488 | } 489 | } 490 | } 491 | } 492 | 493 | protected function browserTerminate() 494 | { 495 | try { 496 | 497 | //in case the browser class was instanciated, but phantomJS was never initiated, 498 | //we will need to initiate before terminating, because without any data in stdIn the process is blocked 499 | //and we get a zombie process 500 | $this->browserInitialize(); 501 | 502 | } catch (\Exception $e) { 503 | switch($e->getCode()){ 504 | default; 505 | if ($this->getDebug() === true) { 506 | $this->addDebugData("While Terminating, Initialize threw the following error: " . $e->getMessage()); 507 | } 508 | $this->_initialized = "terminating"; 509 | throw $e; 510 | } 511 | } 512 | 513 | //now we are ready to terminate 514 | $this->_initialized = "terminating"; 515 | 516 | $result = $this->getResultArray($this->browserExecute(null, 'terminate', null)); 517 | if ($result['code'] != 200) { 518 | if ($this->getDebug() === true) { 519 | $this->addDebugData("Termination command received the following return code: " . $result['code']); 520 | } 521 | throw new \Exception(__METHOD__ . ">> Got code: " . $result['code']); 522 | } 523 | 524 | $this->_initialized = false; 525 | $this->addDebugData("Browser terminated successfully."); 526 | } 527 | 528 | //read and write 529 | protected function browserExecute($windowObj=null, $cmd=null, $options=null, $maxTimeout=null) 530 | { 531 | try { 532 | 533 | if ($this->getInitialized() === false) { 534 | throw new \Exception(__METHOD__ . ">> Cannot execute commands. Browser has been terminated"); 535 | } elseif ($this->getInitialized() === null) { 536 | $this->browserInitialize(); 537 | } 538 | 539 | if ($maxTimeout === null) { 540 | $maxTimeout = $this->getDefaultExecutionTime(); 541 | } 542 | 543 | $cmdArr = array(); 544 | $cmdArr['cmd'] = array(); 545 | $cmdArr['cmd']['id'] = uniqid(); 546 | $cmdArr['cmd']['name'] = strtolower($cmd); 547 | $cmdArr['cmd']['timeout'] = $maxTimeout; 548 | 549 | if (is_array($options) === true) { 550 | $cmdArr['cmd']['options'] = $options; 551 | } else { 552 | $cmdArr['cmd']['options'] = array(); 553 | } 554 | 555 | if (is_object($windowObj) === true) { 556 | 557 | $size = $windowObj->getSize(); 558 | $raster = $windowObj->getRasterSize(); 559 | 560 | $cmdArr['cmd']['window'] = array(); 561 | $cmdArr['cmd']['window']['UUID'] = $windowObj->getUUID(); 562 | $cmdArr['cmd']['window']['loadImages'] = 1; 563 | if ($windowObj->getLoadImages() === false) { 564 | $cmdArr['cmd']['window']['loadImages'] = 0; 565 | } 566 | $cmdArr['cmd']['window']['userAgent'] = ""; 567 | if ($windowObj->getUserAgent() !== null) { 568 | $cmdArr['cmd']['window']['userAgent'] = $windowObj->getUserAgent(); 569 | } 570 | 571 | $cmdArr['cmd']['window']['width'] = $size['width']; 572 | $cmdArr['cmd']['window']['height'] = $size['height']; 573 | 574 | $cmdArr['cmd']['window']['raster']['top'] = $raster['top']; 575 | $cmdArr['cmd']['window']['raster']['left'] = $raster['left']; 576 | $cmdArr['cmd']['window']['raster']['width'] = $raster['width']; 577 | $cmdArr['cmd']['window']['raster']['height'] = $raster['height']; 578 | 579 | $scroll = $windowObj->getScrollPosition(); 580 | $cmdArr['cmd']['window']['scroll']['top'] = $scroll['top']; 581 | $cmdArr['cmd']['window']['scroll']['left'] = $scroll['left']; 582 | 583 | } else { 584 | $cmdArr['cmd']['window'] = array(); 585 | } 586 | 587 | //turn array into jSON 588 | $cmdJson = json_encode($cmdArr, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT); 589 | 590 | //clean up 591 | $this->getPipes()->resetReadPosition(); 592 | 593 | //execute command 594 | $wData = $this->browserWrite($cmdJson); 595 | if (strlen($wData['error'] ?? '') > 0) { 596 | throw new \Exception(__METHOD__ . ">> Failed to write command to browser. Error: " . $wData['error']); 597 | } else { 598 | 599 | if ($maxTimeout > 0) { 600 | 601 | $rData = $this->browserRead($cmdJson); 602 | if (strlen($rData['error'] ?? '') > 0) { 603 | throw new \Exception(__METHOD__ . ">> Failed to read command from browser. Error: " . $rData['error']); 604 | } else { 605 | 606 | //create / remove children as needed 607 | if (is_object($windowObj) === true) { 608 | $deCmd = json_decode($rData['data'], true); 609 | 610 | //parent logic 611 | $parentUUID = $deCmd['result']['parent']; 612 | if ($parentUUID !== null) { 613 | $parentObj = $this->getWindow($parentUUID); 614 | if ($parentObj !== false) { 615 | $curParent = $windowObj->getParent(); 616 | if ($curParent !== null) { 617 | if ($curParent->getUUID() != $parentUUID) { 618 | throw new \Exception(__METHOD__ . ">> Window: " . $windowObj->getUUID . ", is reported to have parent: " . $parentUUID . ", however the current parent is ".$curParent->getUUID().". Should not be possible."); 619 | } else { 620 | //parent already set, no change 621 | } 622 | } else { 623 | //no current parent, add it 624 | $windowObj->setParent($parentObj); 625 | } 626 | } else { 627 | throw new \Exception(__METHOD__ . ">> Window: " . $windowObj->getUUID() . ", is reported to have parent: " . $parentUUID . ", however the parent is unknown. Should not be possible."); 628 | } 629 | } 630 | 631 | //parent logic 632 | $parentUUID = $deCmd['result']['parent']; 633 | if ($parentUUID !== null) { 634 | $parentObj = $this->getWindow($parentUUID); 635 | if ($parentObj !== false) { 636 | $curParent = $windowObj->getParent(); 637 | if ($curParent !== null) { 638 | if ($curParent->getUUID() != $parentUUID) { 639 | throw new \Exception(__METHOD__ . ">> Window: " . $windowObj->getUUID . ", is reported to have parent: " . $parentUUID . ", however the current parent is ".$curParent->getUUID().". Should not be possible."); 640 | } else { 641 | //parent already set, no change 642 | } 643 | } else { 644 | //no current parent, add it 645 | $windowObj->setParent($parentObj); 646 | } 647 | } else { 648 | throw new \Exception(__METHOD__ . ">> Window: " . $windowObj->getUUID . ", is reported to have parent: " . $parentUUID . ", however the parent is unknown. Should not be possible."); 649 | } 650 | } 651 | 652 | 653 | //child logic 654 | $pjsChildren = $deCmd['result']['children']; 655 | $childObjs = $windowObj->getChildren(); 656 | 657 | //start by removing children that are no longer present 658 | foreach ($childObjs as $childObj) { 659 | $exist = false; 660 | foreach ($pjsChildren as $pjsChild) { 661 | if ($pjsChild['window']['uuid'] == $childObj->getUUID()) { 662 | $exist = true; 663 | break; 664 | } 665 | } 666 | 667 | if ($exist === false) { 668 | //child no longer exist, remove 669 | $this->closeWindow($childObj); 670 | } 671 | } 672 | 673 | 674 | //add new children 675 | foreach ($pjsChildren as $pjsChild) { 676 | if ($windowObj->getChild($pjsChild['window']['uuid']) === false) { 677 | //child must be created 678 | $childObj = $this->getNewWindow(null); 679 | //uuid comes from outside this time 680 | $childObj->setUUID($pjsChild['window']['uuid']); 681 | $childObj->setSize($pjsChild['window']['width'], $pjsChild['window']['height']); 682 | $childObj->setRasterSize($pjsChild['window']['raster']['top'], $pjsChild['window']['raster']['left'], $pjsChild['window']['raster']['width'], $pjsChild['window']['raster']['height']); 683 | 684 | $windowObj->setChild($childObj); 685 | } 686 | } 687 | } 688 | 689 | return $rData['data']; 690 | } 691 | 692 | } else { 693 | // no return requested 694 | } 695 | } 696 | 697 | } catch (\Exception $e) { 698 | switch($e->getCode()){ 699 | default; 700 | throw $e; 701 | } 702 | } 703 | } 704 | private function browserWrite($cmdJson) 705 | { 706 | $cmdStr = "cmdStart>>>" . base64_encode($cmdJson) . "<<getEpochTool()->getCurrentMiliTime(); 710 | try { 711 | $this->getPipes()->strWrite($cmdStr); 712 | } catch (\Exception $e) { 713 | 714 | switch($e->getCode()){ 715 | default; 716 | $return['error'] = $e->getMessage(); 717 | } 718 | } 719 | 720 | $return['etime'] = \MTS\Factories::getTime()->getEpochTool()->getCurrentMiliTime(); 721 | 722 | if ($this->_debug === true) { 723 | $debugData = $return; 724 | $debugData['type'] = __FUNCTION__; 725 | $debugData['cmdJson'] = $cmdJson; 726 | $this->addDebugData($debugData); 727 | } 728 | return $return; 729 | } 730 | private function browserRead($cmdJson) 731 | { 732 | $decodedOrigCmd = json_decode($cmdJson, true); 733 | $cmdUUID = $decodedOrigCmd['cmd']['id']; 734 | //getCurrentMiliTime returns a decimal 735 | //add 250 milliseconds that way phantomJS has time to return the error reason 736 | $maxWait = ($decodedOrigCmd['cmd']['timeout'] + 250) / 1000; 737 | 738 | $return['error'] = null; 739 | //add partial data return if we read some of a return earlier 740 | $return['data'] = ""; 741 | $lDataTime = \MTS\Factories::getTime()->getEpochTool()->getCurrentMiliTime(); 742 | $return['stime'] = $lDataTime; 743 | $done = false; 744 | 745 | try { 746 | while ($done === false) { 747 | $newData = $this->getPipes()->strRead(); 748 | $exeTime = \MTS\Factories::getTime()->getEpochTool()->getCurrentMiliTime(); 749 | 750 | if ($newData != "") { 751 | $lDataTime = $exeTime; 752 | $return['data'] .= $newData; 753 | 754 | $allData = $this->_partialReturn . $return['data']; 755 | $startPos = strpos($allData, "cmdStartReturn>>>"); 756 | $endPos = strpos($allData, "<<>>", $allData); 760 | 761 | foreach ($cmdLines as $cmdLine) { 762 | $cmdLine = trim($cmdLine); 763 | if ($cmdLine != "") { 764 | $cmdEndPos = strpos($cmdLine, "<<_partialReturn = ""; 780 | } else { 781 | //there is some data left 782 | $rCmdStart = strpos($allData, $origCmd); 783 | //we trim because commands are followed by \n\n, if that is all that is left 784 | //we can just remove it 785 | $this->_partialReturn = trim(substr($allData, ($rCmdStart + $origCmdLen))); 786 | } 787 | 788 | //override the return so only the command remains 789 | //also make pretty so the execution class get a return like what it sent 790 | $return['data'] = json_encode($decodedCmd, JSON_PRETTY_PRINT | JSON_FORCE_OBJECT);; 791 | $done = true; 792 | } 793 | } 794 | } 795 | } 796 | } 797 | 798 | } else { 799 | //wait for a tiny bit no need to saturate the CPU 800 | usleep(10000); 801 | } 802 | 803 | if ($done === false && ($exeTime - $return['stime']) > $maxWait) { 804 | //timed out 805 | $return['error'] = 'timeout'; 806 | $done = true; 807 | } 808 | } 809 | } catch (\Exception $e) { 810 | 811 | switch($e->getCode()) { 812 | default; 813 | $return['error'] = $e->getMessage(); 814 | } 815 | } 816 | 817 | if ($return['error'] !== null) { 818 | //we have an error, all data is partial 819 | $this->_partialReturn = $this->_partialReturn . $return['data']; 820 | } 821 | 822 | $return['etime'] = \MTS\Factories::getTime()->getEpochTool()->getCurrentMiliTime(); 823 | 824 | if ($this->_debug === true) { 825 | $debugData = $return; 826 | $debugData['type'] = __FUNCTION__; 827 | $debugData['cmdJson'] = $cmdJson; 828 | $debugData['timeout'] = ($maxWait * 1000); //we want a milisec value 829 | $this->addDebugData($debugData); 830 | } 831 | 832 | return $return; 833 | } 834 | private function getResultArray($rCmdJson) 835 | { 836 | $deCmd = json_decode($rCmdJson, true); 837 | return $deCmd['result']; 838 | } 839 | } 840 | -------------------------------------------------------------------------------- /MTS/Common/Devices/Browsers/Window.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Browsers/Window.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Device.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Device.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Shells/Base.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Shells/Base.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Shells/Bash.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Shells/Bash.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Shells/Cmd.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Shells/Cmd.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Shells/PowerShell.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Shells/PowerShell.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Shells/RouterOS.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Shells/RouterOS.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Types/Localhost.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Types/Localhost.php -------------------------------------------------------------------------------- /MTS/Common/Devices/Types/Remotehost.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Devices/Types/Remotehost.php -------------------------------------------------------------------------------- /MTS/Common/Devices/VendorData/PowerShell/mtsPsInit.ps1: -------------------------------------------------------------------------------- 1 | $workPath = $args[0]; 2 | 3 | $stdIn = $workPath + "\stdIn"; 4 | $stdOut = $workPath + "\stdOut"; 5 | $stdErr = $workPath + "\stdErr"; 6 | 7 | #when we did not get a command, how long should we wait before 8 | #checking again 9 | $waitDelay = 10; 10 | 11 | #debug data 12 | $debug = 0; 13 | $debugFile = ""; 14 | 15 | $cmdObj = ""; 16 | $run = 1; 17 | 18 | #Init system 19 | 20 | #do not clear stdin there may already be a pending command 21 | Clear-Content "$stdOut"; 22 | Clear-Content "$stdErr"; 23 | 24 | #We are using json to send and receive commands, but the json-decode function 25 | #is only available in ps v3, so we use the web module instead since it is likely to 26 | #already be installed 27 | add-type -assembly system.web.extensions; 28 | $serObj = new-object system.web.script.serialization.javascriptSerializer; 29 | 30 | 31 | 32 | $i=0; 33 | DO { 34 | $i++; 35 | 36 | try { 37 | 38 | $encCMD = Get-Content "$stdIn"; 39 | if (![string]::IsNullOrEmpty($encCMD)) { 40 | 41 | #got something, is it a full command? 42 | $getCmdRegex = [regex]"cmdStart>>>(.*)<<>>$rJsonEnc<<>>$rEncData<< 0) { 182 | 183 | for (var i=0; i < winLen; i++) { 184 | var winObj = classData.windows[i]; 185 | if (windowObj.uuid == winObj.uuid) { 186 | //found the correct window 187 | //close the window and free up the memory 188 | windowObj.pjsPage.close(); 189 | //remove from the array 190 | classData.windows.splice(i, 1); 191 | break; 192 | } 193 | } 194 | } 195 | 196 | cmdObj.result.code = 200; 197 | 198 | writeReturn(cmdObj); 199 | processLoop(); 200 | 201 | } catch(e) { 202 | cmdObj.result.error.msg = "Failed to close window. Error: " + e; 203 | writeReturn(cmdObj); 204 | processLoop(); 205 | } 206 | } 207 | 208 | 209 | function getSelectorExists(cmdObj) 210 | { 211 | try { 212 | 213 | //validate 214 | var windowObj = getWindowByCommand(cmdObj); 215 | if (typeof cmdObj.cmd.options.selector == 'undefined') { 216 | throw "Selector must be set"; 217 | } 218 | 219 | var selector = cmdObj.cmd.options.selector; 220 | var result = windowObj.pjsPage.evaluate(function(selector){ 221 | var element = document.querySelector(selector); 222 | 223 | if (element === null) { 224 | return 'selectorNotExist'; 225 | } else { 226 | return 'selectorExist'; 227 | } 228 | 229 | }, selector); 230 | 231 | if (result == 'selectorExist') { 232 | cmdObj.result.code = 200; 233 | cmdObj.result.data.dom = 1; 234 | } else if (result == 'selectorNotExist') { 235 | cmdObj.result.code = 200; 236 | cmdObj.result.data.dom = 0; 237 | } else { 238 | throw "Invalid Return: " + result; 239 | } 240 | writeReturn(cmdObj); 241 | processLoop(); 242 | 243 | } catch(e) { 244 | cmdObj.result.error.msg = "Failed to get selector exists. Error: " + e; 245 | writeReturn(cmdObj); 246 | processLoop(); 247 | } 248 | } 249 | 250 | function getElement(cmdObj) 251 | { 252 | try { 253 | 254 | //validate 255 | var windowObj = getWindowByCommand(cmdObj); 256 | if (typeof cmdObj.cmd.options.selector == 'undefined') { 257 | throw "Selector must be set"; 258 | } 259 | 260 | var selector = cmdObj.cmd.options.selector; 261 | var result = windowObj.pjsPage.evaluate(function(selector){ 262 | var element = document.querySelector(selector); 263 | 264 | if (element === null) { 265 | return 'selectorNotExist'; 266 | } else { 267 | 268 | //keep adding data on element over time, this shoud be the primary way to get data on 269 | //an element outside of getDom() 270 | 271 | try { 272 | 273 | var result = {}; 274 | result.tagName = element.tagName; 275 | 276 | //get value 277 | if (typeof element.value !== "undefined") { 278 | result.value = element.value; 279 | } else { 280 | result.value = null; 281 | } 282 | //get type 283 | if (typeof element.type !== "undefined") { 284 | result.type = element.type; 285 | } else { 286 | result.type = null; 287 | } 288 | //get text 289 | if (typeof element.text !== "undefined") { 290 | result.text = element.text; 291 | } else { 292 | result.text = null; 293 | } 294 | 295 | //get innerHTML 296 | if (typeof element.innerHTML !== "undefined") { 297 | result.innerHTML = encodeURIComponent(element.innerHTML); 298 | } else { 299 | result.innerHTML = null; 300 | } 301 | 302 | //get location 303 | result.location = {}; 304 | var rect = element.getBoundingClientRect(); 305 | result.location.top = rect.top; 306 | result.location.bottom = rect.bottom; 307 | result.location.right = rect.right; 308 | result.location.left = rect.left; 309 | 310 | return JSON.stringify(result); 311 | 312 | } catch(e) { 313 | return "Get Element Inside Evaluate. Error: " + String(e); 314 | } 315 | } 316 | 317 | }, selector); 318 | 319 | if (result == 'selectorNotExist') { 320 | cmdObj.result.error.msg = selector + " does not exist"; 321 | } else if (result.match(/^Get Element Inside Evaluate/)) { 322 | cmdObj.result.error.msg = result; 323 | } else { 324 | cmdObj.result.code = 200; 325 | cmdObj.result.data.dom = result; 326 | } 327 | writeReturn(cmdObj); 328 | processLoop(); 329 | 330 | } catch(e) { 331 | cmdObj.result.error.msg = "Failed to get element. Error: " + e; 332 | writeReturn(cmdObj); 333 | processLoop(); 334 | } 335 | } 336 | function getDocument(cmdObj) 337 | { 338 | try { 339 | 340 | //validate 341 | var windowObj = getWindowByCommand(cmdObj); 342 | var result = windowObj.pjsPage.evaluate(function(){ 343 | 344 | //keep adding data on document over time, this shoud be the primary way to get data on 345 | //the document outside of getDom() 346 | 347 | try { 348 | 349 | var result = {}; 350 | 351 | //body attributes 352 | result.body = {}; 353 | result.body.clientHeight = document.body.clientHeight; 354 | result.body.offsetHeight = document.body.offsetHeight; 355 | result.body.scrollHeight = document.body.scrollHeight; 356 | result.body.clientWidth = document.body.clientWidth; 357 | result.body.offsetWidth = document.body.offsetWidth; 358 | result.body.scrollWidth = document.body.scrollWidth; 359 | 360 | result.documentElement = {}; 361 | result.documentElement.clientHeight = document.documentElement.clientHeight; 362 | result.documentElement.scrollHeight = document.documentElement.scrollHeight; 363 | result.documentElement.clientWidth = document.documentElement.clientWidth; 364 | result.documentElement.scrollWidth = document.documentElement.scrollWidth; 365 | 366 | result.document = {}; 367 | //best guess of total document height and width 368 | result.document.width = Math.max(result.body.clientWidth, result.body.offsetWidth, result.body.scrollWidth, result.documentElement.clientWidth, result.documentElement.scrollWidth); 369 | result.document.height = Math.max(result.body.clientHeight, result.body.offsetHeight, result.body.scrollHeight, result.documentElement.clientHeight, result.documentElement.scrollHeight); 370 | return JSON.stringify(result); 371 | 372 | } catch(e) { 373 | return "Get Document Inside Evaluate. Error: " + String(e); 374 | } 375 | }); 376 | 377 | if (result.match(/^Get Document Inside Evaluate/)) { 378 | cmdObj.result.error.msg = result; 379 | } else { 380 | cmdObj.result.code = 200; 381 | cmdObj.result.data.dom = result; 382 | } 383 | writeReturn(cmdObj); 384 | processLoop(); 385 | 386 | } catch(e) { 387 | cmdObj.result.error.msg = "Failed to get document. Error: " + e; 388 | writeReturn(cmdObj); 389 | processLoop(); 390 | } 391 | } 392 | function getCookies(cmdObj) 393 | { 394 | try { 395 | //validate 396 | var windowObj = getWindowByCommand(cmdObj); 397 | var result = windowObj.pjsPage.cookies; 398 | 399 | cmdObj.result.code = 200; 400 | cmdObj.result.data.dom = JSON.stringify(result); 401 | writeReturn(cmdObj); 402 | processLoop(); 403 | 404 | } catch(e) { 405 | cmdObj.result.error.msg = "Failed to get cookies. Error: " + e; 406 | writeReturn(cmdObj); 407 | processLoop(); 408 | } 409 | } 410 | function setCookie(cmdObj) 411 | { 412 | try { 413 | 414 | //several active issues setting cookies, see: 415 | //https://github.com/ariya/phantomjs/issues/13409 416 | //https://github.com/ariya/phantomjs/issues/14047 417 | 418 | var windowObj = getWindowByCommand(cmdObj); 419 | 420 | var nCookie = {}; 421 | nCookie.name = cmdObj.cmd.options.name; 422 | nCookie.value = cmdObj.cmd.options.value; 423 | nCookie.domain = cmdObj.cmd.options.domain; 424 | nCookie.path = cmdObj.cmd.options.path; 425 | nCookie.httponly = cmdObj.cmd.options.httponly; 426 | nCookie.secure = cmdObj.cmd.options.secure; 427 | nCookie.expires = cmdObj.cmd.options.expires; 428 | 429 | var success = windowObj.pjsPage.addCookie(nCookie); 430 | 431 | // if (success === true) { 432 | cmdObj.result.code = 200; 433 | writeReturn(cmdObj); 434 | processLoop(); 435 | // } else { 436 | // cmdObj.result.error.msg = "Failed to set cookie"; 437 | // writeReturn(cmdObj); 438 | // processLoop(); 439 | // } 440 | 441 | } catch(e) { 442 | cmdObj.result.error.msg = "Failed to set Url. Error: " + e; 443 | writeReturn(cmdObj); 444 | processLoop(); 445 | } 446 | } 447 | function clickElement(cmdObj) 448 | { 449 | try { 450 | 451 | //validate 452 | var windowObj = getWindowByCommand(cmdObj); 453 | if (typeof cmdObj.cmd.options.selector == 'undefined') { 454 | throw "Selector must be set"; 455 | } 456 | 457 | var selector = cmdObj.cmd.options.selector; 458 | var result = windowObj.pjsPage.evaluate(function(selector){ 459 | var element = document.querySelector(selector); 460 | 461 | if (element === null) { 462 | return 'selectorNotExist'; 463 | } else { 464 | element.click(); 465 | return 'selectorClicked'; 466 | } 467 | 468 | }, selector); 469 | 470 | if (result != 'selectorNotExist') { 471 | cmdObj.result.code = 200; 472 | cmdObj.result.data.script = result; 473 | } else { 474 | cmdObj.result.error.msg = selector + " does not exist"; 475 | } 476 | 477 | writeReturn(cmdObj); 478 | processLoop(); 479 | 480 | } catch(e) { 481 | cmdObj.result.error.msg = "Failed to focus selector. Error: " + e; 482 | writeReturn(cmdObj); 483 | processLoop(); 484 | } 485 | } 486 | function mouseEventOnElement(cmdObj) 487 | { 488 | try { 489 | 490 | //validate 491 | var windowObj = getWindowByCommand(cmdObj); 492 | if (typeof cmdObj.cmd.options.selector == 'undefined') { 493 | throw "Selector must be set"; 494 | } else if (typeof cmdObj.cmd.options.mouseEvent == 'undefined') { 495 | throw "Mouse event must be set"; 496 | } 497 | 498 | var selector = cmdObj.cmd.options.selector; 499 | var result = windowObj.pjsPage.evaluate(function(selector){ 500 | var element = document.querySelector(selector); 501 | 502 | if (element === null) { 503 | return 'selectorNotExist'; 504 | } else { 505 | 506 | try { 507 | 508 | //get center of the element location 509 | var rect = element.getBoundingClientRect(); 510 | var vertical = (((rect.bottom - rect.top) / 2) + rect.top); 511 | var horizontal = (((rect.right - rect.left) / 2) + rect.left); 512 | 513 | var result = {}; 514 | result.vertical = vertical; 515 | result.horizontal = horizontal; 516 | 517 | return JSON.stringify(result); 518 | 519 | } catch(e) { 520 | return "Mouse Event Inside Evaluate. Error: " + String(e); 521 | } 522 | } 523 | 524 | }, selector); 525 | 526 | if (result == 'selectorNotExist') { 527 | cmdObj.result.error.msg = selector + " does not exist"; 528 | } else if (result === null) { 529 | cmdObj.result.error.msg = selector + " does not have a size we can perform a mouse event on"; 530 | } else if (result.match(/^Mouse Event Inside Evaluate/)) { 531 | cmdObj.result.error.msg = result; 532 | } else { 533 | 534 | var coords = JSON.parse(result); 535 | 536 | if (coords.horizontal > 0 && coords.vertical > 0) { 537 | if (cmdObj.cmd.options.mouseEvent == 'up') { 538 | windowObj.pjsPage.sendEvent("mouseup", coords.horizontal, coords.vertical); 539 | } else if (cmdObj.cmd.options.mouseEvent == 'down') { 540 | windowObj.pjsPage.sendEvent("mousedown", coords.horizontal, coords.vertical); 541 | } else if (cmdObj.cmd.options.mouseEvent == 'move') { 542 | windowObj.pjsPage.sendEvent("mousemove", coords.horizontal, coords.vertical); 543 | } else if (cmdObj.cmd.options.mouseEvent == 'leftclick') { 544 | windowObj.pjsPage.sendEvent("click", coords.horizontal, coords.vertical, 'left'); 545 | } else if (cmdObj.cmd.options.mouseEvent == 'leftdoubleclick') { 546 | windowObj.pjsPage.sendEvent("doubleclick", coords.horizontal, coords.vertical, 'left'); 547 | } else if (cmdObj.cmd.options.mouseEvent == 'rightclick') { 548 | windowObj.pjsPage.sendEvent("click", coords.horizontal, coords.vertical, 'right'); 549 | } else if (cmdObj.cmd.options.mouseEvent == 'rightdoubleclick') { 550 | windowObj.pjsPage.sendEvent("doubleclick", coords.horizontal, coords.vertical, 'right'); 551 | } else { 552 | throw "Invalid mouse event: " + cmdObj.cmd.options.mouseEvent; 553 | } 554 | 555 | //in the clear 556 | cmdObj.result.code = 200; 557 | 558 | } else { 559 | throw "Cannot execute mouse event: " + cmdObj.cmd.options.mouseEvent + "size is: " + coords.horizontal + ":" + coords.vertical; 560 | } 561 | 562 | } 563 | writeReturn(cmdObj); 564 | processLoop(); 565 | 566 | } catch(e) { 567 | cmdObj.result.error.msg = "Failed to execute mouse event on selector. Error: " + e; 568 | writeReturn(cmdObj); 569 | processLoop(); 570 | } 571 | } 572 | function sendKeyPresses(cmdObj) 573 | { 574 | try { 575 | 576 | //validate 577 | var windowObj = getWindowByCommand(cmdObj); 578 | if (typeof cmdObj.cmd.options.keys == 'undefined') { 579 | throw "Keys must be set"; 580 | } else if (typeof cmdObj.cmd.options.modifiers == 'undefined') { 581 | throw "Modifiers must be set"; 582 | } 583 | 584 | var keyCount = Object.keys(cmdObj.cmd.options.keys).length; 585 | var modCount = Object.keys(cmdObj.cmd.options.modifiers).length; 586 | 587 | if (modCount > 0) { 588 | var modString = ""; 589 | for (var i=0; i < modCount; i++) { 590 | var modkey = cmdObj.cmd.options.modifiers[i]; 591 | 592 | if (modkey == "alt") { 593 | var modifier = "0x08000000"; 594 | } else if (modkey == "shift") { 595 | var modifier = "0x02000000"; 596 | } else if (modkey == "ctrl") { 597 | var modifier = "0x04000000"; 598 | } else if (modkey == "meta") { 599 | var modifier = "0x10000000"; 600 | } else if (modkey == "keypad") { 601 | var modifier = "0x20000000"; 602 | } else { 603 | throw "Invalid Modifier Key: " . modkey; 604 | } 605 | 606 | if (i > 0) { 607 | modString += " | " + modifier; 608 | } else { 609 | modString += modifier; 610 | } 611 | } 612 | } else { 613 | var modString = 0; 614 | } 615 | 616 | //full list 617 | //src: https://github.com/ariya/phantomjs/commit/cab2635e66d74b7e665c44400b8b20a8f225153a 618 | for (var i=0; i < keyCount; i++) { 619 | var kp = cmdObj.cmd.options.keys[i]; 620 | var kpLow = String(kp).toLowerCase(); 621 | if ( 622 | kpLow == "backspace" 623 | || kpLow == "enter" 624 | || kpLow == "delete" 625 | || kpLow == "up" 626 | || kpLow == "down" 627 | || kpLow == "left" 628 | || kpLow == "right" 629 | || kpLow == "pageup" 630 | || kpLow == "pagedown" 631 | || kpLow == "numlock" 632 | || kpLow == "tab" 633 | || kpLow == "braceleft" 634 | || kpLow == "braceright" 635 | || kpLow == "bracketleft" 636 | || kpLow == "bracketright" 637 | || kpLow == "f1" 638 | || kpLow == "f2" 639 | || kpLow == "f3" 640 | || kpLow == "f4" 641 | || kpLow == "f5" 642 | || kpLow == "f6" 643 | || kpLow == "f7" 644 | || kpLow == "f8" 645 | || kpLow == "f9" 646 | || kpLow == "f10" 647 | || kpLow == "f11" 648 | || kpLow == "f12" 649 | ) { 650 | var result = windowObj.pjsPage.sendEvent("keypress", windowObj.pjsPage.event.key[kp], modString); 651 | } else { 652 | var result = windowObj.pjsPage.sendEvent("keypress", kp, modString); 653 | } 654 | } 655 | 656 | //result is undefined, dont know how we could validate the job was done 657 | cmdObj.result.code = 200; 658 | writeReturn(cmdObj); 659 | processLoop(); 660 | 661 | } catch(e) { 662 | cmdObj.result.error.msg = "Failed to send Key presses. Error: " + e; 663 | writeReturn(cmdObj); 664 | processLoop(); 665 | } 666 | } 667 | function focusElement(cmdObj) 668 | { 669 | try { 670 | 671 | //validate 672 | var windowObj = getWindowByCommand(cmdObj); 673 | if (typeof cmdObj.cmd.options.selector == 'undefined') { 674 | throw "Selector must be set"; 675 | } 676 | 677 | var selector = cmdObj.cmd.options.selector; 678 | var result = windowObj.pjsPage.evaluate(function(selector){ 679 | var element = document.querySelector(selector); 680 | 681 | if (element === null) { 682 | return 'selectorNotExist'; 683 | } else { 684 | element.click(); 685 | element.focus(); 686 | return 'selectorFocused'; 687 | } 688 | 689 | }, selector); 690 | 691 | if (result != 'selectorNotExist') { 692 | cmdObj.result.code = 200; 693 | cmdObj.result.data.script = result; 694 | } else { 695 | cmdObj.result.error.msg = selector + " does not exist"; 696 | } 697 | writeReturn(cmdObj); 698 | processLoop(); 699 | 700 | } catch(e) { 701 | cmdObj.result.error.msg = "Failed to focus selector. Error: " + e; 702 | writeReturn(cmdObj); 703 | processLoop(); 704 | } 705 | } 706 | function JSCallFunction(cmdObj) 707 | { 708 | try { 709 | 710 | //validate 711 | var windowObj = getWindowByCommand(cmdObj); 712 | if (typeof cmdObj.cmd.options.functionName == 'undefined') { 713 | throw "Function name must be set"; 714 | } 715 | 716 | var funcName = cmdObj.cmd.options.functionName; 717 | var result = windowObj.pjsPage.evaluate(function(funcName) { 718 | if (typeof window[funcName] == 'function') { 719 | var result = window[funcName](); 720 | //add check that the result can be serialized and returned out of eval, objects cannot be returned 721 | return result; 722 | } else { 723 | return 'FunctionDoesNotExistBLA433'; 724 | } 725 | }, funcName); 726 | 727 | if (result != 'FunctionDoesNotExistBLA433') { 728 | cmdObj.result.code = 200; 729 | cmdObj.result.data.script = result; 730 | } else { 731 | cmdObj.result.error.msg = funcName + " is not a valid function name"; 732 | } 733 | writeReturn(cmdObj); 734 | processLoop(); 735 | 736 | } catch(e) { 737 | cmdObj.result.error.msg = "Failed to call JS function. Error: " + e; 738 | writeReturn(cmdObj); 739 | processLoop(); 740 | } 741 | } 742 | function loadJS(cmdObj) 743 | { 744 | try { 745 | 746 | //validate 747 | var windowObj = getWindowByCommand(cmdObj); 748 | if (typeof cmdObj.cmd.options.scriptPath == 'undefined') { 749 | throw "Script path must be set"; 750 | } 751 | 752 | var result = windowObj.pjsPage.injectJs(cmdObj.cmd.options.scriptPath); 753 | if (result === true) { 754 | cmdObj.result.code = 200; 755 | } else { 756 | cmdObj.result.error.msg = "Script Failed to Load"; 757 | } 758 | writeReturn(cmdObj); 759 | processLoop(); 760 | 761 | } catch(e) { 762 | cmdObj.result.error.msg = "Failed to load java script. Error: " + e; 763 | writeReturn(cmdObj); 764 | processLoop(); 765 | } 766 | } 767 | function getDom(cmdObj) 768 | { 769 | try { 770 | 771 | //validate 772 | var windowObj = getWindowByCommand(cmdObj); 773 | 774 | cmdObj.result.data.dom = encodeURIComponent(windowObj.pjsPage.content); 775 | cmdObj.result.code = 200; 776 | writeReturn(cmdObj); 777 | processLoop(); 778 | 779 | } catch(e) { 780 | cmdObj.result.error.msg = "Failed to get DOM. Error: " + e; 781 | writeReturn(cmdObj); 782 | processLoop(); 783 | } 784 | } 785 | function screenshot(cmdObj) 786 | { 787 | try { 788 | 789 | //validate 790 | var windowObj = getWindowByCommand(cmdObj); 791 | if (typeof cmdObj.cmd.options.imgFormat == 'undefined') { 792 | throw "Image format must be set"; 793 | } else if ( 794 | cmdObj.cmd.options.imgFormat != 'png' 795 | && cmdObj.cmd.options.imgFormat != 'jpeg' 796 | ) { 797 | throw "Invalid image format: " + cmdObj.cmd.options.imgFormat; 798 | } 799 | 800 | cmdObj.result.data.image = windowObj.pjsPage.renderBase64(cmdObj.cmd.options.imgFormat); 801 | cmdObj.result.code = 200; 802 | writeReturn(cmdObj); 803 | processLoop(); 804 | 805 | } catch(e) { 806 | cmdObj.result.error.msg = "Failed to take screenshot. Error: " + e; 807 | writeReturn(cmdObj); 808 | processLoop(); 809 | } 810 | } 811 | function setDebug(cmdObj) 812 | { 813 | try { 814 | //this command does not need a window 815 | 816 | //validate 817 | if (typeof cmdObj.cmd.options.debug == 'undefined') { 818 | throw "debug must be set"; 819 | } else { 820 | if (cmdObj.cmd.options.debug == 1) { 821 | if (typeof cmdObj.cmd.options.debugPath == 'undefined') { 822 | throw "When debug is enabled, a debug path must be set"; 823 | } 824 | } 825 | } 826 | 827 | //configure class 828 | if (cmdObj.cmd.options.debug == 1) { 829 | classData.debug = true; 830 | classData.debugPath = cmdObj.cmd.options.debugPath; 831 | writeDebug(arguments.callee.name, "Debug Enabled"); 832 | 833 | } else { 834 | if (classData.debug === true) { 835 | writeDebug(arguments.callee.name, "Disabling Debug"); 836 | } 837 | classData.debug = false; 838 | classData.debugPath = ""; 839 | } 840 | 841 | //return the command 842 | cmdObj.result.code = 200; 843 | writeReturn(cmdObj); 844 | processLoop(); 845 | 846 | } catch(e) { 847 | cmdObj.result.error.msg = "Failed to set debug. Error: " + e; 848 | writeReturn(cmdObj); 849 | processLoop(); 850 | } 851 | } 852 | function initialize(cmdObj) 853 | { 854 | try { 855 | //this command does not need a window 856 | 857 | //validate 858 | if (typeof cmdObj.cmd.options.stdInPath == 'undefined') { 859 | throw "stdIn path must be set"; 860 | } else { 861 | classData.stdIn.path = cmdObj.cmd.options.stdInPath; 862 | } 863 | 864 | if (typeof cmdObj.cmd.options.terminationSecs != 'undefined') { 865 | //update the termination epoch 866 | terminationEpoch = (getEpoch(false) + parseInt(cmdObj.cmd.options.terminationSecs)); 867 | } 868 | 869 | //return the command 870 | cmdObj.result.PID = system.pid; 871 | cmdObj.result.code = 200; 872 | writeReturn(cmdObj); 873 | 874 | classData.initialized = true; 875 | 876 | } catch(e) { 877 | classData.initialized = false; 878 | var eMsg = "Failed to initialize"; 879 | writeError(e, eMsg); 880 | } 881 | 882 | processLoop(); 883 | } 884 | function terminate(cmdObj) 885 | { 886 | try { 887 | //return the command 888 | if (cmdObj !== null) { 889 | cmdObj.result.code = 200; 890 | writeReturn(cmdObj); 891 | } 892 | setTimeout(function() { 893 | //give time to pickup return 894 | //we are sure to exit successfully now 895 | phantom.exit(1); 896 | }, 4000); 897 | } catch(e) { 898 | phantom.exit(1); 899 | } 900 | } 901 | 902 | 903 | //utilities 904 | function commandWaitForWindowLoad(cmdObj, callBackFunc) 905 | { 906 | try { 907 | var windowObj = getWindowByCommand(cmdObj); 908 | if (windowObj.loading === true) { 909 | cmdObj.result.stats.pageLoadWait += classData.loadWaitInterval; 910 | setTimeout(function() { 911 | commandWaitForWindowLoad(cmdObj, callBackFunc); 912 | }, classData.loadWaitInterval); 913 | } else { 914 | //command ready to trigger 915 | callBackFunc(cmdObj); 916 | if (classData.debug === true) { 917 | if (cmdObj.result.stats.pageLoadWait > 0) { 918 | writeDebug(arguments.callee.name, "Command: " + cmdObj.cmd.name + ", Waited: " + cmdObj.result.stats.pageLoadWait + " for page to finish loading"); 919 | } 920 | } 921 | } 922 | 923 | } catch(e) { 924 | var eMsg = "Failed command wait for load"; 925 | writeError(e, eMsg); 926 | //dont write out the command, timeout in cmdExe() handles that 927 | } 928 | } 929 | function getWindowByCommand(cmdObj) 930 | { 931 | try { 932 | //return an existing window based on the command uuid 933 | //saves a ton of validations in every action function 934 | if (typeof cmdObj.cmd.window == 'undefined') { 935 | throw "Window must be defined in command"; 936 | } else if (typeof cmdObj.cmd.window.UUID == 'undefined') { 937 | throw "Window UUID must be set"; 938 | } 939 | 940 | var windowObj = getWindowByUUID(cmdObj.cmd.window.UUID); 941 | if (windowObj === false) { 942 | windowObj = getNewWindow(cmdObj.cmd.window.UUID); 943 | configurePage(windowObj); 944 | } 945 | 946 | //make sure the window attributes are synced with the command 947 | 948 | //viewport 949 | var changeViewPort = false; 950 | var viewPortWidth = windowObj.pjsPage.viewportSize.width; 951 | var viewPortHeight = windowObj.pjsPage.viewportSize.height; 952 | 953 | if (typeof cmdObj.cmd.window.width != 'undefined') { 954 | if (cmdObj.cmd.window.width != viewPortWidth) { 955 | changeViewPort = true; 956 | viewPortWidth = cmdObj.cmd.window.width; 957 | } 958 | } 959 | if (typeof cmdObj.cmd.window.height != 'undefined') { 960 | if (cmdObj.cmd.window.height != viewPortHeight) { 961 | changeViewPort = true; 962 | viewPortHeight = cmdObj.cmd.window.height; 963 | } 964 | } 965 | if (changeViewPort === true) { 966 | if (classData.debug === true) { 967 | writeDebug(arguments.callee.name, "Changing viewPort from " + windowObj.pjsPage.viewportSize.width + ":" + windowObj.pjsPage.viewportSize.height + " to " + viewPortWidth + ":" + viewPortHeight); 968 | } 969 | windowObj.pjsPage.viewportSize = { width: viewPortWidth, height: viewPortHeight }; 970 | } 971 | 972 | //clipRect 973 | if (typeof cmdObj.cmd.window.raster != 'undefined') { 974 | 975 | var changeRaster = false; 976 | var rasterTop = windowObj.pjsPage.clipRect.top; 977 | var rasterLeft = windowObj.pjsPage.clipRect.left; 978 | var rasterWidth = windowObj.pjsPage.clipRect.width; 979 | var rasterHeight = windowObj.pjsPage.clipRect.height; 980 | 981 | if (typeof cmdObj.cmd.window.raster.top != 'undefined') { 982 | if (cmdObj.cmd.window.raster.top != rasterTop) { 983 | changeRaster = true; 984 | rasterTop = cmdObj.cmd.window.raster.top; 985 | } 986 | } 987 | if (typeof cmdObj.cmd.window.raster.left != 'undefined') { 988 | if (cmdObj.cmd.window.raster.left != rasterLeft) { 989 | changeRaster = true; 990 | rasterLeft = cmdObj.cmd.window.raster.left; 991 | } 992 | } 993 | if (typeof cmdObj.cmd.window.raster.width != 'undefined') { 994 | if (cmdObj.cmd.window.raster.width != rasterWidth) { 995 | changeRaster = true; 996 | rasterWidth = cmdObj.cmd.window.raster.width; 997 | } 998 | } 999 | if (typeof cmdObj.cmd.window.raster.height != 'undefined') { 1000 | if (cmdObj.cmd.window.raster.height != rasterHeight) { 1001 | changeRaster = true; 1002 | rasterHeight = cmdObj.cmd.window.raster.height; 1003 | } 1004 | } 1005 | if (changeRaster === true) { 1006 | if (classData.debug === true) { 1007 | writeDebug(arguments.callee.name, "Changing raster area from " + windowObj.pjsPage.clipRect.top + ":" + windowObj.pjsPage.clipRect.left + ":" + windowObj.pjsPage.clipRect.width + ":" + windowObj.pjsPage.clipRect.height + " to " +rasterTop + ":" + rasterLeft + ":" + rasterWidth + ":" + rasterHeight); 1008 | } 1009 | windowObj.pjsPage.clipRect = { top: rasterTop, left: rasterLeft, width: rasterWidth, height: rasterHeight }; 1010 | } 1011 | } 1012 | 1013 | //load images 1014 | if (typeof cmdObj.cmd.window.loadImages != 'undefined') { 1015 | var curLoadImgs = windowObj.pjsPage.settings.loadImages; 1016 | var cmdLoadImgs = true; 1017 | if (cmdObj.cmd.window.loadImages == 0) { 1018 | cmdLoadImgs = false; 1019 | } 1020 | 1021 | if (cmdLoadImgs != curLoadImgs) { 1022 | windowObj.pjsPage.settings.loadImages = cmdLoadImgs; 1023 | if (classData.debug === true) { 1024 | writeDebug(arguments.callee.name, "Changing loading of images to: " + cmdLoadImgs); 1025 | } 1026 | } 1027 | } 1028 | 1029 | //user agent 1030 | if (typeof cmdObj.cmd.window.userAgent != 'undefined') { 1031 | var curUserAgent = windowObj.pjsPage.settings.userAgent; 1032 | var cmdUserAgent = cmdObj.cmd.window.userAgent; 1033 | 1034 | if (cmdUserAgent != "" && curUserAgent != cmdUserAgent) { 1035 | windowObj.pjsPage.settings.userAgent = cmdUserAgent; 1036 | if (classData.debug === true) { 1037 | writeDebug(arguments.callee.name, "Changing User Agent from:\n" + curUserAgent + "\nto:\n" + cmdUserAgent); 1038 | } 1039 | } 1040 | } 1041 | 1042 | //scrollPosition 1043 | if (typeof cmdObj.cmd.window.scroll != 'undefined') { 1044 | var changeScrollPos = false; 1045 | var scrollTop = windowObj.pjsPage.scrollPosition.top; 1046 | var scrollLeft = windowObj.pjsPage.scrollPosition.left; 1047 | 1048 | if (typeof cmdObj.cmd.window.scroll.top != 'undefined') { 1049 | if (cmdObj.cmd.window.scroll.top != scrollTop) { 1050 | changeScrollPos = true; 1051 | scrollTop = cmdObj.cmd.window.scroll.top; 1052 | } 1053 | } 1054 | if (typeof cmdObj.cmd.window.scroll.left != 'undefined') { 1055 | if (cmdObj.cmd.window.scroll.left != scrollLeft) { 1056 | changeScrollPos = true; 1057 | scrollLeft = cmdObj.cmd.window.scroll.left; 1058 | } 1059 | } 1060 | if (changeScrollPos === true) { 1061 | if (classData.debug === true) { 1062 | writeDebug(arguments.callee.name, "Changing scroll position from " + windowObj.pjsPage.scrollPosition.top + ":" + windowObj.pjsPage.scrollPosition.left + " to " + scrollTop + ":" + scrollLeft); 1063 | } 1064 | windowObj.pjsPage.scrollPosition = { top: scrollTop, left: scrollLeft }; 1065 | } 1066 | } 1067 | 1068 | return windowObj; 1069 | 1070 | } catch(e) { 1071 | var eMsg = "Failed to get window by command"; 1072 | writeError(e, eMsg); 1073 | //dont write out the command, timeout in cmdExe() handles that 1074 | } 1075 | } 1076 | function getWindowByUUID(uuid) 1077 | { 1078 | var winLen = classData.windows.length; 1079 | if (winLen > 0) { 1080 | for (var i=0; i < winLen; i++) { 1081 | var windowObj = classData.windows[i]; 1082 | if (windowObj.uuid == uuid) { 1083 | return windowObj; 1084 | } 1085 | } 1086 | } 1087 | 1088 | //no match 1089 | return false; 1090 | } 1091 | function getNewWindow(uuid) 1092 | { 1093 | var newWindow = {}; 1094 | newWindow.uuid = uuid; 1095 | newWindow.loading = false; 1096 | newWindow.errors = {}; 1097 | newWindow.errors.resource = ""; 1098 | newWindow.errors.general = ""; 1099 | 1100 | newWindow.parent = null; 1101 | newWindow.children = []; 1102 | 1103 | classData.windows.push(newWindow); 1104 | 1105 | return newWindow; 1106 | } 1107 | 1108 | function configurePage(windowObj) 1109 | { 1110 | try { 1111 | 1112 | if (typeof windowObj.pjsPage == 'undefined') { 1113 | windowObj.pjsPage = webpage.create(); 1114 | } 1115 | if (typeof windowObj.pjsPage.uuid == 'undefined') { 1116 | //assign internal UUID 1117 | windowObj.pjsPage.uuid = getUUID(); 1118 | } 1119 | 1120 | //set default functions 1121 | windowObj.pjsPage.onLoadStarted = function() { 1122 | try { 1123 | windowObj.loading = true; 1124 | } catch(e) { 1125 | var eMsg = "Failure in onLoadStarted for UUID: " + windowObj.pjsPage.uuid; 1126 | writeError(e, eMsg); 1127 | } 1128 | }; 1129 | //page completed load 1130 | windowObj.pjsPage.onLoadFinished = function(status) { 1131 | 1132 | try { 1133 | windowObj.loading = false; 1134 | 1135 | //make sure the scroll is corrected, it is not enough that the window is configured 1136 | //if scroll is set and then the page is loaded scroll will not have taken effect 1137 | var scrollTop = windowObj.pjsPage.scrollPosition.top; 1138 | var scrollLeft = windowObj.pjsPage.scrollPosition.left; 1139 | windowObj.pjsPage.scrollPosition = { top: scrollTop, left: scrollLeft }; 1140 | } catch(e) { 1141 | var eMsg = "Failure in onLoadFinished for UUID: " + windowObj.pjsPage.uuid; 1142 | writeError(e, eMsg); 1143 | } 1144 | }; 1145 | 1146 | //error handling 1147 | windowObj.pjsPage.onResourceError = function(resourceError) { 1148 | try { 1149 | windowObj.errors.resource = resourceError.errorString; 1150 | } catch(e) { 1151 | var eMsg = "Failure in onResourceError for UUID: " + windowObj.pjsPage.uuid; 1152 | writeError(e, eMsg); 1153 | } 1154 | }; 1155 | 1156 | windowObj.pjsPage.onError = function(msg, trace) { 1157 | try { 1158 | windowObj.errors.general = msg; 1159 | } catch(e) { 1160 | var eMsg = "Failure in onError for UUID: " + windowObj.pjsPage.uuid; 1161 | writeError(e, eMsg); 1162 | } 1163 | }; 1164 | 1165 | windowObj.pjsPage.onPageCreated = function(childPage) { 1166 | 1167 | try { 1168 | var childUUID = getUUID(); 1169 | 1170 | var childWindowObj = getNewWindow(childUUID); 1171 | childWindowObj.pjsPage = childPage; 1172 | configurePage(childWindowObj); 1173 | 1174 | childWindowObj.parent = windowObj; 1175 | windowObj.children.push(childWindowObj); 1176 | 1177 | if (classData.debug === true) { 1178 | writeDebug(arguments.callee.name, "Page: " + windowObj.uuid + ", conceived a child named: " + childWindowObj.uuid); 1179 | } 1180 | } catch(e) { 1181 | var eMsg = "Failure in onPageCreated for UUID: " + windowObj.pjsPage.uuid; 1182 | writeError(e, eMsg); 1183 | } 1184 | }; 1185 | windowObj.pjsPage.onClosing = function(closingPage) { 1186 | try { 1187 | //need to add logic to remove child objects from parents 1188 | if (classData.debug === true) { 1189 | writeDebug(arguments.callee.name, "Child Closed"); 1190 | } 1191 | } catch(e) { 1192 | var eMsg = "Failure in onClosing for UUID: " + windowObj.pjsPage.uuid; 1193 | writeError(e, eMsg); 1194 | } 1195 | }; 1196 | 1197 | windowObj.pjsPage.onResourceReceived = function(response) { 1198 | 1199 | try { 1200 | 1201 | } catch(e) { 1202 | var eMsg = "Failure in onResourceReceived for UUID: " + windowObj.pjsPage.uuid; 1203 | writeError(e, eMsg); 1204 | } 1205 | }; 1206 | 1207 | //viewport, clipRect, scrollPosition will have default values 1208 | //and they are dictated from control 1209 | 1210 | } catch(e) { 1211 | var eMsg = "Failed to Configure Page"; 1212 | writeError(e, eMsg); 1213 | } 1214 | } 1215 | function getCommand() 1216 | { 1217 | try { 1218 | 1219 | var cmdLen = classData.cmdStack.length; 1220 | 1221 | if (cmdLen == 0) { 1222 | 1223 | var rData = stdInRead(); 1224 | if (rData !== null) { 1225 | var cmdLines = rData.split("cmdStart>>>"); 1226 | 1227 | for (var i=0; i < cmdLines.length; i++) { 1228 | var cmdLine = cmdLines[i].trim(); 1229 | 1230 | if (cmdLine != "") { 1231 | 1232 | var cmdEndPos = cmdLine.indexOf("<< 0) { 1261 | //there are commands pending 1262 | return classData.cmdStack.shift(); 1263 | } else { 1264 | return false; 1265 | } 1266 | 1267 | } catch(e) { 1268 | var eMsg = "Failed to get command"; 1269 | writeError(e, eMsg); 1270 | terminate(null); 1271 | } 1272 | } 1273 | function getUUID() 1274 | { 1275 | function s4() 1276 | { 1277 | return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); 1278 | } 1279 | return s4() + s4() + '_' + s4() + '_' + s4() + '_' + s4() + '_' + s4() + s4() + s4(); 1280 | } 1281 | function getEpoch(mili) 1282 | { 1283 | if (mili === false) { 1284 | return Math.floor((new Date).getTime()/1000); 1285 | } else { 1286 | return (Math.floor((new Date).getTime()) / 1000); 1287 | } 1288 | } 1289 | function forceTermination() 1290 | { 1291 | setTimeout(function() { 1292 | var rTime = (terminationEpoch - getEpoch(false)); 1293 | if (rTime < 0) { 1294 | if (classData.debug === true) { 1295 | writeDebug(arguments.callee.name, "Timeout Terminating"); 1296 | } 1297 | //hard exit, there is no command to send 1298 | phantom.exit(1); 1299 | } else { 1300 | forceTermination(); 1301 | } 1302 | }, 1000); 1303 | } 1304 | 1305 | //Read Write 1306 | 1307 | function stdInRead() 1308 | { 1309 | try { 1310 | 1311 | //stdIn is blocking and will hang the process 1312 | //there is not yet a Async method available, so I have opted 1313 | //to send a delimitor to stdIn, once that delimiter has been 1314 | //reached there is no more data to read. That way we never end up blocked 1315 | 1316 | if (classData.initialized === true) { 1317 | var delimitor = "[" + getUUID() + "]"; 1318 | fSystem.write(classData.stdIn.path, delimitor, 'a'); 1319 | } else { 1320 | //the init command will always end in 2 new lines 1321 | var delimitor = "\n\n"; 1322 | } 1323 | 1324 | var dLen = delimitor.length; 1325 | 1326 | var newData = ""; 1327 | var done = false; 1328 | var i=1; 1329 | while(done === false) { 1330 | i++; 1331 | 1332 | newData += system.stdin.read(1); 1333 | 1334 | if (i > dLen) { 1335 | var dPos = newData.indexOf(delimitor); 1336 | if (dPos != -1) { 1337 | var done = true; 1338 | if (dPos == 0) { 1339 | return null; 1340 | } else { 1341 | return newData.substring(0, dPos); 1342 | } 1343 | } 1344 | } 1345 | } 1346 | 1347 | } catch(e) { 1348 | var eMsg = "Failed to read from stdIn"; 1349 | writeError(e, eMsg); 1350 | } 1351 | } 1352 | function stdOutWrite(data) 1353 | { 1354 | try { 1355 | system.stdout.write(data); 1356 | } catch(e) { 1357 | //hard exit 1358 | phantom.exit(1); 1359 | } 1360 | } 1361 | function stdErrWrite(data) 1362 | { 1363 | try { 1364 | system.stderr.write(data); 1365 | } catch(e) { 1366 | phantom.exit(1); 1367 | } 1368 | } 1369 | function postCmdProcessing(cmdObj) 1370 | { 1371 | try { 1372 | 1373 | var windowObj = getWindowByUUID(cmdObj.cmd.window.UUID); 1374 | if (windowObj !== false) { 1375 | 1376 | //add parent 1377 | if (windowObj.parent !== null) { 1378 | cmdObj.result.parent = windowObj.parent.uuid; 1379 | } 1380 | 1381 | //add children 1382 | var childCount = windowObj.children.length; 1383 | for (var i=0; i < childCount; i++) { 1384 | var childObj = windowObj.children[i]; 1385 | 1386 | var child = {}; 1387 | child.window = {}; 1388 | child.window.uuid = childObj.uuid; 1389 | child.window.width = childObj.pjsPage.viewportSize.width; 1390 | child.window.height = childObj.pjsPage.viewportSize.height; 1391 | 1392 | child.window.raster = {}; 1393 | child.window.raster.top = childObj.pjsPage.clipRect.top; 1394 | child.window.raster.left = childObj.pjsPage.clipRect.left; 1395 | child.window.raster.width = childObj.pjsPage.clipRect.width; 1396 | child.window.raster.height = childObj.pjsPage.clipRect.height; 1397 | 1398 | cmdObj.result.children.push(child); 1399 | } 1400 | } 1401 | 1402 | } catch(e) { 1403 | var eMsg = "Failed to perform post command processing"; 1404 | writeError(e, eMsg); 1405 | } 1406 | } 1407 | function writeReturn(cmdObj) 1408 | { 1409 | try { 1410 | 1411 | postCmdProcessing(cmdObj); 1412 | 1413 | var jsonStr = JSON.stringify(cmdObj); 1414 | var rData = "cmdStartReturn>>>" + btoa(jsonStr) + "<<>>" + getEpoch(true) + " - " + funcName + "\n" + data + "<<> Input Not Path Obj"); 15 | } 16 | } 17 | public function isDirectory($dirObj, $throw=false) 18 | { 19 | $this->isDirectoryObj($dirObj, true); 20 | $isDir = is_dir($dirObj->getPathAsString()); 21 | if ($isDir === true) { 22 | return true; 23 | } else { 24 | if ($throw === false) { 25 | return false; 26 | } else { 27 | throw new \Exception(__METHOD__ . ">> Input Not a Directory"); 28 | } 29 | } 30 | } 31 | public function create($dirObj) 32 | { 33 | $isDir = $this->isDirectory($dirObj); 34 | if ($isDir === false) { 35 | 36 | $dirs = array(); 37 | $dirs[] = $dirObj; 38 | while ($dirObj->getParent() !== null) { 39 | $dirObj = $dirObj->getParent(); 40 | $dirs[] = $dirObj; 41 | } 42 | 43 | $dirs = array_reverse($dirs); 44 | 45 | foreach ($dirs as $dirObj) { 46 | $isDir = $this->isDirectory($dirObj); 47 | 48 | if ($isDir === false) { 49 | $valid = mkdir($dirObj->getPathAsString()); 50 | if ($valid === false) { 51 | throw new \Exception(__METHOD__ . ">> Failed to create directory: ".$dirObj->getPathAsString().""); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | public function delete($dirObj) 58 | { 59 | //should delete recursively files and directories 60 | $isDir = $this->isDirectory($dirObj); 61 | 62 | if ($isDir === true) { 63 | 64 | function removePath($path) { 65 | 66 | $items = scandir($path); 67 | foreach ($items as $item) { 68 | $item = trim($item); 69 | if ($item != "." && $item != "..") { 70 | $nPath = $path . DIRECTORY_SEPARATOR . $item; 71 | $isDir = is_dir($nPath); 72 | if ($isDir === true) { 73 | //recurse 74 | removePath($nPath); 75 | } else { 76 | //delete file 77 | $deleted = @unlink($nPath); 78 | if ($deleted === false) { 79 | throw new \Exception(__METHOD__ . ">> Deleting Directory, Failed to Delete File: " . $nPath); 80 | } 81 | } 82 | } 83 | } 84 | 85 | //directory is now empty, delete it 86 | $deleted = rmdir($path); 87 | if ($deleted === false) { 88 | throw new \Exception(__METHOD__ . ">> Deleting Directory, Failed to Delete Directory: " . $path); 89 | } 90 | } 91 | removePath($dirObj->getPathAsString()); 92 | } 93 | } 94 | public function setMode($dirObj, $mode) 95 | { 96 | $this->isDirectory($dirObj, true); 97 | 98 | $valid = chmod($dirObj->getPathAsString(), $mode); 99 | if ($valid === false) { 100 | throw new \Exception(__METHOD__ . ">> Failed to set mode. Directory name: " . $dirObj->getPathAsString()); 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /MTS/Common/Tools/FileSystems/Files.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Tools/FileSystems/Files.php -------------------------------------------------------------------------------- /MTS/Common/Tools/Time/Epoch.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Common/Tools/Time/Epoch.php -------------------------------------------------------------------------------- /MTS/EnableMTS.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/EnableMTS.php -------------------------------------------------------------------------------- /MTS/Factories.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Factories.php -------------------------------------------------------------------------------- /MTS/Factories/Actions.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Factories/Actions.php -------------------------------------------------------------------------------- /MTS/Factories/Devices.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Factories/Devices.php -------------------------------------------------------------------------------- /MTS/Factories/Files.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Factories/Files.php -------------------------------------------------------------------------------- /MTS/Factories/Time.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/MTS/Factories/Time.php -------------------------------------------------------------------------------- /MTS/WorkDirectory/placeHolder.php: -------------------------------------------------------------------------------- 1 | People should only do interesting work. If it can be automated it should be automated. 10 | 11 | ## The Browser: 12 | Browser Documentation. 13 | 14 | ## The Shell: 15 | Shell Documentation. 16 | 17 | ## The Install: 18 | Installation Documentation. 19 | -------------------------------------------------------------------------------- /SHELL_README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/SHELL_README.md -------------------------------------------------------------------------------- /Tests/Common/Devices/Actions/Host/ApplicationPathsTest.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/Tests/Common/Devices/Actions/Host/ApplicationPathsTest.php -------------------------------------------------------------------------------- /Tests/Common/Devices/Actions/Host/BrowserTest.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/Tests/Common/Devices/Actions/Host/BrowserTest.php -------------------------------------------------------------------------------- /Tests/Common/Devices/Actions/Host/OperatingSystemTest.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/Tests/Common/Devices/Actions/Host/OperatingSystemTest.php -------------------------------------------------------------------------------- /Tests/Common/Devices/Actions/Host/ProcessesTest.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/Tests/Common/Devices/Actions/Host/ProcessesTest.php -------------------------------------------------------------------------------- /Tests/Common/Devices/Actions/Host/ShellTest.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/Tests/Common/Devices/Actions/Host/ShellTest.php -------------------------------------------------------------------------------- /Tests/Common/Devices/Actions/Host/UsersTest.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/Tests/Common/Devices/Actions/Host/UsersTest.php -------------------------------------------------------------------------------- /Tests/Common/Devices/Browsers/PhantomJSTest.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/Tests/Common/Devices/Browsers/PhantomJSTest.php -------------------------------------------------------------------------------- /Tests/Common/Devices/Types/LocalhostTest.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/Tests/Common/Devices/Types/LocalhostTest.php -------------------------------------------------------------------------------- /Tests/Factories/ActionsTest.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/Tests/Factories/ActionsTest.php -------------------------------------------------------------------------------- /Tests/Factories/DevicesTest.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/Tests/Factories/DevicesTest.php -------------------------------------------------------------------------------- /Tests/MtsBootstrap.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/Tests/MtsBootstrap.php -------------------------------------------------------------------------------- /Tests/MtsPhpUnit.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | Factories 10 | 11 | 12 | Common/Devices/Actions/Host 13 | Common/Devices/Types 14 | Common/Devices/Browsers 15 | 16 | 17 | -------------------------------------------------------------------------------- /Tests/MtsUnitTestDevices.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/merlinthemagic/MTS/558425de1552a99e89ed348fae1023a1e597a0e0/Tests/MtsUnitTestDevices.php -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "merlinthemagic/mts", 3 | "description" : "PHP Automation Tools", 4 | "type" : "library", 5 | "homepage" : "https://github.com/merlinthemagic/MTS", 6 | "keywords" : [ 7 | "PHP", 8 | "Automation", 9 | "Shell", 10 | "Browser", 11 | "PhantomJS", 12 | "Bash" 13 | ], 14 | "authors" : [{ 15 | "name" : "Martin Madsen", 16 | "email" : "dev@martinpetermadsen.com", 17 | "role" : "Developer", 18 | "homepage" : "https://github.com/merlinthemagic" 19 | } 20 | ], 21 | "support" : { 22 | "issues" : "https://github.com/merlinthemagic/MTS/issues", 23 | "email" : "github@martinpetermadsen.com" 24 | }, 25 | "require" : { 26 | "php" : ">=5.3.0" 27 | }, 28 | "autoload" : { 29 | "files" : [ 30 | "MTS/EnableMTS.php" 31 | ] 32 | }, 33 | "scripts" : { 34 | "post-install-cmd" : "php vendor/merlinthemagic/mts/MtsSetup.php", 35 | "post-update-cmd" : "php vendor/merlinthemagic/mts/MtsSetup.php", 36 | "post-package-install" : "php vendor/merlinthemagic/mts/MtsSetup.php", 37 | "post-package-update" : "php vendor/merlinthemagic/mts/MtsSetup.php" 38 | } 39 | } --------------------------------------------------------------------------------