# Version 12 - 20052025(DDMMYYYY)
#!/usr/bin/env bash

KEYCHAIN_NAME="login.keychain"

function show_error() {
  message="$1"
  printf "$message. Exiting..."
  exit 1
}
function check_status() {
  if [ $? -ne 0 ]; then
    show_error "🚫 An error occurred, sorry! Please check the input."
  fi
}
function get_certificate() {
  if [[ ! $NEW_BUNDLE_ID =~ "docebo" ]]; then ## Doesn't contain "docebo"
    echo "👉 Importing certificates..."
    # Extract cert and key from the p12 file (--legacy is important!)
    ## p12 MUST BE EXPORTED WITHOUT A PASSWORD!
    openssl pkcs12 -in "$CERTIFICATE" -nocerts -out key.pem -legacy -nodes -passin pass:
    check_status
    openssl pkcs12 -in "$CERTIFICATE" -clcerts -nokeys -out cert.pem -legacy -passin pass:
    check_status
    security import cert.pem -k $KEYCHAIN_NAME -P $BITRISE_KEYCHAIN_PASSWORD -A
    security import key.pem -k $KEYCHAIN_NAME -P $BITRISE_KEYCHAIN_PASSWORD -A
    CERTIFICATE_NAME=$(openssl x509 -in cert.pem -noout -subject | sed -E 's/.*CN=([^)]*\)).*/\1/')
    echo "👉 Imported certificate and keys of $CERTIFICATE_NAME"
  else
    # Get the certificate name from security find-identity
    CERTIFICATE_NAME=$(security find-identity -p codesigning $KEYCHAIN_NAME | grep "Distribution" | awk -F'"' '{print $2}' | head -n 1)
    echo "👉 Using existing $CERTIFICATE_NAME"
  fi
}
function manage_existing_file_to_resign() {
  if [ "${ORIGINAL_FILE##*.}" = "ipa" ]; then
    # Unzip the old ipa quietly
    unzip -q "$ORIGINAL_FILE" -d temp
    check_status
  else
    show_error "Error: Resign require a .ipa file"
  fi
}
function set_target_app() {
  # Set the name of the original app, used as global variable in this script
  TARGET_APP=$(ls temp/Payload/)
}
function set_display_name() { ## Replace AppName, usually not changed from resign process
  if [ ! -z "$DISPLAY_NAME" ]; then
    /usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName $DISPLAY_NAME" "temp/Payload/$TARGET_APP/Info.plist"
    echo "👉 New Display Name: $DISPLAY_NAME"
  fi
}
function set_app_id_prefix() { ## Replace APP_ID, specific of the Developer account
  OLD_APP_ID_PREFIX=$(grep '<key>application-identifier</key>' "temp/Payload/$TARGET_APP/embedded.mobileprovision" -A 1 --binary-files=text | sed -E -e '/<key>/ d' -e 's/(^.*<string>)//' -e 's/([A-Z0-9]*)(.*)/\1/')
  check_status
  NEW_APP_ID_PREFIX=$(grep '<key>application-identifier</key>' "$PROVISION_FILE" -A 1 --binary-files=text | sed -E -e '/<key>/ d' -e 's/(^.*<string>)//' -e 's/([A-Z0-9]*)(.*)/\1/')
  check_status
  echo "👉 New app id: $NEW_APP_ID_PREFIX"
}
function set_bundle_id() {
  OLD_BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "temp/Payload/$TARGET_APP/Info.plist")
  check_status
  NEW_BUNDLE_ID=$(egrep -a -A 2 application-identifier "${PROVISION_FILE}" | grep string | sed -e 's/<string>//' -e 's/<\/string>//' -e 's/ //' | awk '{split($0,a,"."); i = length(a); for(ix=2; ix <= i;ix++){ s=s a[ix]; if(i!=ix){s=s "."};} print s;}')
  if [[ "${NEW_BUNDLE_ID}" == *\** ]]; then
    show_error "Bundle Identifier contains a *"
  fi
  check_status
  echo "👉 New bundle id: $NEW_BUNDLE_ID"
  ### Replace bundle identifier and BundleURLSchema contained inside Info.plist
  /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $NEW_BUNDLE_ID" "temp/Payload/$TARGET_APP/Info.plist"
  /usr/libexec/PlistBuddy -c "Set :CFBundleURLTypes:1:CFBundleURLSchemes:0 $NEW_BUNDLE_ID" "temp/Payload/$TARGET_APP/Info.plist"
  ## Update GoogleSignin URL schema
  NEW_CLIENT_ID=$(/usr/libexec/PlistBuddy -c "Print :REVERSED_CLIENT_ID" "temp/Payload/$TARGET_APP/GoogleService-Info.plist")
  if [[ ! -z $NEW_CLIENT_ID ]]; then
    /usr/libexec/PlistBuddy -c "set :CFBundleURLTypes:0:CFBundleURLSchemes:0 $NEW_CLIENT_ID" "temp/Payload/$TARGET_APP/Info.plist"
    echo "👉 Google schema replaced with $NEW_CLIENT_ID"
  fi
  ## End of update GoogleSignin URL schema
  # Uncomment to convert Info.plist to binary format
  # plutil -convert binary1 "temp/Payload/$TARGET_APP/Info.plist"
}
function replace_provisioning_profile() {
	cp "$PROVISION_FILE" "temp/Payload/$TARGET_APP/embedded.mobileprovision"
	check_status
}
function resign_app() {
  # PP enable all domains via a non-AppStore-compliant '*' value, must use App entitlements value
  # similar fix on Fastlane repository https://github.com/fastlane/fastlane/blob/master/sigh/lib/assets/resign.sh#L707
  associatedDomainKey="com.apple.developer.associated-domains"
  # This is the only way to get the entitlement values in a signed ipa file! The returned value is a String.
  APP_ENTITLEMENTS="$(/usr/bin/codesign -d --entitlements :- "temp/Payload/$TARGET_APP")"
  # save string value inside a temp file
  echo $APP_ENTITLEMENTS > temp/appEntitlements.plist
  # Print the values with PlistBuddy and save the values in a new Array.
  declare -a FILE_ARRAY=($(/usr/libexec/PlistBuddy -c "Print :$associatedDomainKey" "temp/appEntitlements.plist" | sed -e 1d -e '$d'))
  # Create a new entitlement file with the provisioning profile values!
  security cms -D -i "$PROVISION_FILE" > "provisioning.plist"
  # Copy all the new entlitements values inside a new temp file. This is a mandatory step!
  /usr/libexec/PlistBuddy -x -c 'Print:Entitlements' "provisioning.plist" > "temp/entitlements.plist"
  # delete the '*' value of the com.apple.developer.associated-domains. The default value of the provisioning is '*'
  /usr/libexec/PlistBuddy -c "delete $associatedDomainKey" "temp/entitlements.plist"
  index=0
  # iterate over FILE_ARRAY if populated
  for element in "${FILE_ARRAY[@]}"
  do
    # Add every array values of associatedDomainKey of the old entitlements inside the new entitlements file!
    /usr/libexec/PlistBuddy -c "add :$associatedDomainKey array" -c "add :$associatedDomainKey:$index string $element" "temp/entitlements.plist"
    ((index++))
  done
  # Print the entitlement file content
  echo "👉 New entitlements: "
  cat temp/entitlements.plist
  xattr -cr "temp/Payload/$TARGET_APP"

  # If $CERTIFICATE is a string ending with p12, then set the variable and use it
  if [[ "$CERTIFICATE" =~ p12$ ]]; then
    echo "👉 Using certificate p12..."
    get_certificate
  else
    echo "👉 Using certificate: $CERTIFICATE"
    CERTIFICATE_NAME=$CERTIFICATE
  fi

  # Prevent password prompt to resign hermes and payload
  security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $BITRISE_KEYCHAIN_PASSWORD $KEYCHAIN_NAME > /dev/null 2>&1

  /usr/bin/codesign -vvvvvvv -f -s "$CERTIFICATE_NAME" "temp/Payload/$TARGET_APP/Frameworks/hermes.framework/hermes"
  check_status

  /usr/bin/codesign -vvvvvvv -f -s "$CERTIFICATE_NAME" --entitlements="temp/entitlements.plist" "temp/Payload/$TARGET_APP"
  check_status
}
function finalize() {
  cd temp/
  zip -qr ../app-resigned.ipa Payload/ BCSymbolMaps/ SwiftSupport/
  cd .. && rm -rf temp
}
if [[ ${#3} -eq 0 ]]; then
  show_error "\nHow to use: \n  $ ${0##*/}   path/to/ipa_or_app_to_sign   path/to/profile   Certificate(='iPhone Distribution: Name') \n\nTry again"
fi
ORIGINAL_FILE="$1"
PROVISION_FILE="$2"
CERTIFICATE="$3"
DISPLAY_NAME="$4"

manage_existing_file_to_resign
set_target_app
set_display_name
set_app_id_prefix
set_bundle_id
replace_provisioning_profile
resign_app
finalize
echo "🎉 Resigned succesfully! 🎉"

## Made with love and low budget from MasterMatteo ##