Skip to content

Instantly share code, notes, and snippets.

@scriptsandthings
Forked from korylprince/merge.py
Created February 8, 2020 17:35
Show Gist options
  • Save scriptsandthings/f8676400d3bdc9692fe482a4bf075a8b to your computer and use it in GitHub Desktop.
Save scriptsandthings/f8676400d3bdc9692fe482a4bf075a8b to your computer and use it in GitHub Desktop.
Modify 10.13 (SFL2) Server Favorites list
#!/usr/bin/python
"""
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, unicode(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 s[1].startswith(r[:-1])]
) < 1]
# generate necessary structures
items = []
for name, path in all_servers:
item = {}
# use unicode to translate to NSString
item["Name"] = unicode(name)
url = Foundation.NSURL.URLWithString_(unicode(path))
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, 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/bin/python
"""
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 = {}
# use unicode to translate to NSString
item["Name"] = unicode(name)
url = Foundation.NSURL.URLWithString_(unicode(path))
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, 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")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment