PageRenderTime 23ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/Support/lib/helper_functions.php

https://github.com/craigulliott/FTP-SSH.tmbundle
PHP | 317 lines | 206 code | 38 blank | 73 comment | 18 complexity | c67572fad2f80bab733fe935f46cc32b MD5 | raw file
  1. <?php
  2. /**
  3. * Helper functions for the TextMate bundle "FTP/SSH"
  4. * Version 2.3, 2008-05-13
  5. * Version 2.5, 2011-01-20 Fixed for Snow leopard, buggy nib file, problem of filenames with spaces and uses a pipe for transmission of the plist xml
  6. *
  7. * @author Bernhard Fürst
  8. * @modifier Alexandre van 't Westende
  9. */
  10. // start initialisation of variables and connection parameters
  11. // More readable flag for stickieness of error dialogs
  12. define('STICKY', TRUE);
  13. // Initialize variables
  14. $PROJECT_DIR = dirname(array_key_exists('TM_PROJECT_FILEPATH', $_SERVER) ? $_SERVER['TM_PROJECT_FILEPATH'] : $_ENV['TM_PROJECT_FILEPATH']);
  15. $PREFS_FILE = (empty($PROJECT_DIR) ? (array_key_exists('TM_DIRECTORY', $_SERVER) ? $_SERVER['TM_DIRECTORY'] : $_ENV['TM_DIRECTORY']) : $PROJECT_DIR).'/.ftpssh_settings';
  16. $PREFS = array();
  17. // Load Settings from the project directory
  18. $PREFS = load_settings_file($PREFS_FILE);
  19. // Show Settings Dialog if no Settings found
  20. if(empty($PREFS)) {
  21. $PREFS = settings_dialog($PREFS_FILE);
  22. }
  23. // end of initialisation
  24. /**
  25. * Get a file from a remote host
  26. *
  27. * @param string $TM_FILENAME
  28. * @param string $TM_DIRECTORY
  29. * @param string $PROJECT_DIR
  30. * @param string $PREFS
  31. * @return void
  32. * @author Bernhard Fürst
  33. */
  34. function get_file($TM_FILENAME, $TM_FILEPATH, $TM_DIRECTORY, $PROJECT_DIR, $PREFS)
  35. {
  36. // Get path of current file relative to the $PROJECT_DIR
  37. $relative_dir = get_relative_dir($TM_DIRECTORY, $PROJECT_DIR);
  38. if(0 == strcasecmp('ftp', $PREFS['protocol'])) {
  39. // The remote path must be encoded by rawurlencode() (RFC 1738)
  40. $path = rawurlencode($PREFS['path'].$relative_dir.$TM_FILENAME);
  41. // FTP command for uploading current file
  42. // The slash between the host and the path is a separator and not part of the remote path
  43. $command = '/usr/bin/ftp -V '.(empty($PREFS['cli_options'])?'':$PREFS['cli_options']).' '.
  44. '-o '.escapeshellarg($TM_FILEPATH).' '.
  45. escapeshellarg('ftp://'.$PREFS['user'].':'.$PREFS['password'].'@'.$PREFS['host'].'/'.$path);
  46. }
  47. elseif(0 == strcasecmp('ssh', $PREFS['protocol'])) {
  48. // Escape spaces. That's essential for the remote SCP path. Simply quoting does not work.
  49. $path = str_replace(' ', '\ ', $PREFS['path'].$relative_dir.$TM_FILENAME);
  50. // SCP command for uploading current file
  51. $command = '/usr/bin/scp '.(empty($PREFS['cli_options'])?'':$PREFS['cli_options']).' '.
  52. escapeshellarg($PREFS['user'].'@'.$PREFS['host']).':'.escapeshellarg($path).' '.escapeshellarg($TM_FILEPATH);
  53. }
  54. else {
  55. notify('Protocol "'.$PREFS['protocol'].'" not known. Please check your remote settings.');
  56. exit();
  57. }
  58. // Execute escaped (for security reasons) command
  59. $command .= ' 2>&1';
  60. $result = shell_exec($command);
  61. // Error occured
  62. if (!empty($result)) {
  63. notify('Error ('.$PREFS['protocol'].'): '.$result."\nCommand being used:\n".$command, TRUE);
  64. return;
  65. }
  66. // Upload sucessful
  67. notify('Reloaded "'.array_key_exists('TM_FILENAME', $_SERVER) ? $_SERVER['TM_FILENAME'] : $_ENV['TM_FILENAME'].'"');
  68. // reload current document
  69. // see "rescan_project" in TextMate.app/Contents/SharedSupport/Support/lib/bash_init.sh
  70. shell_exec('osascript &>/dev/null -e \'tell app "SystemUIServer" to activate\' -e \'tell app "TextMate" to activate\'');
  71. return;
  72. }
  73. /**
  74. * Put a file on a remote host
  75. *
  76. * @param string $TM_FILENAME
  77. * @param string $TM_DIRECTORY
  78. * @param string $PROJECT_DIR
  79. * @param string $PREFS
  80. * @return void
  81. * @author Bernhard Fürst
  82. */
  83. function put_file($TM_FILENAME, $TM_FILEPATH, $TM_DIRECTORY, $PROJECT_DIR, $PREFS) {
  84. // Get path of current file relative to the $PROJECT_DIR
  85. $relative_dir = get_relative_dir($TM_DIRECTORY, $PROJECT_DIR);
  86. if(0 == strcasecmp('ftp', $PREFS['protocol'])) {
  87. // Encoded by rawurlencode() (RFC 1738) does not work here. May be a bug in the ftp client?
  88. // Escaping spaces not nessecary because of escapeshellarg() below
  89. $path = $PREFS['path'].$relative_dir.$TM_FILENAME;
  90. // FTP command for uploading current file
  91. $command = '/usr/bin/ftp -V '.(empty($PREFS['cli_options'])?'':$PREFS['cli_options']).' -u '.
  92. escapeshellarg('ftp://'.$PREFS['user'].':'.$PREFS['password'].'@'.$PREFS['host'].'/'.$path).' '.escapeshellarg($TM_FILEPATH);
  93. }
  94. elseif(0 == strcasecmp('ssh', $PREFS['protocol'])) {
  95. // Escape spaces. That's essential for the remote SCP path. Simply quoting does not work.
  96. $path = str_replace(' ', '\ ', $PREFS['path'].$relative_dir);
  97. // Remote path must be quoted
  98. $command = '/usr/bin/scp '.(empty($PREFS['cli_options'])?'':$PREFS['cli_options']).
  99. ' '.escapeshellarg($TM_FILEPATH).' '.escapeshellarg($PREFS['user'].'@'.$PREFS['host']).':'.escapeshellarg($path).'';
  100. }
  101. else {
  102. notify('Protocol "'.$PREFS['protocol'].'" not found. Please check your .ftpssh_settings file.', STICKY);
  103. exit();
  104. }
  105. // Execute escaped (for security reasons) command
  106. $command .= ' 2>&1';
  107. $result = shell_exec($command);
  108. // Error occured
  109. if (!empty($result)) {
  110. notify('Error ('.$PREFS['protocol'].'): '.$result."\nCommand being used:\n".$command, TRUE);
  111. return;
  112. }
  113. // Upload sucessful
  114. notify('Uploaded "'.array_key_exists('TM_FILENAME', $_SERVER) ? $_SERVER['TM_FILENAME'] : $_ENV['TM_FILENAME'].'"');
  115. return;
  116. }
  117. /**
  118. * Echo a message using Growl if available or just by the echo command
  119. *
  120. * @param string $message The message text
  121. * @param boolean $sticky Growl: Make the message sticky on the screen. Use this for error mssages
  122. * @return void
  123. * @author Bernhard Fürst
  124. */
  125. function notify($message, $sticky = false) {
  126. $sticky = ($sticky) ? '-s' : '';
  127. // Use GROWL for notifying
  128. if(file_exists('/usr/local/bin/growlnotify')) {
  129. shell_exec('/usr/local/bin/growlnotify '.$sticky.' -a TextMate.app -m '.escapeshellarg($message).' FTP/SSH Bundle'.($sticky?' Error':''));
  130. return;
  131. }
  132. // Just use echo
  133. echo $message;
  134. }
  135. function settings_dialog($PREFS_FILE, $prefs='') {
  136. // default settings
  137. $plist = '<plist version="1.0"><dict><key>protocol</key><string>ftp</string></dict></plist>';
  138. // Show current remote settings if any
  139. if (is_array($prefs)) {
  140. $plist = '<plist version="1.0"><dict>';
  141. foreach($prefs as $key => $value) {
  142. $plist .= '<key>'.$key.'</key><string>'.$value.'</string>';
  143. }
  144. $plist .= '</dict></plist>';
  145. }
  146. // v2.5: 2011-01-20 Fixed for Snow leopard, buggy nib file, problem of filenames with spaces and uses a pipe for transmission of the plist xml
  147. // open a 2 way process
  148. $process = proc_open('"$DIALOG" -cm "$TM_BUNDLE_SUPPORT/nibs/FTP_SSH Settings.nib"', array(array("pipe", "r"), array("pipe", "w")), $pipes);
  149. if (is_resource($process)) {
  150. fwrite($pipes[0], $plist); // stdin: send the xml plist
  151. fclose($pipes[0]);
  152. $result = stream_get_contents($pipes[1]); // stdout: read the result
  153. fclose($pipes[1]);
  154. proc_close($process); // close the process
  155. }
  156. else {
  157. notify("Problem dealing with the .nib file.");
  158. return false;
  159. }
  160. // Make array from $result
  161. $result = parse_plist($result);
  162. // User caneled the Settings dialog, full stop here
  163. if(empty($result['save'])) {
  164. exit();
  165. }
  166. // remove the "save" key because we don't need to save it to the settings file
  167. $result = clean_settings($result);
  168. // Trailer for settings file (don't forget closing \n !)
  169. $settings = "; Preferences for the FTP/SSH Bundle for TextMate\n; See http://internalaffairs.fuerstnet.de/ftp-ssh-bundle-textmate\n; (c) 2007 Bernhard Fürst <bernhard@fuersten.info>\n; Warning: Content of this file will be overwritten when using the\n; \"Remote Connection Settings...\" command of FTP/SSH Bundle\n";
  170. // Get values
  171. foreach($result as $key => $value) {
  172. $settings .= $key.'="'.$value."\"\n";
  173. }
  174. // TODO: check values
  175. // Save settings to .ftpssh_settings
  176. if (!$handle = fopen($PREFS_FILE, 'w')) {
  177. notify("Cannot open or create settings file.", STICKY);
  178. return false;
  179. }
  180. // Write $settings to our opened file.
  181. if (fwrite($handle, $settings) === FALSE) {
  182. notify("Cannot write to the settings file.", STICKY);
  183. return false;
  184. }
  185. fclose($handle);
  186. // Change mode to owner r/w, group and others no rights
  187. chmod($PREFS_FILE, 0600);
  188. // Finally load the settings
  189. $PREFS = load_settings_file($PREFS_FILE);
  190. return $PREFS;
  191. }
  192. /**
  193. * Little dirty parser for key/value pairs in plist's
  194. *
  195. * @param string $plist
  196. * @return array $result
  197. * @author Bernhard Fürst
  198. */
  199. function parse_plist($plist) {
  200. // Check for key/value pairs
  201. $matches = array();
  202. preg_match_all('@<key>([^<]+)</key>\s*<(string|integer)>([^<]+)</(string|integer)>@', $plist, $matches);
  203. // transform to an array(key => value)
  204. // keys are in $matches[1], values in $matches[3]
  205. $result = array();
  206. while (list($k, $key) = each($matches[1])) {
  207. $result[$key] = $matches[3][$k];
  208. }
  209. // Finally check for the key "returnArgument" which will indicate the user clicked the "Save" button
  210. if(preg_match('@<key>returnArgument</key>@', $plist)) {
  211. $result['save'] = true;
  212. }
  213. return $result;
  214. }
  215. function load_settings_file($PREFS_FILE) {
  216. // check if settings file exists
  217. if(!file_exists($PREFS_FILE)) {
  218. notify("Remote settings file not found.");
  219. return array();
  220. }
  221. // Load settings
  222. $PREFS = parse_ini_file($PREFS_FILE);
  223. if(empty($PREFS)) {
  224. notify("Remote settings file is empty or invalide.");
  225. return array();
  226. }
  227. // Clean settings from unwanted parameters
  228. $PREFS = clean_settings($PREFS);
  229. // Append trailing slash to the path if it is not empty
  230. $PREFS['path'] = (!empty($PREFS['path']) AND !preg_match('|/$|', $PREFS['path'] )) ? $PREFS['path'].'/' : $PREFS['path'];
  231. return $PREFS;
  232. }
  233. function clean_settings($settings) {
  234. $allowed_keys = array('cli_options','host','password','protocol','user', 'path');
  235. // remove all unwanted keys from the settings
  236. foreach($settings as $key => $value) {
  237. if(!in_array($key, $allowed_keys)) {
  238. unset($settings[$key]);
  239. }
  240. }
  241. // return clean settings
  242. return $settings;
  243. }
  244. function get_relative_dir($TM_DIRECTORY, $PROJECT_DIR) {
  245. // If no TM Project is used there is no chance to get any relative dir
  246. if (empty($PROJECT_DIR)) {
  247. return '';
  248. }
  249. // Get path of current file relative to the $PROJECT_DIR
  250. $relative_dir = substr($TM_DIRECTORY, strlen($PROJECT_DIR));
  251. $relative_dir = ltrim($relative_dir, '/');
  252. // Append trailing slash only if $relative_dir is not empty
  253. $relative_dir = '' === $relative_dir ? '' : $relative_dir.'/';
  254. return $relative_dir;
  255. }