├── LICENSE ├── README.md ├── SIGNED.md ├── automation-perms.png ├── install.sh └── rbbedit /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Charlie Garrison 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rbbedit 2 | ======= 3 | 4 | Easily edit server files on your workstation using [BBEdit][]. 5 | 6 | [BBEdit]: http://www.barebones.com/products/bbedit/ 7 | 8 | **Important Update: Fix for macOS "Automation Permissions"** 9 | 10 | A new feature has been added to `rbbedit` to address the issue of missing Automation permissions when running `bbedit` 11 | from an SSH session. Users can now request Automation permissions by passing the `-P` argument to `rbbedit`. This will 12 | display a dialog in BBEdit, prompting the user to grant the necessary permissions for `sshd-keygen-wrapper` to control 13 | BBEdit. The permissions only need to be requested once, to update System Settings. 14 | 15 | Please see the "Requesting Automation Permissions" section below for detailed instructions on how to use this new 16 | feature. 17 | 18 | Unleash the Power of BBEdit for Remote File Editing 19 | ----------------------------------------------------------- 20 | 21 | As a developer or sysadmin, you often find yourself working on remote hosts, connected via SSH. While command-line 22 | editors like vi are available on the server, you miss the rich features and intuitive interface of your favorite text 23 | editor, BBEdit, on your macOS workstation. 24 | 25 | That's where `rbbedit` comes in. This powerful tool bridges the gap between your local BBEdit installation and remote 26 | file editing needs. With `rbbedit`, you can seamlessly open and edit files residing on remote hosts using the full 27 | capabilities of BBEdit, right from your macOS workstation. 28 | 29 | BBEdit is the leading professional HTML and text editor for macOS, crafted to serve the needs of writers, web authors, 30 | and software developers. It provides an abundance of features for editing, searching, and manipulating prose, source 31 | code, and textual data. By leveraging `rbbedit`, you can bring the power of BBEdit to your remote file editing workflow. 32 | 33 | Imagine the convenience of editing Apache configuration files, server-side JavaScript, or web page content with the 34 | familiar and feature-rich interface of BBEdit. No more struggling with command-line editors or transferring files back 35 | and forth. With `rbbedit`, you can efficiently edit remote files as if they were local, thanks to BBEdit's built-in SFTP 36 | support. 37 | 38 | Whether you're tweaking configuration files, debugging scripts, or updating web content, `rbbedit` streamlines your 39 | workflow and boosts your productivity. It allows you to leverage the full potential of BBEdit's editing capabilities 40 | while seamlessly integrating with your remote development environment. 41 | 42 | It's important to note that `rbbedit` is designed to open individual files rather than directories. While BBEdit 43 | supports opening directories, `rbbedit` focuses on providing a smooth experience for editing specific files. 44 | Additionally, `rbbedit` works with real files on disk and does not support reading from standard input (stdin) in its 45 | current version. 46 | 47 | If you frequently find yourself editing files on remote hosts and yearning for the power and convenience of BBEdit, 48 | `rbbedit` is the tool you've been looking for. Embrace the efficiency and familiarity of BBEdit for your remote file 49 | editing needs with `rbbedit`. 50 | 51 | Usage 52 | ----- 53 | 54 | Connect to `server-host` from the BBEdit workstation. 55 | 56 | workstation$ ssh server-host 57 | 58 | Open `filename` in BBEdit on workstation, using default copy method (sftp). 59 | 60 | server-host$ rbbedit filename 61 | 62 | Open `filename` in BBEdit, connecting to workstation as `myuser`. 63 | 64 | server-host$ rbbedit -u myuser filename 65 | 66 | Open `filename` in BBEdit, using scp method. 67 | 68 | server-host$ rbbedit -m scp filename 69 | 70 | Open `filename` in BBEdit, using ExpanDrive with volume named `expandrive-volume`. 71 | 72 | server-host$ rbbedit -x expandrive-volume filename 73 | 74 | Request permission for `sshd-keygen-wrapper` to control BBEdit. 75 | 76 | server-host$ rbbedit -P 77 | 78 | Send SSH public key from `server-host` to workstation. 79 | 80 | server-host$ rbbedit -u myuser -k send 81 | 82 | Get SSH public key from workstation. 83 | 84 | server-host$ rbbedit -u myuser -k get 85 | 86 | 87 | Requirements 88 | ------------ 89 | 90 | #### Local machine 91 | 92 | * `sshd` must be enabled, and if needed, with firewall and port forwarding configured to allow access to BBEdit workstation 93 | * BBEdit command line utilities 94 | * SSH keys for user account *(to avoid entering password repeatedly)* 95 | 96 | #### Remote machine 97 | 98 | * `sh` 99 | * `ssh` 100 | * common unix utilities (such as `basename`, `dirname`, `curl`) 101 | * SSH keys for user account *(to avoid entering password repeatedly)* 102 | 103 | 104 | Installation 105 | ------------ 106 | 107 | #### Server Host 108 | 109 | * Automatic installation with: 110 | * `curl -L https://raw.githubusercontent.com/cngarrison/rbbedit/master/install.sh | bash` 111 | * Or install manually from GitHub: 112 | * `wget https://github.com/cngarrison/rbbedit/raw/master/rbbedit` 113 | * `chmod 755 rbbedit` 114 | * Copy `rbbedit` somewhere in `PATH` 115 | * `cp rbbedit /usr/local/bin/` 116 | * Or clone from GitHub: 117 | * `git clone https://github.com/cngarrison/rbbedit.git` 118 | * `cd rbbedit` 119 | * `./install.sh` 120 | * Create SSH key pair for user account (unless already done) 121 | * `ssh-keygen` 122 | 123 | #### BBEdit workstation 124 | 125 | * Enable SSH (remote login) 126 | * Configure firewall and port forwarding to allow SSH connections 127 | * Copy SSH public key from server(s) to .ssh/authorized_keys (keys can be copied with the `-k` option) 128 | 129 | 130 | Script options 131 | -------------- 132 | 133 | * `-U username`: server-host/SFTP user; specify username to use in hostname argument for SFTP command - will default to $USER 134 | * `-H hostname`: server-host/SFTP host; specify hostname or IP address - will default to $HOSTNAME 135 | * `-u username`: workstation/SSH user; specify username to use in hostname argument for SSH command 136 | * `-h hostname`: workstation/SSH host; specify hostname or IP address, optionally prefix with USER@, eg. myuser@myworkstation.example.com 137 | * `-p port`: workstation/SSH port; connect to SSH using port other than 22 138 | * `-m copy-method`: `sftp | ftp | expan | scp | rsync` - method used to copy files between hosts 139 | * `-x expandrive-volume`: volume name as configured in drives list of ExpanDrive, implies `-m expan` 140 | * `-k copy-direction`: `get | send` - direction to copy the SSH key 141 | * `-i identity-file`: path to identity file if `ssh-copy-id` can't find default public key file 142 | * `-y understood`: confirmation that you understand the implication of the 'copy SSH public key' command *(not required in current version)* 143 | * `-b /path/to/bbedit`: path to `bbedit` on the workstation 144 | * `-v`: verbose 145 | * `-w`: Enable BBEdit wait mode (default) 146 | * `-W`: Disable BBEdit wait mode (only sftp, ftp & expan methods) 147 | * `-P`: Request Automation permissions for `sshd-keygen-wrapper` to control BBEdit 148 | * `-R`: Copy `bbedit-restricted` script to the workstation and display instructions for updating `authorized_keys` 149 | * `-+`: self-update 150 | * `-?`: help usage 151 | 152 | #### User Defaults 153 | 154 | All options specified in the users ~/.rbbedit file will be used as defaults. Options specified on the command line will override user defaults. 155 | 156 | The following options can be set in ~/.rbbedit file. These are the script default values. 157 | 158 | host_sftp_user="" 159 | host_sftp_host="" 160 | 161 | bbedit_ssh_user="" 162 | bbedit_ssh_host="" 163 | bbedit_ssh_port="" 164 | 165 | copy_method="sftp" # scp | expan | rsync | sftp | ftp 166 | expan_volume="" 167 | 168 | bbedit_prog="/usr/local/bin/bbedit" 169 | bbedit_wait_args=" --wait --resume" 170 | bbedit_wait="$bbedit_wait_args" 171 | 172 | local_ssh_copy_id_command="ssh-copy-id" 173 | local_key_identity="" 174 | bbedit_ssh_copy_id_command="/usr/local/bin/ssh-copy-id" 175 | bbedit_key_identity="" 176 | yes_agree="" 177 | 178 | verbose=0 179 | 180 | Set default for username on BBEdit workstation: 181 | 182 | echo 'bbedit_ssh_user="> ~/.rbbedit 183 | 184 | Set default copy method to 'ftp': 185 | 186 | echo 'copy_method="ftp"' >> ~/.rbbedit 187 | 188 | Set path for `bbedit`: 189 | 190 | echo 'bbedit_prog="/usr/bin/bbedit"' >> ~/.rbbedit 191 | 192 | Disable the `--wait` option for `bbedit`: 193 | 194 | echo 'bbedit_wait=""' >> ~/.rbbedit 195 | 196 | 197 | #### Set as EDITOR 198 | 199 | Use rbbedit as your EDITOR, eg: 200 | 201 | export EDITOR="rbbedit -w" 202 | 203 | Some programs that use EDITOR don't expect multiple arguments (& editing fails). The solutions is to create a wrapper script and use that for EDITOR. 204 | 205 | cat << 'EOF' > wait_rbbedit 206 | #!/bin/sh 207 | rbbedit -w "$@" 208 | EOF 209 | 210 | chmod +x wait_rbbedit 211 | mv wait_rbbedit /usr/local/bin/wait_rbbedit 212 | 213 | export EDITOR="wait_rbbedit" 214 | 215 | Copy Methods 216 | ------------ 217 | 218 | The default copy method is 'sftp'. The 'sftp' method will construct an sftp url and use `bbedit` to open the file using BBEdit's built-in SFTP functionality. 219 | 220 | The 'ftp' copy method is almost identical to the 'sftp' method. The difference is that the home directory will be stripped from the filename, under the assumption that the FTP server is chrooting connections. So a relative path for the filename is used, if the first part of the path matches `$HOME`. You can disable the relative path behaviour by setting `ftp_absolute=1` in your `~/.rbbedit` file. 221 | 222 | The 'scp' method uses `scp` to copy each file to edit to the user's `TMPDIR`, then opens the file using `bbedit`. Once the file closes (editing is finished) then `scp` is used to copy file back to the server. 223 | 224 | The 'rsync' method is identical to 'scp' except that `rsync` is used to copy the file each direction. 225 | 226 | The 'expan' method will instruct ExpanDrive on the workstation to mount `expandrive-volume`, and then opens the file using `bbedit`. The script can't get response indicating whether ExpanDrive has finished mounting the volume successfully, other than checking path exists, so we just sleep for 3 seconds and hope for the best. 227 | 228 | All the commands sent from server to the workstation use `ssh`. 229 | 230 | Multiple files can be specified at once. Only one file will opened/edited at a time. 231 | 232 | Only files can be specified for `scp` and `rsync` methods, while the `sftp`, `ftp` and `expan` methods will also open directories. 233 | 234 | 235 | Requesting Automation Permissions 236 | --------------------------------- 237 | 238 | If you encounter the following error when running `rbbedit` from an SSH session: 239 | 240 | ``` 241 | You must allow the application which is running `bbedit` to send events to the BBEdit application. 242 | Please make appropriate changes in your Security & Privacy system preferences, 243 | or contact your terminal/IDE application's developer for assistance. 244 | bbedit: error: -1743 245 | ``` 246 | 247 | You can use the new `-P` argument to request Automation permissions. Run the following command from your SSH session on the server host: 248 | 249 | ``` 250 | rbbedit -P 251 | ``` 252 | 253 | This will request a "display dialog" in BBEdit, the process will request Automation permissions. Click "Granted" to complete the request. The `sshd-keygen-wrapper` can now control BBEdit. 254 | 255 | After granting the permissions, you should see `sshd-keygen-wrapper` listed in the System Settings -> Privacy -> 256 | Automation section, as shown in the screenshot below: 257 | 258 | ![Automation Permissions](automation-perms.png "Automation Permissions") 259 | 260 | Once the permissions are granted, you should be able to run `rbbedit` from your SSH session without encountering the 261 | Automation permissions error. 262 | 263 | 264 | Restricting SSH Access with `bbedit-restricted` 265 | ---------------------------------------------- 266 | 267 | To enhance security, it's recommended to restrict SSH access to limited commands when using `rbbedit`. The 268 | `bbedit-restricted` script helps achieve this by allowing only specific `osascript`, `bbedit`, and `bbresults` commands 269 | to be executed via SSH. 270 | 271 | To set up restricted access: 272 | 273 | 1. Copy the `bbedit-restricted` script to your BBEdit workstation by running the following command from your server: 274 | 275 | ``` 276 | rbbedit -R 277 | ``` 278 | 279 | This will copy the `bbedit-restricted` script to the `/usr/local/bin/` directory on your workstation. 280 | 281 | 2. Update your `~/.ssh/authorized_keys` file on the BBEdit workstation to include the following line: 282 | 283 | ``` 284 | command="/usr/local/bin/bbedit-restricted",no-port-forwarding,no-X11-forwarding ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... user@host 285 | ``` 286 | 287 | Replace `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... user@host` with your actual SSH public key and user@host details. 288 | 289 | This line restricts the SSH access to only execute the `bbedit-restricted` script, which in turn allows only specific 290 | `osascript`, `bbedit`, and `bbresults` commands. 291 | 292 | By using the `bbedit-restricted` script and updating your `authorized_keys` file, you can ensure that SSH access is 293 | limited to the necessary commands required by `rbbedit`, providing an additional layer of security. 294 | 295 | 296 | SSH Key Copy 297 | ------------ 298 | 299 | __WARNING: *Be very sure you understand the implications of copying SSH public keys between hosts.*__ You could easily, and unitentionally, allow user access between hosts for more than just editing files with BBEdit. This key copy feature has been added as a convenience for the `ssh-copy-id` command. Use it only if you understand what is happening. 300 | 301 | The key copy feature depends on the `ssh-copy-id` command. It is not installed by default on OSX systems. I suggest installing the `ssh-copy-id-for-OSX` [version from GitHub](https://github.com/beautifulcode/ssh-copy-id-for-OSX): 302 | 303 | curl -L https://raw.githubusercontent.com/beautifulcode/ssh-copy-id-for-OSX/master/install.sh | sh 304 | 305 | There is also a homebrew version; it has not been tested with `rbbedit`. 306 | 307 | brew install ssh-copy-id 308 | 309 | For easiest use of `rbbedit`, it's best to have the __public__ SSH key on both the BBEdit workstation and the server host. (__Important:__ never share or copy your *private* SSH key.) Passing the `-k` command option with either `get` or `send` will transfer the SSH public key between hosts. Note, `put` works as an alias for `send`. 310 | 311 | The `send` option will transfer the server host SSH public key to the BBEdit workstation. The `get` option will transfer the BBEdit workstation SSH public key to the current user account on the server host. Both options will attempt to use `ssh-copy-id` to tranfer the public key. If the `get` option fails with `ssh-copy-id`, then it will fallback to a simple `cat $bbedit_key_identity >> ~/.ssh/authorized_keys` method. 312 | 313 | If the SSH identity file containing the public key cannot be found, you can specify which file to use with the `-i` option. Default values can also be specified in `~/.rbbedit` with either `local_key_identity` or `bbedit_key_identity` options. In addition, you can specify defaults for the path to the `ssh-copy-id` executable on each host using the `bbedit_ssh_copy_id_command` or `local_ssh_copy_id_command` options. 314 | 315 | [*The `-y` option is not required in current version of `rbbedit`.*] You must specify `-y understood` on the command line to show your understanding of the implications of copying SSH public keys. You can also set `yes_agree="understood"` in the `~/.rbbedit` file. 316 | 317 | The contents of `~/.ssh/authorized_keys` on both BBEdit workstation and server host should be checked after running either the `get` or `send` key copy option. 318 | 319 | Self Updating 320 | ------------ 321 | 322 | Use the `-+` option to update `rbbedit` using the latest version from GitHub. The update is done by downloading the install.sh script and running it. 323 | 324 | Known Issues 325 | ------------ 326 | 327 | No error checking is done for any of the `ssh` commands (or 'copy' commands). 328 | 329 | To allow copying files back to server for the 'scp' and 'rsync' methods, the `bbedit` `--wait` option must be specified. 330 | 331 | Only real (existing) files can be edited; not text passed via STDIN. 332 | 333 | There is no simple/reliable way to determine whether ExpanDrive has finished mounting the remote volume, or whether there was an error. Existence of volume's path could be checked; currently the script just sleeps for 3 seconds before trying to open the file. 334 | 335 | Copying SSH public keys between hosts should make people uncomfortable. This script only uses commonly accepted methods for copying public keys, so the author doesn't believe the script is adding additional security concerns. But neither does the author believe that using this script to simplify copying SSH public keys should absolve the user from understandind the implications of copying SSH public keys. 336 | 337 | Project Home 338 | ------------ 339 | 340 | [rbbedit @GitHub](https://github.com/cngarrison/rbbedit/) 341 | 342 | License 343 | ------- 344 | 345 | [The MIT License (MIT)](http://opensource.org/licenses/MIT) 346 | -------------------------------------------------------------------------------- /SIGNED.md: -------------------------------------------------------------------------------- 1 | ##### Signed by https://keybase.io/cngarrison 2 | ``` 3 | -----BEGIN PGP SIGNATURE----- 4 | Version: GnuPG/MacGPG2 v2.0.20 (Darwin) 5 | Comment: GPGTools - http://gpgtools.org 6 | 7 | iD8DBQBUMoVnIw5BvwqM8ugRAnvoAJ9e8FncrrnhSuMyFZ66FLGqKtE0UACgsTlf 8 | yUtjrXYQWen0sHApHIYLbJ4= 9 | =ynVi 10 | -----END PGP SIGNATURE----- 11 | 12 | ``` 13 | 14 | 15 | 16 | ### Begin signed statement 17 | 18 | #### Expect 19 | 20 | ``` 21 | size exec file contents 22 | ./ 23 | 1083 LICENSE 4178033099aaf70f243ee900b5efdcd63d580d3f06b1b75356786ccedcc4045e 24 | 9348 README.md a6b6c47d4ebca77bd94b9809df2b9bfa4995ea6c746275a9774c9fea0991accd 25 | 1269 install.sh 2586247475de27699b513acb39399ecf69d58b0ad5706c23f490ddafc3397c0a 26 | 12611 x rbbedit d2f48ed034b1fd1a7bbb39f95d68701ee480210d26dbd432b6448ebdf4c20347 27 | ``` 28 | 29 | #### Ignore 30 | 31 | ``` 32 | /SIGNED.md 33 | ``` 34 | 35 | #### Presets 36 | 37 | ``` 38 | git # ignore .git and anything as described by .gitignore files 39 | dropbox # ignore .dropbox-cache and other Dropbox-related files 40 | kb # ignore anything as described by .kbignore files 41 | ``` 42 | 43 | 44 | 45 | ### End signed statement 46 | 47 |
48 | 49 | #### Notes 50 | 51 | With keybase you can sign any directory's contents, whether it's a git repo, 52 | source code distribution, or a personal documents folder. It aims to replace the drudgery of: 53 | 54 | 1. comparing a zipped file to a detached statement 55 | 2. downloading a public key 56 | 3. confirming it is in fact the author's by reviewing public statements they've made, using it 57 | 58 | All in one simple command: 59 | 60 | ```bash 61 | keybase dir verify 62 | ``` 63 | 64 | There are lots of options, including assertions for automating your checks. 65 | 66 | For more info, check out https://keybase.io/docs/command_line/code_signing -------------------------------------------------------------------------------- /automation-perms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cngarrison/rbbedit/5c22d7659b611fe8ee438ef3f3757e97bcc7f5ad/automation-perms.png -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Installs rbbedit into /usr/local/bin 3 | # Installer ideas from https://github.com/beautifulcode/ssh-copy-id-for-OSX 4 | 5 | if [[ $(id -u) != 0 ]]; then 6 | if command -v sudo >/dev/null 2>&1; then 7 | SUDO="sudo" 8 | else 9 | echo >&2 "Requires sudo but it's not installed. Aborting." 10 | exit 1 11 | fi 12 | fi 13 | 14 | if [ ! -z $RBBEDIT_INSTALL_PATH ]; then 15 | rbbedit_path=$RBBEDIT_INSTALL_PATH 16 | else 17 | rbbedit_path="/usr/local/bin/rbbedit" 18 | fi 19 | install_base=`basename $rbbedit_path` 20 | install_path=`dirname $rbbedit_path` 21 | 22 | if git ls-files >& /dev/null && [[ -f rbbedit ]]; then 23 | $SUDO cp rbbedit $rbbedit_path || { echo "Failed to install $install_base into $install_path"; exit 1; } 24 | else 25 | # $SUDO wget --no-check-certificate -O $rbbedit_path https://raw.githubusercontent.com/cngarrison/rbbedit/master/rbbedit || { echo "Failed to install $install_base into $install_path"; exit 1; } 26 | $SUDO curl -L --insecure -o $rbbedit_path https://raw.githubusercontent.com/cngarrison/rbbedit/master/rbbedit || { echo "Failed to install $install_base into $install_path"; exit 1; } 27 | $SUDO chmod +x $rbbedit_path || { echo "Failed to chmod rbbedit at $rbbedit_path"; exit 1; } 28 | fi 29 | echo "Installed $install_base into $install_path"; exit 0; 30 | 31 | 32 | # Local Variables: 33 | # tab-width: 3 34 | # x-auto-expand-tabs: true 35 | # End: 36 | -------------------------------------------------------------------------------- /rbbedit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Created by: Charlie Garrison 4 | # Copyright: Copyright (c) 2014 Charlie Garrison 5 | # All Rights Reserved. 6 | # Created on: 2014-09-01 7 | # Purpose: Script to edit local files on remote workstation using BBEdit 8 | #----------------------------------------------------------------------- 9 | # v0.1 Requires ExpanDrive for remote volume mounting 10 | # v0.2 Parse ARGS using getopts 11 | # v0.3 Multiple copy methods supported, absolute or relative paths, better error handling 12 | # v0.4 Better support for `sh` on different platforms 13 | # v0.5 Command option to send or get SSH keys to/from BBEdit workstation 14 | # v0.6 Installer script and command option to self-update script 15 | # v0.7 Add 'ftp' as a copy method 16 | # v0.8 Command option to set path to `bbedit`; default changed to /usr/local/bin/bbedit 17 | # v1.0 Command option to request Automation permission from `sshd-keygen-wrapper` to BBEdit 18 | # Command option to create a `bbedit-restricted` file for use in `authorized_keys` 19 | #----------------------------------------------------------------------- 20 | 21 | host_sftp_user="" 22 | host_sftp_host="" 23 | bbedit_ssh_user="" 24 | bbedit_ssh_host="" 25 | bbedit_ssh_port="" 26 | local_ssh_copy_id_command="ssh-copy-id" 27 | bbedit_ssh_copy_id_command="/usr/local/bin/ssh-copy-id" 28 | osascript_command="/usr/bin/osascript" 29 | osascript_command_script="tell application \"BBEdit\" 30 | activate 31 | display dialog \"Show me the perms!\" buttons \"Granted\" default button \"Granted\" 32 | end tell" 33 | copy_method="sftp" # scp | expan | rsync | sftp | ftp 34 | ftp_absolute=0 35 | expan_volume="" 36 | bbedit_wait_args=" --wait --resume" 37 | bbedit_wait="$bbedit_wait_args" 38 | bbedit_prog="/usr/local/bin/bbedit" 39 | local_key_identity="" 40 | bbedit_key_identity="" 41 | yes_agree="" 42 | verbose=0 43 | request_perms=0 44 | copy_restricted=0 45 | self_update=0 #don't set this to true in ~/.rbbedit, really, just don't 46 | 47 | # get user defaults, can set user defaults for any of the above settings 48 | if [ -r ~/.rbbedit ] ; then 49 | . ~/.rbbedit 50 | fi 51 | 52 | # Check that at least one argument was specified 53 | if [ $# -lt 1 ] ; then 54 | echo "You must specify at least 1 file name or option." 55 | exit 1 56 | fi 57 | 58 | while getopts u:h:p:m:x:k:i:y:b:wWvPR+? opt 59 | do case "$opt" in 60 | U) host_sftp_user="$OPTARG";; 61 | H) host_sftp_host="$OPTARG";; 62 | u) bbedit_ssh_user="$OPTARG";; 63 | h) bbedit_ssh_host="$OPTARG";; 64 | p) bbedit_ssh_port="$OPTARG";; 65 | m) copy_method="$OPTARG";; 66 | x) expan_volume="$OPTARG" 67 | copy_method="expan";; 68 | k) key_copy="$OPTARG";; 69 | i) local_key_identity="$OPTARG" 70 | bbedit_key_identity="$OPTARG";; 71 | w) bbedit_wait="$bbedit_wait_args";; 72 | W) bbedit_wait="";; 73 | b) bbedit_prog="$OPTARG";; 74 | v) verbose=1;; 75 | P) request_perms=1;; 76 | R) copy_restricted=1;; 77 | y) yes_agree="$OPTARG";; 78 | \+) self_update=1;; 79 | \?) echo >&2 "Usage: $0 [-u ssh-user] [-h ssh-host | ssh-user@ssh-host] [-p ssh-port] [-m copy-method (sftp | ftp | expan | scp | rsync)] [-x expan-volume] [-w|W] file ..." 80 | echo >&2 " $0 [-u ssh-user] [-h ssh-host | ssh-user@ssh-host] [-p ssh-port] [-k copy-direction (get | send)] [-i identity-file] [-y understood] [-P] [-R] ..." 81 | exit 1;; 82 | esac 83 | done 84 | 85 | shift_args=$(( $OPTIND-1 )) 86 | shift $shift_args 87 | edit_files=$@ 88 | 89 | 90 | if [ $self_update -gt 0 ] ; then 91 | echo "Script will update itself using the installer at https://github.com/cngarrison/rbbedit" 92 | 93 | # Write update script 94 | cat > update.sh << EOF 95 | #!/bin/bash 96 | echo "Performing self-update..." 97 | # Use 'curl' to download installer and pipe to 'sh' 98 | export RBBEDIT_INSTALL_PATH="$0" 99 | EOF 100 | if [ $verbose -gt 0 ] ; then 101 | echo "curl -L --insecure -o - https://raw.githubusercontent.com/cngarrison/rbbedit/master/install.sh | bash" >> update.sh 102 | else 103 | echo "curl -L --insecure -o - https://raw.githubusercontent.com/cngarrison/rbbedit/master/install.sh 2>/dev/null | bash" >> update.sh 104 | fi 105 | cat >> update.sh << EOF 106 | echo "Done." 107 | rm \$0 108 | EOF 109 | 110 | echo "Inserting update process..." 111 | exec /bin/bash update.sh 112 | 113 | exit 0; 114 | fi 115 | 116 | # Check that we have copy method 117 | if [ -z "$copy_method" ] ; then 118 | echo "You must specify a copy method." 119 | exit 1 120 | fi 121 | 122 | # Set bbedit_ssh_host from SSH_CLIENT if not set already 123 | if [ -z "$bbedit_ssh_host" ] ; then 124 | bbedit_ssh_host=`echo $SSH_CLIENT | awk '{ print $1}'` 125 | fi 126 | # Set bbedit_ssh_host from `who` if still not set 127 | if [ -z "$bbedit_ssh_host" ] ; then 128 | bbedit_ssh_host=`who -m | awk 'NR==1{ print $5}' | sed 's/[()]//g'` 129 | fi 130 | 131 | # Check that we have ssh host 132 | if [ -z "$bbedit_ssh_host" ] ; then 133 | echo "You must specify a hostname." 134 | exit 1 135 | fi 136 | 137 | # Add bbedit_ssh_user (if set) to bbedit_ssh_host 138 | if [ ! -z "$bbedit_ssh_user" ] ; then 139 | bbedit_ssh_host="$bbedit_ssh_user@$bbedit_ssh_host" 140 | fi 141 | 142 | 143 | bbedit_ssh_port_arg="" 144 | # Check whether we have ssh port 145 | if [ ! -z "$bbedit_ssh_port" ] ; then 146 | bbedit_ssh_port_arg="-p $bbedit_ssh_port" 147 | fi 148 | 149 | # scp and rsync methods require bbedit_wait 150 | if [ $copy_method = "scp" -o $copy_method = "rsync" ] ; then 151 | # if bbedit_wait is already set, then keep supplied value 152 | # otherwise set to default wait args 153 | if [ -z "$bbedit_wait" ] ; then 154 | bbedit_wait="$bbedit_wait_args" 155 | fi 156 | fi 157 | 158 | bbedit_command="$bbedit_prog $bbedit_wait" 159 | bbedit_ssh_command="ssh $bbedit_ssh_port_arg $bbedit_ssh_host " 160 | 161 | if [ $request_perms -gt 0 ] ; then 162 | 163 | ssh_request_perms_command="$bbedit_ssh_command '$osascript_command -e '\''$osascript_command_script'\'''" 164 | 165 | if [ $verbose -gt 0 ] ; then 166 | echo "request-perms command: $ssh_request_perms_command" 167 | fi 168 | 169 | if eval "$ssh_request_perms_command"; then 170 | echo "Permissions granted on BBEdit workstation." 171 | exit 0 172 | else 173 | echo "Unknown error requesting permissions" 174 | exit 1 175 | fi 176 | fi 177 | 178 | 179 | if [ $copy_restricted -gt 0 ] ; then 180 | echo "Copying bbedit-restricted script to the workstation..." 181 | 182 | # Write bbedit-restricted script to a temporary file 183 | cat > "/tmp/bbedit-restricted.sh" << 'EOF' 184 | #!/opt/homebrew/bin/bash 185 | 186 | if [[ -n $SSH_ORIGINAL_COMMAND ]]; then # command given, so run it 187 | if [[ $SSH_ORIGINAL_COMMAND == "/usr/bin/osascript -e 'tell application \"BBEdit\""* ]]; then 188 | # Allow the specific osascript command with the given arguments 189 | exec /bin/sh -c "$SSH_ORIGINAL_COMMAND" 190 | elif [[ $SSH_ORIGINAL_COMMAND == */bbedit* || $SSH_ORIGINAL_COMMAND == */bbresults* ]]; then 191 | # Allow bbedit and bbresults commands and execute them 192 | exec /bin/sh -c "$SSH_ORIGINAL_COMMAND" 193 | else 194 | # Reject all other commands 195 | echo "Only the specific osascript command, bbedit, and bbresults commands are supported: 196 | $SSH_ORIGINAL_COMMAND" 197 | fi 198 | else # no command, so ignore 199 | echo "Only the specific osascript command, bbedit, and bbresults commands are supported" 200 | fi 201 | EOF 202 | 203 | bbedit_scp_port_arg="" 204 | if [ ! -z "$bbedit_ssh_port" ] ; then 205 | bbedit_scp_port_arg="-P $bbedit_ssh_port" 206 | fi 207 | bbedit_scp_command="scp $bbedit_scp_port_arg -p -q" 208 | 209 | if [ $verbose -gt 0 ] ; then 210 | echo "SCP command: $bbedit_scp_command" 211 | echo "Local file: /tmp/bbedit-restricted.sh" 212 | echo "Copy to: $bbedit_ssh_host:/usr/local/bin/bbedit-restricted" 213 | fi 214 | 215 | # Copy bbedit-restricted script to the workstation 216 | $bbedit_scp_command "/tmp/bbedit-restricted.sh" $bbedit_ssh_host:/usr/local/bin/bbedit-restricted 217 | 218 | # Remove the temporary file 219 | rm "/tmp/bbedit-restricted.sh" 220 | 221 | echo "bbedit-restricted script copied to the workstation at: /usr/local/bin/bbedit-restricted." 222 | echo "Please add (or modify) the following line to your ~/.ssh/authorized_keys file on the workstation:" 223 | echo "command=\"/usr/local/bin/bbedit-restricted\",no-port-forwarding,no-X11-forwarding ssh-rsa 224 | AAAAB3NzaC1yc2EAAAADAQC... user@host" 225 | exit 0 226 | fi 227 | 228 | 229 | if [ ! -z "$key_copy" ] ; then 230 | if [ "$yes_agree" != 'understood' ] ; then 231 | echo "Please be aware of the implications of copying SSH public keys. Read the README at https://github.com/cngarrison/rbbedit" 232 | # echo "You must give your undestanding of the implications of copying SSH public keys. Read the README at https://github.com/cngarrison/rbbedit" 233 | # exit 1 234 | fi 235 | 236 | ssh_copy_id_command="" 237 | if [ $key_copy = "send" -o $key_copy = "put" ]; then 238 | 239 | key_identity_arg="" 240 | if [ ! -z "$local_key_identity" ] ; then 241 | key_identity_arg="-i $local_key_identity" 242 | fi 243 | 244 | if [ ! -z "$bbedit_ssh_port_arg" ] ; then 245 | ssh_copy_id_command="$local_ssh_copy_id_command $key_identity_arg $bbedit_ssh_host $bbedit_ssh_port_arg" 246 | else 247 | ssh_copy_id_command="$local_ssh_copy_id_command $key_identity_arg $bbedit_ssh_host" 248 | fi 249 | 250 | if [ $verbose -gt 0 ] ; then 251 | echo "Key Copy Direction: $key_copy" 252 | echo "Local Key Identity: $local_key_identity" 253 | echo "ssh-copy-id command: $ssh_copy_id_command" 254 | fi 255 | 256 | if ${ssh_copy_id_command}; then 257 | echo "Key copied. Be sure to check contents of ~/.ssh/authorized_keys on BBEdit workstation." 258 | echo "You can restrict ssh access to only rbbedit - run `rbbedit -R` to copy the `bbedit-restricted` command" 259 | echo "to the BBEdit workstation, and then modify ~/.ssh/authorized_keys as instructed." 260 | exit 0 261 | else 262 | echo "Unknown error copying key" 263 | exit 1 264 | fi 265 | 266 | 267 | elif [ $key_copy = "get" ]; then 268 | 269 | key_identity_arg="" 270 | if [ ! -z "$bbedit_key_identity" ] ; then 271 | key_identity_arg="-i $bbedit_key_identity" 272 | fi 273 | 274 | if [ -z "$HOSTNAME" ] ; then 275 | HOSTNAME=`hostname` 276 | fi 277 | 278 | ssh_copy_id_command="$bbedit_ssh_command $bbedit_ssh_copy_id_command $key_identity_arg $USER@$HOSTNAME" 279 | 280 | if [ $verbose -gt 0 ] ; then 281 | echo "Key Copy Direction: $key_copy" 282 | echo "Workstation Key Identity: $bbedit_key_identity" 283 | echo "ssh-copy-id command: $ssh_copy_id_command" 284 | fi 285 | 286 | if ${ssh_copy_id_command}; then 287 | echo "Key copied. Be sure to check contents of local ~/.ssh/authorized_keys on server host." 288 | exit 0 289 | else 290 | echo "Unknown error copying key, trying fallback method" 291 | 292 | if [ -z "$bbedit_key_identity" ] ; then 293 | bbedit_key_identity="~/.ssh/id_rsa.pub" 294 | fi 295 | ssh_copy_id_command="$bbedit_ssh_command cat $bbedit_key_identity" 296 | 297 | if [ $verbose -gt 0 ] ; then 298 | echo "Workstation Key Identity: $bbedit_key_identity" 299 | echo "ssh-copy-id command: $ssh_copy_id_command >> ~/.ssh/authorized_keys" 300 | fi 301 | 302 | bbedit_ssh_key=`$ssh_copy_id_command` 303 | echo $bbedit_ssh_key >> ~/.ssh/authorized_keys 304 | 305 | if [ ! -z "$bbedit_ssh_key" ]; then 306 | echo "Key copied. Be sure to check contents of local ~/.ssh/authorized_keys on server host." 307 | exit 0 308 | else 309 | echo "Unknown error copying key" 310 | exit 1 311 | fi 312 | fi 313 | 314 | 315 | else 316 | echo "Unknown key copy direction: $key_copy - must be either 'send' or 'get'" 317 | exit 1 318 | 319 | fi 320 | fi 321 | 322 | if [ $verbose -gt 0 ] ; then 323 | echo "Method: $copy_method" 324 | echo "SSH Command: $bbedit_ssh_command" 325 | echo "BBEdit Command: $bbedit_command" 326 | echo "Files: $edit_files" 327 | fi 328 | 329 | 330 | if [ $copy_method = "expan" ]; then 331 | 332 | # Check that we have ssh host 333 | if [ -z "$expan_volume" ] ; then 334 | echo "You must specify an expandrive volume." 335 | exit 1 336 | fi 337 | if [ $verbose -gt 0 ] ; then 338 | echo "ExpanDrive Volume: $expan_volume" 339 | fi 340 | 341 | $bbedit_ssh_command /usr/local/bin/expan connect $expan_volume 2>/dev/null 342 | 343 | ## wait for volume mount to finish (HACK!!) 344 | sleep 3 345 | 346 | for i in $edit_files 347 | do 348 | abs_path=`readlink -n -f $i` 349 | 350 | if [ ! -r "$abs_path" ] ; then 351 | echo "File/dir not found, or is not readable: $abs_path" 352 | 353 | else 354 | if [ $verbose -gt 0 ] ; then 355 | echo "Editing file: $abs_path using /Volumes/$expan_volume/$abs_path" 356 | fi 357 | 358 | $bbedit_ssh_command $bbedit_command /Volumes/$expan_volume/$abs_path 359 | 360 | fi 361 | 362 | done 363 | 364 | elif [ $copy_method = "sftp" ]; then 365 | 366 | for i in $edit_files 367 | do 368 | abs_path=`readlink -n -f $i` 369 | 370 | if [ ! -r "$abs_path" ] ; then 371 | echo "File/dir not found, or is not readable: $abs_path" 372 | 373 | else 374 | 375 | if [ -z "$host_sftp_user" ] ; then 376 | host_sftp_user=$USER 377 | fi 378 | 379 | if [ -z "$host_sftp_host" ] ; then 380 | if [ -z "$HOSTNAME" ] ; then 381 | host_sftp_host=`hostname` 382 | else 383 | host_sftp_host=$HOSTNAME 384 | fi 385 | fi 386 | 387 | if [ -d "$abs_path" ] ; then 388 | abs_path="$abs_path/" 389 | fi 390 | 391 | if [ $verbose -gt 0 ] ; then 392 | echo "Editing file: $abs_path using sftp://$host_sftp_user@$host_sftp_host/$abs_path" 393 | fi 394 | 395 | $bbedit_ssh_command $bbedit_command "sftp://$host_sftp_user@$host_sftp_host/$abs_path" 396 | fi 397 | 398 | done 399 | 400 | elif [ $copy_method = "ftp" ]; then 401 | 402 | for i in $edit_files 403 | do 404 | abs_path=`readlink -n -f $i` 405 | 406 | if [ ! -r "$abs_path" ] ; then 407 | echo "File/dir not found, or is not readable: $abs_path" 408 | 409 | else 410 | if [ -z "$HOSTNAME" ] ; then 411 | HOSTNAME=`hostname` 412 | fi 413 | 414 | if [ -d "$abs_path" ] ; then 415 | abs_path="$abs_path/" 416 | fi 417 | 418 | if [ $ftp_absolute -eq 0 ]; then 419 | ## assume ftp server has chrooted to user's HOME dir 420 | ## so strip $HOME/ from beginning of path 421 | rel_path=${abs_path#$HOME/} 422 | else 423 | rel_path=$abs_path 424 | fi 425 | 426 | if [ $verbose -gt 0 ] ; then 427 | echo "Editing file: $abs_path using ftp://$USER@$HOSTNAME/$rel_path" 428 | fi 429 | 430 | $bbedit_ssh_command $bbedit_command "ftp://$USER@$HOSTNAME/$rel_path" 431 | fi 432 | 433 | done 434 | 435 | elif [ $copy_method = "scp" ]; then 436 | 437 | bbedit_tmp_dir=`$bbedit_ssh_command echo '$TMPDIR'` 438 | #bbedit_tmp_dir=`$bbedit_ssh_command /usr/bin/getconf DARWIN_USER_CACHE_DIR` 439 | if [ $verbose -gt 0 ] ; then 440 | echo "TMP dir: $bbedit_tmp_dir" 441 | fi 442 | 443 | bbedit_scp_port_arg="" 444 | if [ ! -z "$bbedit_ssh_port" ] ; then 445 | bbedit_scp_port_arg="-P $bbedit_ssh_port" 446 | fi 447 | bbedit_scp_command="scp $bbedit_scp_port_arg -p -q" 448 | 449 | for i in $edit_files 450 | do 451 | abs_path=`readlink -n -f $i` 452 | filename=`basename $abs_path` 453 | 454 | ## it should be fine working with directories, but there is non-trivial risk when copying data back 455 | ## so let's only support editing of single files with this version 456 | if [ ! -f "$abs_path" -o ! -r "$abs_path" ] ; then 457 | echo "File not found, or is not readable: $abs_path" 458 | 459 | else 460 | if [ $verbose -gt 0 ] ; then 461 | echo "Editing file: $abs_path using $bbedit_tmp_dir/$filename" 462 | fi 463 | 464 | $bbedit_scp_command $abs_path $bbedit_ssh_host:$bbedit_tmp_dir/$filename 465 | 466 | $bbedit_ssh_command $bbedit_command $bbedit_tmp_dir/$filename 467 | 468 | $bbedit_scp_command $bbedit_ssh_host:$bbedit_tmp_dir/$filename $abs_path 469 | fi 470 | 471 | done 472 | 473 | elif [ $copy_method = "rsync" ]; then 474 | 475 | bbedit_tmp_dir=`$bbedit_ssh_command echo '$TMPDIR'` 476 | #bbedit_tmp_dir=`$bbedit_ssh_command /usr/bin/getconf DARWIN_USER_CACHE_DIR` 477 | if [ $verbose -gt 0 ] ; then 478 | echo "TMP dir: $bbedit_tmp_dir" 479 | fi 480 | 481 | bbedit_rsync_port_arg="" 482 | if [ ! -z "$bbedit_ssh_port" ] ; then 483 | bbedit_rsync_port_arg="--port $bbedit_ssh_port" 484 | fi 485 | bbedit_rsync_command="rsync $bbedit_rsync_port_arg -pt -q --rsh=ssh" 486 | 487 | for i in $edit_files 488 | do 489 | abs_path=`readlink -n -f $i` 490 | filename=`basename $abs_path` 491 | 492 | ## it should be fine working with directories, but there is non-trivial risk when copying data back 493 | ## so let's only support editing of single files with this version 494 | if [ ! -f "$abs_path" -o ! -r "$abs_path" ] ; then 495 | echo "File not found, or is not readable: $abs_path" 496 | 497 | else 498 | if [ $verbose -gt 0 ] ; then 499 | echo "Editing file: $abs_path using $bbedit_tmp_dir/$filename" 500 | fi 501 | 502 | $bbedit_rsync_command $abs_path $bbedit_ssh_host:$bbedit_tmp_dir/$filename 503 | 504 | $bbedit_ssh_command $bbedit_command $bbedit_tmp_dir/$filename 505 | 506 | $bbedit_rsync_command $bbedit_ssh_host:$bbedit_tmp_dir/$filename $abs_path 507 | fi 508 | 509 | done 510 | 511 | else 512 | echo "Unknown copy method: $copy_method" 513 | exit 1 514 | 515 | fi 516 | 517 | 518 | # Local Variables: 519 | # tab-width: 3 520 | # x-auto-expand-tabs: true 521 | # End: 522 | --------------------------------------------------------------------------------