2 votos

¿Cómo puedo obtener la URL de la AppStore de una aplicación de macOS al analizar el MASReceipt incrustado?

Necesito obtener una lista de URLs para aplicaciones de AppStore en varios sistemas. Tengo una vaga idea de que la URL se puede formular concatenando la cadena https://apps.apple.com/app/id + el ID de Producto numérico que se encuentra en algún lugar en el paquete de la aplicación, bajo ../Contents/_MASReceipt/receipt

¿Alguien tiene un método confiable para analizar este archivo y extraer ese atributo de ID de la aplicación? Se prefiere Python3, pero cualquier solución es bienvenida.

2voto

Encontré un código muy antiguo en GitHub. Con algunas modificaciones, funciona. Lo probé en algunas aplicaciones de la tienda de aplicaciones que tengo, y tuve éxito al obtener los appIds de todas ellas.

Se necesita tener instalado python3 + el módulo asn1

pip install asn1

luego guardar como appstore-url.py y ejecutar por ejemplo:

$ appstore-url.py /Applications/Keynote.app
https://apps.apple.com/app/id409183694

código

#!/usr/bin/env python3

"""
Analiza _MASReceipt/receipt y muestra una URL de App Store

requiere el módulo asn1:
  pip install asn1

original:
  https://github.com/pudquick/pyMASreceipt

referencias:
  https://developer.apple.com/documentation/appstorereceipts/validating_receipts_on_the_device
  https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html

"""

import asn1
import os
import sys
from collections import namedtuple
import argparse

MASAttr = namedtuple('MASAttr', 'type version value')

MAS_TYPES = {
    1: 'ID de Producto',
    2: 'Identificador del Paquete',
    3: 'Versión de la Aplicación',
    4: 'Valor Opaco',
    5: 'Hash SHA-1',
    8: 'Fecha de Compra',
    10: 'Clasificación de Contenido Parental',
    16: 'ID de Versión de Instalador de la App Store',
    17: 'Recibo de Compra en la App'
}

def unwind(input):
    ret_val = []
    while not input.eof():
        tag = input.peek()
        if tag[1] == 0x00:
            tag, value = input.read()
            ret_val.append((tag[2], tag[0], value))
        elif tag[1] == 0x20:
            ret_val.append((tag[2], tag[0]))
            input.enter()
            ret_val.append(unwind(input))
            input.leave()
    return ret_val

def extract_data(asn1_seq):
    ret_val = []
    for i, x in enumerate(asn1_seq):
        if isinstance(x, tuple):
            if len(x) == 3 and x[2] == '1.2.840.113549.1.7.1':
                ret_val.append(asn1_seq[i + 2][0][2])
        elif isinstance(x, list):
            sub_val = extract_data(x)
            if isinstance(sub_val, list):
                ret_val.extend(sub_val)
            else:
                ret_val.append(sub_val)
    return ret_val[0] if len(ret_val) == 1 else ret_val

def parse_receipt(rec):
    ret_val = []
    for y in [z for z in rec if isinstance(z, list)]:
        try:
            dec = asn1.Decoder()
            dec.start(y[2][2])
            ret_val.append(MASAttr(MAS_TYPES.get(y[0][2], f'0x{y[0][2]:02X}'), y[1][2], unwind(dec)[0][-1]))
        except asn1.Error as e:
            #print(f"Error al decodificar ASN.1: {e}", file=sys.stderr)
            ret_val.append(MASAttr(MAS_TYPES.get(y[0][2], f'0x{y[0][2]:02X}'), y[1][2], y[2][2]))
    return ret_val

def get_app_receipt(path_to_MAS_app):
    receipt_path = os.path.join(path_to_MAS_app, "Contents/_MASReceipt/receipt")
    if not os.path.isfile(receipt_path):
        raise FileNotFoundError(f"Archivo de recibo no encontrado: {receipt_path}")
    with open(receipt_path, 'rb') as f:
        dec1 = asn1.Decoder()
        dec1.start(f.read())
        payload = extract_data(unwind(dec1))
        dec2 = asn1.Decoder()
        dec2.start(payload)
        return parse_receipt(unwind(dec2)[1])

def main():
    parser = argparse.ArgumentParser(description="Extrae el ID de Producto y devuelve una URL de AppStore analizando el MASReceipt.")
    parser.add_argument("path", help="Ruta de la app MAS")
    args = parser.parse_args()
    try:
        decoded_receipt = get_app_receipt(args.path)
        for p in decoded_receipt:
            if p.type == 'ID de Producto':
                print(f'https://apps.apple.com/app/id{p.value}')
    except FileNotFoundError as e:
        print(e, file=sys.stderr)
    except Exception as e:
        print(f"error: {e}", file=sys.stderr)

if __name__ == "__main__":
    main()

AppleAyuda.com

AppleAyuda es una comunidad de usuarios de los productos de Apple en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X