diff --git a/createuserpkg b/createuserpkg index 59ac2ec..b3a09b3 100755 --- a/createuserpkg +++ b/createuserpkg @@ -26,7 +26,7 @@ from locallibs import kcpassword from locallibs import shadowhash from locallibs import userplist from locallibs import userpkg - +from locallibs import imageutil def main(): '''Main''' @@ -36,16 +36,20 @@ def main(): required_user_options = optparse.OptionGroup( parser, 'Required User Options') required_user_options.add_option( - '--name', '-n', help='User shortname. REQUIRED.') + '--name', '-n', + help='User shortname. REQUIRED.') required_user_options.add_option( - '--uid', '-u', help='User uid. REQUIRED.') + '--uid', '-u', + help='User uid. REQUIRED.') required_package_options = optparse.OptionGroup( parser, 'Required Package Options') required_package_options.add_option( - '--version', '-V', help='Package version number. REQUIRED.') + '--version', '-V', + help='Package version number. REQUIRED.') required_package_options.add_option( - '--identifier', '-i', help='Package identifier. REQUIRED.') + '--identifier', '-i', + help='Package identifier. REQUIRED.') optional_user_options = optparse.OptionGroup( parser, 'Optional User Options') @@ -54,22 +58,36 @@ def main(): help='User password. If this is not provided, interactively prompt for ' 'password.') optional_user_options.add_option( - '--fullname', '-f', help='User full name. Optional.') - optional_user_options.add_option('--gid', '-g', help='User gid. Optional.') + '--fullname', '-f', + help='User full name. Optional.') + optional_user_options.add_option( + '--gid', '-g', + help='User gid. Optional.') optional_user_options.add_option( - '--generateduid', '-G', help='GeneratedUID (UUID). Optional.') + '--generateduid', '-G', + help='GeneratedUID (UUID). Optional.') optional_user_options.add_option( - '--home', '-H', help='Path to user home directory. Optional.') + '--home', '-H', + help='Path to user home directory. Optional.') optional_user_options.add_option( - '--shell', '-s', help='User shell path. Optional.') + '--shell', '-s', + help='User shell path. Optional.') optional_user_options.add_option( '--admin', '-a', action='store_true', help='User account should be added to admin group.') optional_user_options.add_option( '--autologin', '-A', action='store_true', help='User account should automatically login.') - optional_user_options.add_option('--hidden', action='store_true', - help='User account should be hidden.') + optional_user_options.add_option( + '--hidden', action='store_true', + help='User account should be hidden.') + optional_user_options.add_option( + '--picture', '-P', + help='Change user picture.') + optional_user_options.add_option( + '--createhomedir', '-c', action='store_true', + help='Create and populate home directories on the local computer ' + '(ATTENTION: works only when installing on the boot volume).') parser.add_option_group(required_user_options) parser.add_option_group(required_package_options) @@ -119,7 +137,9 @@ def main(): user_data['shell'] = options.shell if options.hidden: user_data['IsHidden'] = u'YES' - + if options.picture: + user_data['image_data'] = imageutil.generate(options.picture) + user_data['image_path'] = options.picture user_plist = userplist.generate(user_data) @@ -132,6 +152,8 @@ def main(): pkg_data['kcpassword'] = kcpassword.generate(password) if options.admin: pkg_data['is_admin'] = True + if options.createhomedir: + pkg_data['createhomedir'] = True # build the package userpkg.generate(pkg_data) diff --git a/locallibs/imageutil.py b/locallibs/imageutil.py new file mode 100644 index 0000000..7708277 --- /dev/null +++ b/locallibs/imageutil.py @@ -0,0 +1,67 @@ +# Copyright 2018 Gerd Niemetz. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Generates a base64 encoded JPEG from an image file''' + +import imghdr +import base64 +import subprocess +import os +from Foundation import NSData + +class ImageUtilException(Exception): + '''Error when generating jpeg''' + pass + +def is_non_zero_file(fpath): + return os.path.isfile(fpath) and os.path.getsize(fpath) > 0 + +def generate(image_path): + try: + # check if image_path is an image + if imghdr.what(image_path) is None: + raise ImageUtilException(u'"{0}" is not a valid picture'.format(image_path)) + # build temporary image file + temp_image = os.path.join(os.path.sep, 'private/var/tmp', + os.path.splitext(os.path.basename(image_path))[0] + '.jpg') + # convert image_path to jpeg with 72 dpi and max. width/height 512 px + cmd = ['/usr/bin/sips', + '-s', 'format', 'jpeg', image_path, + '-s', 'formatOptions', 'high', + '-s', 'dpiHeight', '72', + '-s', 'dpiWidth', '72', + '--resampleHeightWidthMax', '512', + '--out', temp_image + ] + try: + retcode = subprocess.call(cmd) + if retcode: + raise ImageUtilException(u'sips command failed') + # check if temporary image exists and is not blank + if is_non_zero_file(temp_image): + with open(temp_image, 'rb') as image_file: + # convert temporary image to a base64 encoded string + return NSData.alloc().initWithBase64EncodedString_options_( + base64.b64encode(image_file.read()), + 0) + else: + raise ImageUtilException(u'Could not convert "{0}" to a jpeg file)'.format(image_path)) + finally: + if os.path.exists(temp_image): + os.unlink(temp_image) + except IOError as e: + raise ImageUtilException(u'Cannot open "{0}" (I/O error {1}: {2})'.format( + image_path, + e.errno, + e.strerror)) diff --git a/locallibs/userpkg.py b/locallibs/userpkg.py index 1787397..57903b0 100644 --- a/locallibs/userpkg.py +++ b/locallibs/userpkg.py @@ -88,6 +88,9 @@ /usr/bin/killall DirectoryService 2>/dev/null || /usr/bin/killall opendirectoryd 2>/dev/null """ +PI_CREATEHOMEDIR = """ + /usr/sbin/createhomedir -c +""" def make_postinstall_script(scripts_path, pkg_info): # Create postinstall script. @@ -103,6 +106,8 @@ def make_postinstall_script(scripts_path, pkg_info): pi_reqs.add(PI_REQ_PLIST_FUNCS) if pkg_info.get('kcpassword'): pi_actions.add(PI_ENABLE_AUTOLOGIN) + if pkg_info.get('createhomedir'): + pi_live_actions.add(PI_CREATEHOMEDIR) postinstall = POSTINSTALL_TEMPLATE postinstall = postinstall.replace( '_POSTINSTALL_REQUIREMENTS_', '\n'.join(pi_reqs))