The slftool thread

djdavetrouble
Contributor III

So, el capitan has a new binary called sfltool. It has been discussed a few times, and I have incorporated it into my firstrun script to add items to the sidebar. I think many more uses for this will be discovered.
Here is my script as an example.

#!/bin/bash
# Get the Username of the currently logged user
loggedInUser=`/bin/ls -l /dev/console | /usr/bin/awk '{ print $3 }'`
if [ -e /usr/local/bin/mysides ]
then
su - "$loggedInUser" -c "/usr/local/bin/mysides remove All My Files file:///System/Library/CoreServices/Finder.app/Contents/Resources/MyLibraries/myDocuments.cannedSearch/" && sleep 2
su - "$loggedInUser" -c "/usr/local/bin/mysides remove iCloud x-apple-finder:icloud" && sleep 2
su - "$loggedInUser" -c "/usr/local/bin/mysides remove domain-AirDrop nwnode://domain-AirDrop" && sleep 2
/usr/bin/sfltool add-item com.apple.LSSharedFileList.FavoriteItems file:///Users/$loggedInUser && sleep 2
/usr/bin/sfltool add-item com.apple.LSSharedFileList.FavoriteItems file:///Users/$loggedInUser/Desktop && sleep 2
/usr/bin/sfltool add-item com.apple.LSSharedFileList.FavoriteItems file:///Users/$loggedInUser/Documents && sleep 2
/usr/bin/sfltool add-item com.apple.LSSharedFileList.FavoriteItems file:///Users/$loggedInUser/Downloads && sleep 2
/usr/bin/sfltool add-item com.apple.LSSharedFileList.FavoriteItems file:///Users/$loggedInUser/Movies && sleep 2
/usr/bin/sfltool add-item com.apple.LSSharedFileList.FavoriteItems file:///Users/$loggedInUser/Music && sleep 2
/usr/bin/sfltool add-item com.apple.LSSharedFileList.FavoriteItems file:///Users/$loggedInUser/Pictures && sleep 2
touch /Users/$loggedInUser/.sidebarshortcuts
fi
/usr/libexec/PlistBuddy -c "Add :networkbrowser:CustomListProperties:com.apple.NetworkBrowser.backToMyMacEnabled bool False" /Users/$loggedInUser/Library/Preferences/com.apple.sidebarlists.plist
/usr/libexec/PlistBuddy -c "Add :networkbrowser:CustomListProperties:com.apple.NetworkBrowser.bonjourEnabled bool False" /Users/$loggedInUser/Library/Preferences/com.apple.sidebarlists.plist
killall cfprefsd
49 REPLIES 49

djdavetrouble
Contributor III

To add an item to the Command+K connect to server favorites:

/usr/bin/sfltool add-item -n "foobar" com.apple.LSSharedFileList.FavoriteServers "afp://foo.bar"

mostlikelee
Contributor

I have to ask. What's ~/.sidebarshortcuts?

jamiegalbreath
New Contributor

Is there a way to delete all server favourites?

LukeMason
New Contributor III

Same as @jamiegalbreath, I have server favorites that I need to remove somehow (in my case, the Server URL has a .local suffix that I need to remove).

I can create the new entry in the favorites list by using the commands above, but I end up with 2 entries in the list (1 with the .local address, which doesn't work with Sierra for some reason.... and one with the updated URL).

Does anyone know how to remove an existing server from the list (or even just clear the list)?

EDIT: I've determined that running an "sfltool clear" will wipe all of the shared file lists on logout (it looks like it deletes the contents of the "com.apple.sharedfilelists" folder).

I haven't figured out how to target just one list (like "Favorite Servers" for example).

Also, because it seems to require a logout to take effect, I can't add the new server after running the clear command (the behaviour I'm seeing is that if I try, it retains the bad server entry).

I've tried "killall cfprefsd" (is this even a thing anymore?), but it doesn't help.

bpavlov
Honored Contributor

sfltool .....10.13..... :(

djdavetrouble
Contributor III

which sfltool in 10.13 returns /usr/bin/sfltool @bpavlov

bpavlov
Honored Contributor

@djdavetrouble I know the tool is still there. But it's been crippled. It doesn't have the same functionality that it previously had the last time I checked.

primalcurve
New Contributor III

Yep. It's been gutted.

10.12
Usage: sfltool restore|add-item|save-lists|test|archive|enable-modern|dump-server-state|clear|disable-modern|dump-storage|list-info [options]

10.13
Usage: sfltool archive|list-info|list [options]

jkb
New Contributor III

Hello,

Radr away!

rdar://35722438

jkb

bwiessner
Contributor II

Apple! bring back sfltool in 10.13 !!!!

rodders
New Contributor III

Apple...
WHyyy

djdavetrouble
Contributor III

"Apologies, we do not plan to add back the feature. You will need to write a tool that does what’s needed." -Apple

jhuls
Contributor III

«sigh»

el2493
Contributor II

Had an app that mapped network drives and wrote Favorites icons to finder using slftool. Worked great in 10.12, in 10.13 I noticed when I ran the app it would create a .sfl2 file. So I'd see:

com.apple.LSSharedFileList.FavoriteItems.sfl
com.apple.LSSharedFileList.FavoriteItems.sfl2

Unfortunate that it appears I can't use the app anymore. Did anyone come up with a workaround for this?

milesleacy
Valued Contributor

I don't have a workaround, but a 'work with [Apple] for future state'...

Rather than asking for a binary solution, I would ask for an MDM payload solution. The settings in question are ultimately stored in plists. This makes them ripe for configuration profile management.

dsavageED
Contributor III

For 10.13 I switched to using FinderSidebarEditor

This uses the "deprecated" but still working LSSharedFileList commands and seems to be the only way to do sidebar tasks on 10.13. I'd say a a configuration profile is less than ideal, two of the items we regularly add to the sidebar are programmatically defined not static (network home and network shared space), profiles also wouldn't help for linking to locations which could be changed by the user (a OneDrive or OwnCloud folder), really sfltool was ideal and needed an improvement to delete entries.

el2493
Contributor II

@dsavageED thanks, I will look into that. I haven't worked with Python before (have been focused on AppleScript and Bash) so it'll probably be a down-the-road kind of thing.

dsavageED
Contributor III

@el2493 I am by no means that familiar with python ( I can read and code it a bit), but to leverage that script all you really need is:

#!/bin/bash

# Assuming FinderSidebarEditor.py script is stored in /usr/local/python you can call the function inside a bash script

    python - <<EOF
import sys
sys.path.append('/usr/local/python')            # Custom location script is installed to
from FinderSidebarEditor import FinderSidebar   # Import the module
sidebar = FinderSidebar()                       # Create a Finder sidebar instance to act on.
sidebar.add("$path")                            # Add '$path' favorite to sidebar
EOF

el2493
Contributor II

Thanks, that's very helpful!

el2493
Contributor II

@dsavageED I'm going to keep looking into this, but since you had the previous information I was wondering what the format of "$path" should be? I tried /volumes/[share name] (and the favorite showed as a "?" and wouldn't open) before looking at the python code and finding it was going to be a little more complicated than that.

Trying to make sense of:

def add(self, to_add, uri="file://localhost"):
        """
        Append item to sidebar list items.
        Args:
            to_add (str): Path to item to append to sidebar list.
        Keyword Args:
            uri (str): URI of server where item resides if not on localhost.
        """
        if uri.startswith("afp") or uri.startswith("smb"):
            path = "%s%s" % (uri, to_add)
            to_add = mount_share(path)
        item = NSURL.alloc().initFileURLWithPath_(to_add)
        LSSharedFileListInsertItemURL(self.sflRef, kLSSharedFileListItemBeforeFirst, None, None, item, None, None)
        self.synchronize()
        self.update()

So would the path need to be defined as "%s%s" % (file://localhost, /volumes/[share name])? Or does it need the actual path of the drive/folder and not the volumes information? I also tried different combinations of "%s%s" % ([the path]) and wasn't having any luck.

el2493
Contributor II

Figured it out...the path of the volume was /Volumes/share_name$ and the "$" was messing things up. Changed to /Volumes/share_name$ and it worked.

gskibum
Contributor III

Using FinderSidebarEditor, I have two questions:

  1. How are current home folders expressed? ~/Desktop isn't working for me. I get a ? mark.
  2. How are spaces in paths for shares handled? "afp://name:password@server/Data" works "afp://name:password@server/Old Data" does not work. Neither does "afp://name:password@server/Old Data"

EDIT: Regarding the home folder:

Using:

#!/usr/bin/python

CurrentUser=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "
");'`

from FinderSidebarEditor import FinderSidebar                  # Import the module

sidebar = FinderSidebar()                                      # Create a Finder sidebar instance to act on.

sidebar.remove("All My Files")                                 # Remove 'All My Files' favorite from sidebar
sidebar.remove("iCloud")                                       # Remove 'iCloud' favorite from sidebar
sidebar.add("/Library")                                        # Add '/Library' favorite to sidebar
sidebar.add("/Applications")                                   # Add '/Library' favorite to sidebar
sidebar.add("/Users/$CurrentUser/Desktop")                     # Add '/Library' favorite to sidebar
sidebar.add("/Users/$CurrentUser/Downloads")                   # Add '/Library' favorite to sidebar
                                                                                                                                                                                                                                                                                       ^

I get the following error (TextWrangler points to the last character in the variable line).

File "/usr/local/bin/Test2.py", line 3
    CurrentUser=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "
");'`

mm2270
Legendary Contributor II

@gskibum mysides seems to work a little easier than FinderSidebarEditor, despite it not being updated in a few years, but outside of that, I'm pretty sure your error is related to the fact that python variables from commands don't use the backtick syntax like in bash.

I don't really know python, but I made the following modification to the beginning of your script and it seems to at least get the correct logged in user's name. Not sure if the rest of it will work since I didn't really try it.

#!/usr/bin/python

from SystemConfiguration import SCDynamicStoreCopyConsoleUser
import sys

CurrentUser = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [CurrentUser,""][CurrentUser in [u"loginwindow", None, u""]]

The main issue is that you had the backtick syntax, plus, you're using a python one liner within a python script, but it was only ever a one liner because of needing to embed it into bash scripts. Since your entire script is python, I broke out the individual commands. Also, it's possible that to use a path to mysides that includes that CurrentUser variable, something else in the script needs to be changed. I don't think you just string them together like that in python.

Again, I really don't know python, so this might be a horrible way to do it. I just don't know what I don't know, so, there you go :-)

gskibum
Contributor III

Some Python pros over at Slack got me working with this:

from SystemConfiguration import SCDynamicStoreCopyConsoleUser

username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]
username = [username, ""][username in [u"loginwindow", None, u""]]

Then call it like this:

sidebar.add("/Users/%s/Pictures" % username)

gskibum
Contributor III

How are ya'll adding AirDrop back to the Finder Sidebar?

I'm trying this command with mysides but getting nowhere:

$mysides add domain-AirDrop "nwnode://domain-AirDrop"
killall cfprefsd

korylprince
New Contributor II

With slftool not able to add favorites in High Sierra, I set out on a journey to find an alternative. The sfl2 format is just a Foundation.NSKeyedArchiver dump, so I wrote the following python script to generate the Server Favorites file:

Edit: See my post below for a script that applies to all users.
Edit 2: The latest version of these scripts are available here.

# some inspiration from: https://github.com/quicksilver/Quicksilver/blob/master/Quicksilver/Code-QuickStepCore/QSObject_FileHandling.m
# get the latest version of this script at: https://gist.github.com/korylprince/be2e09e049d2fd721ce769770d983850

import os
import uuid

import Foundation

servers = ("smb://server.example.com/share", "vnc://server.example.com")
user = "administrator"

items = []
for server in servers:
    item = {}
    item["Name"] = unicode(server)
    url = Foundation.NSURL.URLWithString_(unicode(server))
    bookmark, _ = url.bookmarkDataWithOptions_includingResourceValuesForKeys_relativeToURL_error_(0, None, None, None)
    item["Bookmark"] = bookmark
    item["uuid"] = unicode(uuid.uuid1()).upper()
    item["visibility"] = 0
    item["CustomItemProperties"] = Foundation.NSDictionary.new()

    items.append(Foundation.NSDictionary.dictionaryWithDictionary_(item))

data = Foundation.NSDictionary.dictionaryWithDictionary_({
    "items": Foundation.NSArray.arrayWithArray_(items),
    "properties": Foundation.NSDictionary.dictionaryWithDictionary_({"com.apple.LSSharedFileList.ForceTemplateIcons": False})
})

Foundation.NSKeyedArchiver.archiveRootObject_toFile_(data, "/Users/{user}/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.FavoriteServers.sfl2".format(user=user))

os.system("killall sharedfilelistd")

Some caveats:

  • This particular code is for the Finder Server Favorites list, but should be easily adaptable to the other lists (I haven't tried.)
  • This code replaces the favorites file, though you could load the current file with Foundation.NSKeyedUnarchiver and append data to it pretty easily as well.
  • Getting the settings to actually apply seems a bit finicky; if Finder is open the new settings won't take effect even if you kill sharedfilelistd.

Hope someone finds this useful!

dstranathan
Valued Contributor II

@korylprince This did the trick for me. Much appreciated.

I just needed a simple way to deploy/mange SMB file server URL favorites which appear in the Finder's "Connect to Server..." box. All previous options have been "broken" since 10.13 High Sierra. Our file servers dont change often, but when they do its critical that all users know how to manually navigate to the new servers (in situations when a login script didn't/couldn't mount them)

I'm not a Python scripter (yet), so what I need to figure out next is how to extend your single-user focus out to recursively apply to ALL local homedirs in /Users.

korylprince
New Contributor II

@dstranathan Here's an updated script that will loop through all active users.

Edit: The latest version of these scripts are available here.

# get the latest version of this script at: https://gist.github.com/korylprince/be2e09e049d2fd721ce769770d983850
import os
import subprocess
import uuid

import Foundation

servers = ("smb://server.example.com/share", "vnc://server.example.com")

def get_users():
    "Get users with a home directory in /Users"

    # get users from dscl
    dscl_users = subprocess.check_output(["/usr/bin/dscl", ".", "-list", "/Users"]).splitlines()

    # get home directories
    homedir_users = os.listdir("/Users")

    # return users that are in both lists
    users = set(dscl_users).intersection(set(homedir_users))
    return [u.strip() for u in users if u.strip() != ""]

def set_favorites(user, servers):
    "Set the Server Favorites for the given user"

    # generate necessary structures
    items = []
    for server in servers:
        item = {}
        # use unicode to translate to NSString
        item["Name"] = unicode(server)
        url = Foundation.NSURL.URLWithString_(unicode(server))
        bookmark, _ = url.bookmarkDataWithOptions_includingResourceValuesForKeys_relativeToURL_error_(0, None, None, None)
        item["Bookmark"] = bookmark
        # generate a new UUID for each server
        item["uuid"] = unicode(uuid.uuid1()).upper()
        item["visibility"] = 0
        item["CustomItemProperties"] = Foundation.NSDictionary.new()

        items.append(Foundation.NSDictionary.dictionaryWithDictionary_(item))

    data = Foundation.NSDictionary.dictionaryWithDictionary_({
        "items": Foundation.NSArray.arrayWithArray_(items),
        "properties": Foundation.NSDictionary.dictionaryWithDictionary_({"com.apple.LSSharedFileList.ForceTemplateIcons": False})
    })

    # write sfl2 file
    Foundation.NSKeyedArchiver.archiveRootObject_toFile_(data, "/Users/{user}/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.FavoriteServers.sfl2".format(user=user))

# loop through users and set favorites
if __name__ == "__main__":
    for user in get_users():
        try:
            set_favorites(user, servers)
            print "Server Favorites set for " + user
        except Exception as e:
            # if there's an error, log it an continue on
            print "Failed setting Server Favorites for {0}: {1}".format(user, str(e))

    # kill sharedfilelistd process to reload file. Finder should be closed when this happens
    os.system("killall sharedfilelistd")

I've also added some comments to explain what the code does. Also note that this won't do anything for a User who hasn't signed in yet. This only works for users that already have a home directory created in /Users. All of the caveats from my post above still apply.

djdavetrouble
Contributor III

@korylprince Very nice. I have been using this script with a jamf policy to generate shortcuts:
when used as a script payload, the even variables are urls and the odd variable is the finder name.

I don't know python yet, but this gives me an excuse to learn.

#!/bin/bash

evens=( "$4" "$6" "$8" "${10}" )
odds=( "$5" "$7" "$9" "${11}" )

for ((i=0;i<${#evens[@]};++i)); do
    if [[ -n ${evens[i]} ]]; then
        sfltool add-item -n "${odds[i]}" com.apple.LSSharedFileList.FavoriteServers "${evens[i]}" &> /dev/null &&
        sleep 2
        /usr/libexec/PlistBuddy -c "Add URL string '${evens[i]}'" "/Users/$3/Desktop/Server Aliases/${odds[i]}.afploc"
    fi
done

mlitton
New Contributor II

@korylprince are you using #!/usr/bin/python to run this?

I have run the script using #!/usr/bin/python and get this echoed in terminal - "Server Favorites set for username" , but nothing shows even after logout/login or restart.

Maybe I am missing something in the compiler?

Thanks
Michael

tnielsen
Valued Contributor

@korylprince, where do I send the bitcoins?!

Thank guy guys, this is great!

korylprince
New Contributor II

@mlitton,

I was just running sudo python /path/to/script.py, but using #!/usr/bin/python will work as well. All Finder windows must be closed or the changes may not apply.

I have updated my scripts. One can be used to overwrite the current favorites, and the other can merge into existing favorites, optionally also removing specified servers. If the scripts are run as root, they will affect all users. If they are run as a normal user they will just affect that user.

You can see them on GitHub.

dstranathan
Valued Contributor II

I have been using versions of Kory's scripts - they work great. I finally have robust control over deploying and managing Server URL favorites. I'm a happy camper.

I use have both versions of the script (destructive and non-destructive). There was time when I needed to add a single specific new SMB server. I didn't want to break/delete any user's existing Server Favorites (used a non-destructive version). In another situation, we migrated to new SMB servers, so I replaced ALL Server URL Favorites with new Favorites - so user's weren't confused (I used a destructive 'nuke & pave' version of the script).

apizz
Valued Contributor

@djdavetrouble I'm not nearly as familiar with python as I should be. Did you get anywhere in your version such that you could set both the server and the Finder name?

georgecm12
Contributor III

I'm in the same boat. Is there any version of any of these scripts that allows for, at least, adding an entry and it's "friendly name"?

ryan_ball
Valued Contributor

This is super clunky but works from 10.9 to 10.13 (running as the user):

#!/bin/bash

path="/path/you/want/to/add"

function side_shortcut () {
/usr/bin/osascript > /dev/null 2>&1 <<-END
tell application "Finder" to activate
tell application "System Events"
    try
        keystroke "t" using {command down, control down}
    end try
end tell
END
}

/usr/bin/open "$path"

side_shortcut

exit 0

ben_hogan
New Contributor II

Hi guys so I'm running into a slight issue with this and feel like I'm missing something. Here is the script I'm currently running.

#!/bin/sh

python - <<EOF
import sys
sys.path.append('/usr/local/bin')
from FinderSidebarEditor import FinderSidebar           # Import the module
sidebar = FinderSidebar()                               # Create a Finder sidebar instance to act on.

sidebar.removeAll()                                     # Remove All Favorites from sidebar
sidebar.add("/Applications")                            # Add '/Applications' favorite to sidebar
sidebar.add("/Users/testuser/Desktop")                  # Add 'User Desktop' favorite to sidebar
sidebar.add("/Users/testuser/Documents")                # Add 'User Documents' favorite to sidebar
sidebar.add("/Users/testuser/Downloads")                # Add 'User Downloads' favorite to sidebar

EOF

Now if I run this locally, or as a command through ARD it does exactly what I expect it to do. However the moment I try running it through Jamf Remote it stops functioning. Logs say it completed successfully but the changes are never made to Finder for the currently logged in user. Just to further test I added the following lines at the bottom.

import os
path = '/Users/testuser/Desktop/PYTHON'
os.mkdir(path)

Running through Jamf Remote this does create a folder on the Desktop called "PYTHON" so from what I can tell the script does appear to be running correctly in every sense, Finder is just not refreshing correctly.

Forgot to mention I have tried running the script with "killall cfprefsd" and/or "killall Finder" at the end just to see if this would give any different results but it's still the same.

Thanks.

korylprince
New Contributor II

@aporlebeke, @georgecm12

I've updated my example scripts on GitHub to include the ability to set a favorite name.

Example:

servers = (("My Name", "smb://example.com/share"), "vnc://example.com")

This would create a favorite named "My Name" pointed to smb://example.com/share and another favorite both named and pointed to vnc://example.com.

Hopefully this should do what you're wanting.

apizz
Valued Contributor

Much appreciated, @korylprince .

Because of timing and my inability to full vet and test this before we deployed I moved most of these shortcuts to NoMAD, as we can configure who has access to what based on AD security group membership and our NoMAD shares config profile.