Skip to content

Instantly share code, notes, and snippets.

@korylprince
Last active October 31, 2023 02:01
Show Gist options
  • Save korylprince/be2e09e049d2fd721ce769770d983850 to your computer and use it in GitHub Desktop.
Save korylprince/be2e09e049d2fd721ce769770d983850 to your computer and use it in GitHub Desktop.
Modify 10.13 (SFL2) Server Favorites list
#!/usr/local/munki/munki-python
# change the above path to your own python if you don't have Munki installed
"""
Merges add_servers into current favorites and removes remove_servers.
Run as root to update all users or as normal user to update just that user.
"""
import os
import getpass
import subprocess
import uuid
import Foundation
favorites_path = "/Users/{user}/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.FavoriteServers.sfl2"
# add these servers if they don't exist already
# use a tuple: ("<name>", "<path>") to set a name for the favorite.
# otherwise just use a string and the path will be used as the name
add_servers = (("My Name", "smb://server.example.com/share"), "vnc://server.example.com")
# remove these servers if they exist. Use a wildcard (*) at the end to remove all shares
# does not support ("<name>", "<path>") syntax
remove_servers = ("smb://old.example.com/*", "vnc://old.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, add_servers, remove_servers):
"Set the Server Favorites for the given user"
# read existing favorites file
data = Foundation.NSKeyedUnarchiver.unarchiveObjectWithFile_(favorites_path.format(user=user))
# reformat add_servers to [(name, path), ...]
new_add_servers = []
for s in add_servers:
if len(s) == 2:
new_add_servers.append(s)
else:
new_add_servers.append((s, s))
add_servers = new_add_servers
existing_servers = []
# read existing items safely
if data is not None:
data_items = data.get("items", [])
# read existing servers
for item in data_items:
name = item["Name"]
url, _, _ = Foundation.NSURL.initByResolvingBookmarkData_options_relativeToURL_bookmarkDataIsStale_error_(Foundation.NSURL.alloc(), item["Bookmark"], Foundation.NSURLBookmarkResolutionWithoutUI, None, None, None)
existing_servers.append((name, url))
# get unique ordered list of all servers
all_servers = []
# add existing_servers to list, updating name if necessary
for s in existing_servers:
try:
idx = [a[1] for a in add_servers].index(s[1])
all_servers.append((add_servers[idx][0], s[1]))
except ValueError:
all_servers.append(s)
# add add_servers if not present already
for s in add_servers:
if s[1] not in [e[1] for e in existing_servers]:
all_servers.append(s)
# remove old servers: exact match
# matches "smb://old.domain" exactly
all_servers = [s for s in all_servers if s[1] not in remove_servers]
# remove old servers: shares
# matches "smb://old.domain/*"
all_servers = [s for s in all_servers if len(
[True for r in remove_servers if r.endswith("*") and str(s[1]).startswith(r[:-1])]
) < 1]
# generate necessary structures
items = []
for name, path in all_servers:
item = {}
item["Name"] = name
url = Foundation.NSURL.URLWithString_(str(path))
bookmark, _ = url.bookmarkDataWithOptions_includingResourceValuesForKeys_relativeToURL_error_(0, None, None, None)
item["Bookmark"] = bookmark
# generate a new UUID for each server
item["uuid"] = str(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, favorites_path.format(user=user))
# loop through users and set favorites
if __name__ == "__main__":
# if running as root, run for all users. Otherwise run for current user
user = getpass.getuser()
if user == "root":
users = get_users()
else:
users = [user]
for user in users:
try:
set_favorites(user, add_servers, remove_servers)
# fix owner if ran as root
if user == "root":
os.system(("chown {user} " + favorites_path).format(user=user))
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")
#!/usr/local/munki/munki-python
# change the above path to your own python if you don't have Munki installed
"""
Overwrites server favorites with servers.
Run as root to update all users or as normal user to update just that user.
"""
import os
import getpass
import subprocess
import uuid
import Foundation
favorites_path = "/Users/{user}/Library/Application Support/com.apple.sharedfilelist/com.apple.LSSharedFileList.FavoriteServers.sfl2"
# Use a tuple: ("<name>", "<path>") to set a name for the favorite.
# Otherwise just use a string and the path will be used as the name
servers = (("My Name", "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:
name = server[0] if len(server) == 2 else server
path = server[1] if len(server) == 2 else server
item = {}
item["Name"] = name
url = Foundation.NSURL.URLWithString_(path)
bookmark, _ = url.bookmarkDataWithOptions_includingResourceValuesForKeys_relativeToURL_error_(0, None, None, None)
item["Bookmark"] = bookmark
# generate a new UUID for each server
item["uuid"] = str(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, favorites_path.format(user=user))
# loop through users and set favorites
if __name__ == "__main__":
# if running as root, run for all users. Otherwise run for current user
user = getpass.getuser()
if user == "root":
users = get_users()
else:
users = [user]
for user in users:
try:
set_favorites(user, servers)
# fix owner if ran as root
if user == "root":
os.system(("chown {user} " + favorites_path).format(user=user))
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")
@hakanmarklund
Copy link

I tried the overwrite.py script in Ventura 13.2 and it worked, but only if not run as root. I found that the reason is that subprocess.check_output() returns the user list as "bytes". I got it working by adding ",encoding='UTF-8'" so that line 26 looks like this:
dscl_users = subprocess.check_output(["/usr/bin/dscl", ".", "-list", "/Users"],encoding='UTF-8').splitlines()

@vn425282
Copy link

may I know how can I change the add_servers pointing to my specific local folder ? let's say I have the ABC folder inside the Documents, and would like to stick it into the SideBar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment