Microsoft To Do
Paquet d’installation silencieuse pour Microsoft To Do
2.148.3611.0-53
Utilities
Utilities
Les paquets PREPROD sont des paquets construits via LUTI.
Ils restent généralement 5 jours en PREPROD, après quoi un deuxième scan VirusTotal est effectué pour vérifier que le status n'a pas changé.
Si le paquet réussit ce dernier contrôle, il est promu en PROD et publié sur le store.
- package: tis-microsoft-todos
- name: Microsoft To Do
- version: 2.148.3611.0-53
- categories: Utilities
- maintainer: WAPT Team,Tranquil IT,Jimmy PELÉ
- editor: Microsoft
- licence: proprietary_free,wapt_public
- locale: all
- target_os: windows
- impacted_process: Todo,Microsoft.Todos.SystemTrayExtension
- architecture: all
- signature_date:
- size: 167.14 Mo
- installed_size: 78.63 Mo
- homepage : https://apps.microsoft.com/store/detail/9NBLGGH5R558
package : tis-microsoft-todos
version : 2.148.3611.0-53
architecture : all
section : base
priority : optional
name : Microsoft To Do
categories : Utilities
maintainer : WAPT Team,Tranquil IT,Jimmy PELÉ
description : Microsoft To Do is a task management app to help you stay organized and manage your day-to-day
depends :
conflicts :
maturity : PREPROD
locale : all
target_os : windows
min_wapt_version : 2.3
sources : https://apps.microsoft.com/store/detail/9NBLGGH5R558
installed_size : 78631036
impacted_process : Todo,Microsoft.Todos.SystemTrayExtension
description_fr : Microsoft To Do est une application de gestion des tâches qui vous aide à rester organisé et à gérer votre quotidien
description_pl : Microsoft To Do to aplikacja do zarządzania zadaniami, która pomaga zachować porządek i zarządzać codziennymi zadaniami
description_de : Microsoft To Do ist eine App zur Aufgabenverwaltung, die Ihnen hilft, organisiert zu bleiben und Ihren Alltag zu bewältigen
description_es : Microsoft To Do es una aplicación de gestión de tareas que te ayuda a organizarte y gestionar tu día a día
description_pt : O Microsoft To Do é uma aplicação de gestão de tarefas que o ajuda a manter-se organizado e a gerir o seu dia-a-dia
description_it : Microsoft To Do è un'applicazione per la gestione delle attività che vi aiuta a rimanere organizzati e a gestire le vostre attività quotidiane
description_nl : Microsoft To Do is een app voor taakbeheer om u te helpen georganiseerd te blijven en uw dagelijkse taken te beheren
description_ru : Microsoft To Do - это приложение для управления задачами, которое поможет вам оставаться организованным и управлять повседневными делами
audit_schedule :
editor : Microsoft
keywords : microsoft,to,do,todo,todos,dos,is,task,tasks,list,management,app,organize,manage,day-to-day
licence : proprietary_free,wapt_public
homepage : https://apps.microsoft.com/store/detail/9NBLGGH5R558
package_uuid : 4f89adf0-288a-4571-befa-1b47ce6282ab
valid_from :
valid_until :
forced_install_on :
changelog :
min_os_version : 10.0.17763
max_os_version :
icon_sha256sum : d05608294b26da683b6efc641070d4828da3465475615d4d24f4a78a7d07dec9
signer : test
signer_fingerprint: b82fc8ef4a4475c0f69ac168176c2bfc58f572eb716c4eadd65e4785c155dd8e
signature_date : 2026-01-05T10:07:33.000000
signed_attributes : package,version,architecture,section,priority,name,categories,maintainer,description,depends,conflicts,maturity,locale,target_os,min_wapt_version,sources,installed_size,impacted_process,description_fr,description_pl,description_de,description_es,description_pt,description_it,description_nl,description_ru,audit_schedule,editor,keywords,licence,homepage,package_uuid,valid_from,valid_until,forced_install_on,changelog,min_os_version,max_os_version,icon_sha256sum,signer,signer_fingerprint,signature_date,signed_attributes
signature : RlqHH4V7Tuv7Fcwc5BdOEhrvIiG8VQbPGYpDI8tRs2eWTgPJAmxrxFiAbv/wMryCKdS+YYX5h7xSSk5gOMIdrc3YMY9WwH3QBYuvcDEAt0aAxMTi8R3R6NO8luTAoh0tJmQa+Xcz9J1fp3U+yzKge4YYiHRE2+iqmRHW8r0a2NGgXQ25kJCi6YvYQLB0Mt7Q8HKi1A8nVn3KHHBgR+b7J3zULS5AhbJwnfonysJAK6fU0Hdo2q3n8q6tfZG8yybHpm121liPFD234dp67sbTrKVlW5tkCjm1SnYyf30l6hKHEimxG8Gle47qf9erpCkaN3wq3f6THtJYpV9iSViTUw==
# -*- coding: utf-8 -*-
from setuphelpers import *
import platform
appx_package_name = "Microsoft.Todos"
appx_dir = makepath(programfiles, "WindowsAppsInstallers")
allow_remove = True
remove_sources = False # remove_sources = True may prevent multi-user installations.
def install():
# Declare local variables
bins_dir = control.package.split("-", 1)[1]
appx_bins_dir = makepath(appx_dir, bins_dir)
# Remove from old path
for appx_file in glob.glob(makepath(appx_dir, "%s_*" % appx_package_name)):
remove_file(appx_file)
# Placing bins for session-setup installs
if isdir(appx_bins_dir):
remove_tree(appx_bins_dir)
mkdirs(appx_bins_dir)
print(f"Creating: {appx_bins_dir}")
for f in glob.glob(bins_dir + "/*"):
filecopyto(f, appx_bins_dir)
for f in glob.glob(appx_bins_dir + "/*"):
fname = f.split(os.sep)[-1]
if control.architecture == "all":
if "x86" in glob.glob(makepath(appx_bins_dir, f"{appx_package_name}_*"))[0].split(os.sep)[-1]:
if not "x86" in fname and not "neutral" in fname:
remove_file(f)
else:
if not get_host_architecture() in fname and not "neutral" in fname:
remove_file(f)
else:
if not get_host_architecture() in fname and not "neutral" in fname:
remove_file(f)
def session_setup():
# Declare local variables
bins_dir = control.package.split("-", 1)[1]
appx_bins_dir = makepath(appx_dir, bins_dir)
# Installing the UWP application in the user environment
try:
appx_version = get_powershell_str(f'Get-AppxPackage -Name "{appx_package_name}"', "Version")
except:
appx_version = "0"
if not appx_version or Version(appx_version) < Version(control.get_software_version()) or force:
try:
bin_path = glob.glob(makepath(appx_bins_dir, f"{appx_package_name}_*"))[0]
except:
error("Unable to find sources.\nPlease reinstall this package or change remove_sources to False if relevant.")
dependencies_pathes = ",".join([f'"{a}"' for a in glob.glob(makepath(appx_bins_dir, "*")) if not appx_package_name in a])
print("Installing: %s" % bin_path.split("\\")[-1])
killalltasks(control.get_impacted_process_list())
add_appx_cmd = f'Add-AppxPackage -Path "{bin_path}"'
if dependencies_pathes:
add_appx_cmd += f" -DependencyPath {dependencies_pathes}"
run_powershell(add_appx_cmd)
else:
print(f"{appx_package_name} ({appx_version}) is installed and up-to-date")
def audit():
bins_dir = control.package.split("-", 1)[1]
appx_bins_dir = makepath(appx_dir, bins_dir)
try:
appx_version = get_powershell_str(f'Get-AppxPackage -Name "{appx_package_name}"', "Version")
appx_installlocation = get_powershell_str(f'Get-AppxPackage -Name "{appx_package_name}"', "InstallLocation")
except:
appx_version = "0"
appx_installlocation = "notfound"
if dir_is_empty(appx_installlocation):
print(f"Removing: {appx_bins_dir} since dir is empty")
if isdir(appx_installlocation):
if Version(appx_version) >= Version(control.get_software_version()):
if remove_sources:
print(f"Removing: {appx_bins_dir} since App is installed and up-to-date")
remove_tree(appx_bins_dir)
else:
print(f"{appx_installlocation} is installed and up-to-date")
else:
print(f"{appx_installlocation} is installed but outdated")
else:
print(f"No user seems to have installed {appx_package_name} on this PC at the moment")
return "OK"
def uninstall():
# Declare local variables
bins_dir = control.package.split("-", 1)[1]
appx_bins_dir = makepath(appx_dir, bins_dir)
if allow_remove:
remove_appx(appx_package_name)
if isdir(appx_bins_dir):
print("Removing: %s" % (appx_bins_dir))
remove_tree(appx_bins_dir)
if dir_is_empty(appx_dir):
print("Removing: %s since it is empty" % (appx_dir))
remove_tree(appx_dir)
# The code below is temporary and may be removed once the WAPT PR has been merged.
def isARM64():
return platform.machine().lower() in ('aarch64', 'arm64')
def get_host_architecture():
if isARM64():
return "arm64"
elif isARM():
return "arm"
elif is64():
return "x64"
elif is32():
return "x86"
# -*- coding: utf-8 -*-
from setuphelpers import *
try:
from setupdevhelpers import *
except:
from bs4 import BeautifulSoup
import json
import waptguihelper
import html
from xml.dom import minidom
import requests
import json
import re
release_name_map = {"retail": "Retail", "RP": "Release Preview", "WIS": "Insider Slow", "WIF": "Insider Fast"}
release_type = "retail"
release_name = release_name_map[release_type]
arch = "x64"
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
import xmltodict
#For debug mode, set as True
debug_mode = False
#To make 5 api requests, set as True / Only use if retrieve binary is bad
loop_enable = False
def update_package():
# Declaring local variables
package_updated = False
proxies = get_proxies()
if not proxies:
proxies = get_proxies_from_wapt_console()
store_url = control.sources
if not store_url:
store_id = waptguihelper.input_dialog(
"Choice of app", "Enter the windows store app id (foundable in package url: https://apps.microsoft.com/store/apps)", store_url
).split("/")[-1]
else:
store_id = store_url.split("/")[-1]
store_id = store_id.split("?")[0]
url_ms = "https://apps.microsoft.com/store/detail/%s" % store_id
control.sources = url_ms
control.save_control_to_wapt()
package_json = get_json(store_id)
# Updating package.json ignored_versions list
if isfile("package.json"):
package_infos = json_load_file("package.json")
else:
package_infos = {"version_prefix": None, "ignored_versions": []}
ignored_versions = package_infos["ignored_versions"]
version_prefix = package_infos.get("version_prefix", None)
#Debug print package.json informations
if debug_mode:
print("\n\n=== CAREFUL : DEBUG MODE IS ACTIVE ===\n")
print(f"version prefix : {version_prefix}")
print(f"ignored versions : {ignored_versions}\n")
#Get files from Microsoft Store API
print("Retrieving informations from Microsoft Store API, please wait\n")
all_files_dict = get_all_files(url_ms,proxies)
#Debug print all_files_dict, contains all files from a Store url API request
if debug_mode:
all_files_dict_json = json.dumps(all_files_dict,indent=4)
print("*** Pretty return from get_all_files() ***")
print(f"all_files_dict : {all_files_dict_json}\n")
# check setup.py incase the package name doesnt match the installer file
with open("setup.py", "r") as f:
for line in f.readlines():
if line.startswith("appx_package"):
store_package_name = line.split("=")[1].split('"')[1]
break
#Find the last binaries or the one matching prefix inside all_files_dict
bin_selected_dict = ask_app_filename(all_files_dict, store_package_name,ignored_versions=ignored_versions,version_prefix=version_prefix)
#Debug print post filtering to see which files have been selected
if debug_mode:
bin_selected_dict_json = json.dumps(bin_selected_dict,indent=4)
print("*** Selected file from ask_app_file_name() ***")
print(f"bin_selected_dict : {bin_selected_dict_json}\n")
#Define binarie variables
appx_package = bin_selected_dict[0]["bin_name"].split("_")[0]
bin_selected = bin_selected_dict[0]
latest_bin = bin_selected["bin_name"]
version = bin_selected["version"]
download_url = bin_selected["download_url"]
package_name = bin_selected["package_name"]
software_name = bin_selected["software_name"]
package_arch = bin_selected["package_arch"]
#Download binarie file
if download_url.split("/")[2].endswith("microsoft.com"):
if not isfile(latest_bin):
print("Downloading: %s" % latest_bin)
wget(download_url, latest_bin, proxies=proxies)
else:
print("Binary file version corresponds to online version")
else:
print("ERROR: The retrieved url will not download from microsoft's servers")
# Asking pkg infos if needed and not running as luti
if not params.get("running_as_luti"):
if package_arch == "all":
ask_control_architecture(package_arch)
ask_control_categories()
ask_control_package(package_name, "template-microsoft-store", remove_base_files=True)
ask_control_name(software_name, "Template Microsoft Store")
# description from microsoft website
ask_control_description("update_package", get_description(package_json))
# Changing setup.py appx_package variable
new_lines = []
with open("setup.py", "r") as f:
for line in f.readlines():
if line.startswith("appx_package"):
line = 'appx_package_name = "%s"\n' % latest_bin.split("_")[0]
new_lines.append(line)
with open("setup.py", "w") as f:
f.writelines(new_lines)
#Making dependency dict from downloaded binary
uwp_app_dict = make_dependency_dict(appx_package)
if not uwp_app_dict:
# error(f'{appx_package_name} not found in "uwp_app_dict".\n\n{list(set([ua["bin_name"].split("_")[0] for ua in all_files]))}')
error(f'"{appx_package}" not found in "uwp_app_dict". This is probably caused by "package_json" filters:\n{package_json}')
#Download each dependency
newer_uwp_app = download_uwp_dependency(uwp_app_dict,all_files_dict,proxies)
#To make things pretty
if debug_mode:
print("\n")
# Changing version of the package
if Version(version) > Version(control.get_software_version()):
print("Software version updated (from: %s to: %s)" % (control.get_software_version(), Version(version)))
package_updated = True
else:
print("Software version up-to-date (%s)" % Version(version))
if debug_mode:
print("Deleting binary files and exiting")
for uwp_file in glob.glob(f'{newer_uwp_app["Name"]}*{newer_uwp_app["Version"]}*.*'):
remove_file(uwp_file)
return
control.set_software_version(version)
control.save_control_to_wapt()
# Placing binaries in a dir ["appxbundle", "msixbundle", "appx", "msix]
for f in glob.glob("*.part"):
remove_file(f)
bins_dir = control.package.split("-", 1)[1]
if isdir(bins_dir):
remove_tree(bins_dir)
mkdirs(bins_dir)
for uwp_file in glob.glob(f'{newer_uwp_app["Name"]}*{newer_uwp_app["Version"]}*.*'):
if not isfile(makepath(bins_dir, uwp_file)):
shutil.move(uwp_file, bins_dir)
if uwp_app_dict[newer_uwp_app["FileName"]]["Dependencies"] is not None:
for l in [glob.glob(a["Name"] + "_*") for a in uwp_app_dict[newer_uwp_app["FileName"]]["Dependencies"]]:
for f in l:
if not isfile(makepath(bins_dir, f)):
shutil.move(f, bins_dir)
# Deleting outdated binaries
remove_outdated_binaries(version, ["appxbundle", "msixbundle", "appx", "msix", "part"], latest_bin)
# Validating or not update-package-sources
return package_updated
def get_json(app_id):
# download json from url and return it as dict
url = "https://storeedgefd.dsx.mp.microsoft.com/v9.0/products/%s?market=US&locale=en-us&deviceFamily=Windows.Desktop" % app_id
dict = json.loads(requests.get(url).text)
return dict
def get_all_files(store_id,proxies):
all_files = url_generator(
store_id,
proxies=proxies
)
#Debug print raw dict directly from API
if debug_mode:
all_files_json = json.dumps(all_files,indent=4)
print("*** Raw return from url_generator() ***")
print(f"all_files : {all_files_json}\n")
all_files_dict = []
# parse every file name and url in all_files and make a dict:
for i,download_url in all_files[0].items():
version = i.split("_")[1]
bin_name = i.replace("~", "_")
pkg_splitted = re.split(r"_\d+\.", bin_name)[0]
package_prefix = control.package.split("-")[0]
package_name = package_prefix + "-" + pkg_splitted.split("_")[0].replace(".", "-").lower()
software_name = bin_name.split("_")[0].replace("-", " ").replace(".", " ")
if "arm64" in bin_name:
package_arch = "arm64"
elif "arm" in bin_name:
package_arch = "arm"
elif "x64" in bin_name:
package_arch = "x64"
# elif "x86" in bin_name:
# package_arch = "all" # not x86 since it may be required for x64
else:
package_arch = "all"
file_dict = {
"version": version,
"bin_name": bin_name,
"package_name": package_name,
"software_name": software_name,
"package_arch": control.architecture,
"package_arch": package_arch,
"download_url": download_url,
}
all_files_dict.append(file_dict)
return all_files_dict
def url_generator(url,proxies):
def uwp_gen(data_list):
cat_id = data_list["WuCategoryId"]
main_file_name = data_list["PackageFamilyName"].split("_")[0]
release_type = "Retail"
#Ask Microsoft API for a cookie
with open(rf"{basedir}\data\xml\GetCookie.xml", "r") as f:
cookie_content = f.read()
out = requests.post(
"https://fe3cr.delivery.mp.microsoft.com/ClientWebService/client.asmx",
data=cookie_content,
headers={"Content-Type": "application/soap+xml; charset=utf-8"},
proxies=proxies,
verify=False
).text
doc = minidom.parseString(out)
cookie = doc.getElementsByTagName("EncryptedData")[0].firstChild.nodeValue
with open(rf"{basedir}\data\xml\WUIDRequest.xml", "r") as f:
cat_id_content = f.read().format(cookie, cat_id, release_type)
out = requests.post(
"https://fe3cr.delivery.mp.microsoft.com/ClientWebService/client.asmx",
data=cat_id_content,
headers={"Content-Type": "application/soap+xml; charset=utf-8"},proxies=proxies,verify=False).text
doc = minidom.parseString(html.unescape(out))
if loop_enable:
#To avoid picking wrong miror, make 5 requests and merge responses
req = 0
for req in range(4):
out2 = requests.post(
"https://fe3cr.delivery.mp.microsoft.com/ClientWebService/client.asmx",
data=cat_id_content,
headers={"Content-Type": "application/soap+xml; charset=utf-8"},proxies=proxies,verify=False).text
doc2 = minidom.parseString(html.unescape(out2))
imp = doc.importNode(doc2.childNodes[0],True)
doc.childNodes[0].appendChild(imp)
req += 1
filenames = {}
for node in doc.getElementsByTagName("Files"):
try:
filenames[
node.parentNode.parentNode.getElementsByTagName("ID")[0].firstChild.nodeValue
] = (
f"{node.firstChild.attributes['InstallerSpecificIdentifier'].value}_{node.firstChild.attributes['FileName'].value}",
node.firstChild.attributes["Modified"].value,
)
except KeyError:
continue
if not filenames:
raise Exception("server returned an empty list")
identities = {}
name_modified = {}
for node in doc.getElementsByTagName("SecuredFragment"):
try:
file_name, modifed = filenames[
node.parentNode.parentNode.parentNode.getElementsByTagName("ID")[0].firstChild.nodeValue
]
update_identity = node.parentNode.parentNode.firstChild
name_modified[file_name] = modifed
identities[file_name] = (
update_identity.attributes["UpdateID"].value,
update_identity.attributes["RevisionNumber"].value,
)
except KeyError:
continue
api_dict = {value: identities[value] for value in name_modified}
#Debug print raw dict directly from API
if debug_mode:
api_dict_json = json.dumps(api_dict,indent=4)
print("*** First API Return from url_generator() ***")
print(f"api_dict : {api_dict_json}\n")
with open(rf"{basedir}\data\xml\FE3FileUrl.xml", "r") as f:
file_content = f.read()
file_dict = {}
for file_name, (updateid, revisionnumber) in api_dict.items():
out = requests.post(
"https://fe3cr.delivery.mp.microsoft.com/ClientWebService/client.asmx/secured",
data=file_content.format(updateid, revisionnumber, release_type),
headers={"Content-Type": "application/soap+xml; charset=utf-8"},
proxies=proxies,
verify=False
).text
doc = minidom.parseString(out)
for i in doc.getElementsByTagName("FileLocation"):
url = i.getElementsByTagName("Url")[0].firstChild.nodeValue
if len(url) != 99:
file_dict[file_name] = url
if len(file_dict) != len(api_dict):
raise Exception("server returned an incomplete list")
return file_dict, main_file_name
def non_uwp_gen(product_id):
api = f"https://storeedgefd.dsx.mp.microsoft.com/v9.0/packageManifests//{product_id}?market=US&locale=en-us&deviceFamily=Windows.Desktop"
data = requests.get(api,proxies=proxies).text
datas = json.loads(data)
if not datas.get("Data"):
raise Exception("server returned an empty list")
file_name = datas["Data"]["Versions"][0]["DefaultLocale"]["PackageName"]
installer_list = datas["Data"]["Versions"][0]["Installers"]
download_data = set((d["Architecture"], d["InstallerLocale"], d["InstallerType"], d["InstallerUrl"]) for d in installer_list)
curr_arch = os_arc()
download_data = list(download_data)
arch, locale, installer_type, url = download_data[0]
if len(download_data) > 1:
for data in download_data[1:]:
if arch not in ("neutral", curr_arch) and data[0] in ("neutral", curr_arch):
arch, locale, installer_type, url = data
elif data[0] == arch and data[1] != locale and ("us" in data[1] or "en" in data[1]):
locale, installer_type, url = data[1], data[2], data[3]
break
main_file_name = clean_name(file_name) + "." + installer_type
file_dict = {main_file_name: url}
return file_dict, [main_file_name], main_file_name, False
def clean_name(badname):
name = "".join([(i if (64 < ord(i) < 91 or 96 < ord(i) < 123) else "") for i in badname])
return name.lower()
pattern = re.compile(r".+\/([^\/\?]+)(?:\?|$)")
matches = pattern.search(str(url))
if not matches:
raise Exception("No Data Found: --> [You Selected Wrong Page, Try Again!]")
product_id = matches.group(1)
details_api = f"https://storeedgefd.dsx.mp.microsoft.com/v9.0/products/{product_id}?market=US&locale=en-us&deviceFamily=Windows.Desktop"
data = requests.get(details_api,proxies=proxies).text
response = json.loads(data, object_hook=lambda obj: {k: json.loads(v) if k == "FulfillmentData" else v for k, v in obj.items()})
if not response.get("Payload"):
raise Exception("No Data Found: --> [You Selected Wrong Page, Try Again!]")
response_data = response["Payload"]["Skus"][0]
data_list = response_data.get("FulfillmentData")
if data_list:
return uwp_gen(data_list)
else:
return non_uwp_gen(product_id)
def ask_app_filename(all_files_dict, store_package_name, ignored_versions=[], version_prefix=None):
# input files dict and a store packa_name if it exist, if empty selct automatically the proper file if not ask user to select it
if "template-microsoft-store" in control.package:
selected = waptguihelper.grid_dialog(
"Please select the proper file",
json.dumps(all_files_dict),
waptguihelper.GRT_SELECTED,
'{"columns":[{"propertyname":"version","datatype":"String","required":false,"readonly":false,"width":130},{"propertyname":"bin_name","datatype":"String","required":false,"readonly":false,"width":420},{"propertyname":"package_name","datatype":"String","required":false,"readonly":false,"width":190},{"propertyname":"software_name","datatype":"String","required":false,"readonly":false,"width":172},{"propertyname":"package_arch","datatype":"String","required":false,"readonly":false,"width":88},{"propertyname":"download_url","datatype":"String","required":false,"readonly":false,"width":1472}]}',
)
else:
selected = [a for a in all_files_dict if (a["package_arch"] in ensure_list(control.architecture) or control.architecture == "all" or a["package_arch"] == "all")]
if len(selected) > 1:
if version_prefix:
target_version = None
for app_file in all_files_dict:
latest_bin = app_file["bin_name"]
if not latest_bin.startswith(f"{store_package_name}_"): # ignore other files
continue
if latest_bin.split(".")[-1].lower().startswith("e"): # ignore encrypted UWP apps
continue
if version_prefix is not None and Version(app_file["version"], len(version_prefix.split("."))) == Version(
version_prefix, len(version_prefix.split(".")) and target_version < Version(app_file["version"]) #Find the file matching version_prefix
):
target_version=app_file["version"]
break
selected = [a for a in selected if target_version == a["version"] and store_package_name in a["bin_name"]]
else:
higher_version = "0"
for a in all_files_dict:
if any(
Version(a["version"], len(ignored_version.split("."))) == Version(ignored_version, len(ignored_version.split(".")))
for ignored_version in ignored_versions #ignore files in ignored_versions
):
continue
if Version(higher_version) < Version(a["version"]) and store_package_name in a["bin_name"]: # Select the higher version
higher_version = a["version"]
continue
selected = [a for a in selected if higher_version == a["version"] and store_package_name in a["bin_name"]]
return selected
def ask_control_categories():
"""Requesting that the user supply package categories for the control.categories field if empty or Template is selected"""
if control.categories == "Template":
categories = waptguihelper.grid_dialog(
"Select package categories",
[
"Internet",
"Utilities",
"Messaging",
"Security",
"System and network",
"Media",
"Development",
"Office",
"Drivers",
"Education",
"Configuration",
"CAD",
"Template",
"Dependencies",
"Extensions",
],
waptguihelper.GRT_SELECTED,
)
if categories:
control.categories = ",".join([a["unknown"] for a in categories])
else:
control.categories = ""
control.save_control_to_wapt()
return control.categories
def ask_control_architecture(package_arch):
"""Requesting that the user supply package architecture for the control.architecture field if empty or Template is selected"""
if control.categories == "Template":
architecture = waptguihelper.grid_dialog(
f"Package Architecture available : {package_arch}",
[
"all",
"x64",
"x86",
"arm",
"arm64",
],
waptguihelper.GRT_SELECTED,
)
if architecture:
control.architecture = ",".join([a["unknown"] for a in architecture])
else:
control.architecture = package_arch
control.save_control_to_wapt()
return control.architecture
def ask_control_package(control_package, conditionnal_package_name=None, remove_base_files=False):
"""Requesting that the user provide a package name to be entered into the control.package field, and offering the possibility of removing the base files (icon.png and changelog.txt) for template package usage
Args:
control_package (str) : prefilled control_package (default: actual control_package)
conditionnal_package_name (str) : only ask when the control.package contains conditionnal_package_name (default: always ask for control_package)
remove_base_files (bool) : removes base files if parameter is True and conditionnal_package_name is provided (default: False)
"""
if conditionnal_package_name is None or conditionnal_package_name in control.package:
control.package = waptguihelper.input_dialog(control.package, "You can redefine the package name", control_package)
control.save_control_to_wapt()
# Removing template files
if conditionnal_package_name in control.package and remove_base_files:
if isfile("WAPT\\changelog.txt"):
remove_file("WAPT\\changelog.txt")
if isfile("WAPT\\icon.png"):
remove_file("WAPT\\icon.png")
return control.package
def ask_control_name(control_name, conditionnal_package_name=None):
"""Requesting that the user provide a package name to be entered into control.name field
Args:
control_name (str) : prefilled control_name (default: control.name)
conditionnal_package_name (str) : only ask when the control.name contains conditionnal_package_name (default: always ask for control_name)
"""
if conditionnal_package_name is None or conditionnal_package_name in control.name:
control.name = waptguihelper.input_dialog(control.name, "You can redefine the name for the self-service", control_name)
control.save_control_to_wapt()
return control.name
def ask_control_description(blank_str=None, description_from_store=""):
"""Requesting that the user supply package description for the control.description field
Args:
blank_str (str): The description will be cleared if it includes the specified string to avoid using the template description (default: do not clear description)
"""
if not control.description:
control.description = ask_dialog("Description", "Please fill the description", description_from_store)
control.save_control_to_wapt()
if blank_str is not None and blank_str in control.description:
control.description = ""
control.save_control_to_wapt()
return control.description
def ask_dialog(title, text, default="", stay_on_top=False):
"""This function opens a dialog box with a action input"""
# - Title: the title for the dialog
# - Text: indicates which value the user should type
# - Default: the default value, if the user do not want to type any
# Optional:
# - StayOnTop: indicates if the form will always stay on top - default is False
return waptguihelper.input_dialog(title, text, default, stay_on_top)
def get_description(dict):
# get the description from the dict
return dict["Payload"]["Skus"][0]["Description"]
def make_dependency_dict(appx_package_name):
#Make dependendy dict directly from binary file
ms_app_db = {}
for ms_app_file in (
glob.glob(f"{appx_package_name}*.appxbundle")
+ glob.glob(f"{appx_package_name}*.msixbundle")
+ glob.glob(f"{appx_package_name}*.appx")
+ glob.glob(f"{appx_package_name}*.msix")
):
data_dict = {}
before_dependencies = []
sub_dependencies = []
dependency_data_dict = None
manifest = None
bundle_manifest = None
manifest_path = makepath(basedir,"AppxManifest.xml")
# Get AppxManifest, contains dependencies list
unzip_with_7zip(ms_app_file, ".", "AppxManifest.xml", False)
if isfile(manifest_path):
manifest = manifest_path
bundle_manifest_path = makepath(basedir,"AppxBundleManifest.xml")
# Get AppxBundleManifest, contains dependencies list
unzip_with_7zip(ms_app_file, ".", "AppxMetadata\\AppxBundleManifest.xml", False)
if isfile(bundle_manifest_path):
bundle_manifest = bundle_manifest_path
ms_app_info = {
"FileName": ms_app_file,
}
if not manifest:
if bundle_manifest:
with open(bundle_manifest, encoding="utf8") as xml_file:
data_dict = xmltodict.parse(xml_file.read(), attr_prefix="")
if type(data_dict["Bundle"]["Packages"]["Package"]) == dict:
data_dict["Bundle"]["Packages"]["Package"] = [dict(data_dict["Bundle"]["Packages"]["Package"])]
for app_pkg in list(data_dict["Bundle"]["Packages"]["Package"]):
if app_pkg["Type"] == "application":
before_dependency_info = {
"FileName": app_pkg["FileName"],
"Version": app_pkg["Version"],
"Architecture": app_pkg["Architecture"],
}
before_dependencies.append(before_dependency_info)
if before_dependencies:
for dependency in before_dependencies:
unzip(
ms_app_file,
".",
makepath(
dependency["FileName"],
),
False,
)
unzip(dependency["FileName"], ".", makepath("AppxManifest.xml"), False)
manifest = manifest_path = makepath(basedir,"AppxManifest.xml")
if isfile(dependency["FileName"]):
remove_file(dependency["FileName"])
with open(manifest, encoding="utf8") as xml_file:
dependency_data_dict = xmltodict.parse(xml_file.read(), attr_prefix="")
if dependency_data_dict["Package"].get("Dependencies"):
if dependency_data_dict["Package"]["Dependencies"].get("PackageDependency"):
sub_dependencies = list(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"])
if type(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"]) == dict:
sub_dependencies = [dict(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"])]
if type(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"]) == list:
sub_dependencies = dependency_data_dict["Package"]["Dependencies"]["PackageDependency"]
if dependency_data_dict["Package"]["Dependencies"].get("TargetDeviceFamily"):
if not "TargetDeviceFamily" in ms_app_info:
if type(dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"]) == dict:
ms_app_info["TargetDeviceFamily"] = [dict(dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"])]
if type(dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"]) == list:
ms_app_info["TargetDeviceFamily"] = dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"]
else:
error("nothing to parse")
else:
unzip_with_7zip(ms_app_file, ".", makepath("AppxManifest.xml"), False)
manifest = manifest_path = makepath(basedir,"AppxManifest.xml")
with open(manifest, encoding="utf8") as xml_file:
dependency_data_dict = xmltodict.parse(xml_file.read(), attr_prefix="")
if dependency_data_dict["Package"].get("Dependencies"):
if "PackageDependency" in dependency_data_dict["Package"]["Dependencies"]:
sub_dependencies = list(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"])
if type(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"]) == dict:
sub_dependencies = [dict(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"])]
if type(dependency_data_dict["Package"]["Dependencies"]["PackageDependency"]) == list:
sub_dependencies = dependency_data_dict["Package"]["Dependencies"]["PackageDependency"]
if "TargetDeviceFamily" in dependency_data_dict["Package"]["Dependencies"]:
if not "TargetDeviceFamily" in ms_app_info:
if type(dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"]) == dict:
ms_app_info["TargetDeviceFamily"] = [dict(dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"])]
if type(dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"]) == list:
ms_app_info["TargetDeviceFamily"] = dependency_data_dict["Package"]["Dependencies"]["TargetDeviceFamily"]
else:
ms_app_info.update({"Dependencies": None})
if ms_app_info.get("TargetDeviceFamily"):
ms_app_info.update(
{
"MinVersion": ms_app_info["TargetDeviceFamily"][0]["MinVersion"],
"MaxVersionTested": ms_app_info["TargetDeviceFamily"][0]["MaxVersionTested"],
}
)
elif dependency_data_dict is not None and dependency_data_dict["Package"].get("Prerequisites"):
ms_app_info.update(
{
"MinVersion": dependency_data_dict["Package"]["Prerequisites"]["OSMinVersion"],
"MaxVersionTested": dependency_data_dict["Package"]["Prerequisites"]["OSMaxVersionTested"],
}
)
else:
ms_app_info.update({"TargetDeviceFamily": None})
if data_dict:
ms_app_info.update(data_dict["Bundle"]["Identity"])
else:
ms_app_info.update(dependency_data_dict["Package"]["Identity"])
if "ProcessorArchitecture" in ms_app_info:
ms_app_info.update({"Architecture": ms_app_info["ProcessorArchitecture"]})
if before_dependencies:
ms_app_info["b4:Dependencies"] = before_dependencies
else:
ms_app_info.update({"b4:Dependencies": None})
if sub_dependencies:
ms_app_info["Dependencies"] = sub_dependencies
else:
ms_app_info.update({"Dependencies": None})
ms_app_db[ms_app_file] = ms_app_info
for xml_file in glob.glob("AppxManifest.xml") + glob.glob("AppxBundleManifest.xml"):
remove_file(xml_file)
return ms_app_db
def get_newer_uwp_depency(dependency_dict, all_files_dict, version_prefix=None, min_version=None):
"""Returns a list of the bin_name of the latest required dependencies"""
newer_version = "0"
for uwp_app in all_files_dict:
if not uwp_app["bin_name"].startswith(f'{dependency_dict["Name"]}_'):
continue
if version_prefix is not None and Version(uwp_app["version"], len(version_prefix.split("."))) != Version(
version_prefix, len(version_prefix.split("."))
):
continue
if Version(newer_version) < Version(uwp_app["version"]):
newer_uwp_depency = uwp_app
newer_version = uwp_app["version"]
return newer_uwp_depency["bin_name"].split(newer_version + "_")[0] + newer_version + "_"
def download_uwp_dependency(uwp_app_dict,all_files_dict,proxies):
#Return first dict from list of dict
newer_uwp_app = next(iter(uwp_app_dict.values()))
# Downloading dependency files
dependencies_to_download = []
if newer_uwp_app["Dependencies"]:
for dependency in newer_uwp_app["Dependencies"]:
#Find in all_files_dict, dependency needed in binary
dependencies_to_download.append(get_newer_uwp_depency(dependency, all_files_dict, min_version=dependency["MinVersion"]))
if dependencies_to_download:
#Trigger warning that nothing will be download
if debug_mode:
print("\nNO DOWNLOADED DEPENDENCY - ONLY PRINTING\n")
for dependency_file in all_files_dict:
latest_bin = dependency_file["bin_name"]
if not True in [d in latest_bin for d in dependencies_to_download]: # ignore unecessary dependencies
continue
if not "all" in ensure_list(control.architecture):
if not get_uwp_filename_arch(latest_bin) in ensure_list(control.architecture) and not "neutral" in latest_bin:
if isfile(latest_bin):
remove_file(latest_bin)
continue
if latest_bin.split(".")[-1].lower().startswith("e"): # ignore encrypted uwp apps
continue
#Only printing the dependencie without downloading
if debug_mode:
print(f"Dependencie to download : {latest_bin}")
continue
download_url = dependency_file["download_url"]
if download_url.split("/")[2].endswith("microsoft.com"):
if not isfile(latest_bin):
print("Downloading: %s" % latest_bin)
wget(download_url, latest_bin, proxies=proxies)
else:
print("Binary is present: %s" % latest_bin)
else:
error("ERROR: The retrieved URL will not download from microsoft's servers")
return newer_uwp_app
def get_uwp_filename_arch(appx_filename, appx_package_name=None):
"""
Returns the architecture of a Universal Windows Platform (UWP) app based on the provided 'appx_filename'
and optionally, the 'appx_package_name'.
Args:
appx_filename (str): The filename of the UWP app package.
appx_package_name (str, optional): The package name of the UWP app. Defaults to None.
Returns:
str: The architecture of the UWP app, which can be "x64", "arm64", "x86", or "all" (if no specific
architecture is detected).
"""
if not appx_package_name:
appx_package_name = None
if len(glob.glob(f'{appx_package_name}*{appx_filename.split("_")[1]}*')) > 1:
pass
elif "arm64" in appx_filename:
return "arm64"
elif "arm" in appx_filename:
return "arm"
elif "x64" in appx_filename:
return "x64"
if appx_filename is not None and "x86" in appx_filename and (appx_package_name is None or not appx_package_name in appx_filename):
return "x86"
return "all"
01ca7fe94636e5a08fcb73849d3b5df25d51e2c82f4dd1a08f01798b25899819 : WAPT/certificate.crt
12e2100a55be29333d04466cf4624c0519ec7ee76169a6e787b5234daaa9a11e : WAPT/control
d05608294b26da683b6efc641070d4828da3465475615d4d24f4a78a7d07dec9 : WAPT/icon.png
87370426807b83b7916c4f43942cd9bf71b1eeaf3b44728d241efe107927e3a2 : data/xml/FE3FileUrl.xml
2d3dd983c9d83c2464afaa85ab49ad5c78a537a131ec61e0c32f6b446bed4f55 : data/xml/GetCookie.xml
f8a4681fbeafb4ddcaac37b406374143900d8885b59ba7a7a0ec782d79bacd9b : data/xml/WUIDRequest.xml
df3af8cef269b261696fb14e7f55b681a0c03a913434223b486bbdb7851449a7 : luti.json
b33c87d60a9c868ec239b7e6a62575151628f29f45e4307ef4a17298dc185008 : microsoft-todos/Microsoft.NET.Native.Framework.2.2_2.2.29512.0_arm64__8wekyb3d8bbwe_e01cabbf-a9f5-41b0-b2c5-062f0b716b34.appx
bdf9c70e4966a4f7ce00ca7fc5179962239514be879f2aa3c1794076b7ff9bc6 : microsoft-todos/Microsoft.NET.Native.Framework.2.2_2.2.29512.0_arm__8wekyb3d8bbwe_dda33e1d-c984-4834-b6be-96919a27f1dc.appx
c195047f4af9c3ff3d92c40e55a901cf7cfa08a679b8ca2543bb104bab2f7ff1 : microsoft-todos/Microsoft.NET.Native.Framework.2.2_2.2.29512.0_x64__8wekyb3d8bbwe_4dd135ac-f0f3-4ae8-b910-14e9839b77cf.appx
e4c5dff287b59e27eb9da6ccd02ed1c76d3f6001eceb0f20e71eebd507b3aad2 : microsoft-todos/Microsoft.NET.Native.Framework.2.2_2.2.29512.0_x86__8wekyb3d8bbwe_76864380-9ee8-4f9b-907f-7ccce9c7ca57.appx
47de27af62a9a31d123a522c4a74056b8a10d15307de9ebead0adf684f3df45d : microsoft-todos/Microsoft.NET.Native.Runtime.2.2_2.2.28604.0_arm64__8wekyb3d8bbwe_1b258fca-ed02-4324-bb41-0d0f51d1de11.appx
7ccf4ef1815d479570fdcf4e462bd7d80f1bc9ae3c39c93220fdb40cc0ebaca9 : microsoft-todos/Microsoft.NET.Native.Runtime.2.2_2.2.28604.0_arm__8wekyb3d8bbwe_44664ff1-3b9f-4984-83c4-6737a49f4034.appx
27fae660d8c609e020703bb463b6601e62811dc75db19975bcb7d68d959b527d : microsoft-todos/Microsoft.NET.Native.Runtime.2.2_2.2.28604.0_x64__8wekyb3d8bbwe_6c5ccbb5-88d3-4e90-ac79-3880010d81af.appx
ec7e811dca0cb511c69859c65aa1d94ccd5cc3cab3ec7f9d6bab95abb03149a8 : microsoft-todos/Microsoft.NET.Native.Runtime.2.2_2.2.28604.0_x86__8wekyb3d8bbwe_bc2b77dd-5801-4779-b34d-77119629866c.appx
83576c8e52c1d9d57fbab4747176fd5cb76fce8addaff35d746e2373cefd4a19 : microsoft-todos/Microsoft.Services.Store.Engagement_10.0.23012.0_arm64__8wekyb3d8bbwe_a6cb74c6-0dad-4551-a8a4-87c995c63ac1.appx
a111d95aa5563363ae0db906e3e7127d35bfd0baf128534e00db5321bcc904c7 : microsoft-todos/Microsoft.Services.Store.Engagement_10.0.23012.0_arm__8wekyb3d8bbwe_65fea734-3d8f-4168-8a47-f22b848b4a90.appx
0133757628606a9c73f6265e235ad6fb6e80973b89a04b8c0208f93786a24f93 : microsoft-todos/Microsoft.Services.Store.Engagement_10.0.23012.0_x64__8wekyb3d8bbwe_3a37e79e-03be-4796-8d25-00a6cd45cc97.appx
f471c4face0f488088d55e375496144ac637b01ab722b4d220d406f56d2c8504 : microsoft-todos/Microsoft.Services.Store.Engagement_10.0.23012.0_x86__8wekyb3d8bbwe_511c6cff-ed08-4366-a308-56cdb4e4d3e5.appx
f2a68a792309087b0c80a169f41ec34d6bb50c6c083a0911a5bdae9db8abbe01 : microsoft-todos/Microsoft.Todos_2.148.3611.0_neutral___8wekyb3d8bbwe_3d32b0d8-e3cf-403b-8a2a-39568f6dab74.msixbundle
b364b22089d1ce9ff70691dcebe75d14f81817fdcd3c312aa4b7b6766942c358 : microsoft-todos/Microsoft.UI.Xaml.2.8_8.2501.31001.0_arm64__8wekyb3d8bbwe_bcedb4c1-c5ea-476c-b810-1ca6b97ceb31.appx
e90549a53002c86f75037cce29e89a886ce57a9f7ac8f3efe51bc08e6b8f4694 : microsoft-todos/Microsoft.UI.Xaml.2.8_8.2501.31001.0_arm__8wekyb3d8bbwe_adf6261a-4006-4d53-b856-92f8e70ba95f.appx
2e7c2397d95f415ff97e26e3a63d52be4c2e4be3f885517e522b8e94e37b4fe2 : microsoft-todos/Microsoft.UI.Xaml.2.8_8.2501.31001.0_x64__8wekyb3d8bbwe_baf6388f-cf6b-4980-a220-0f1cc8c2f857.appx
34377ebe0cb380f5754f2bbcc13bff1fdfa02d8cc83cc8e237e635d9b8b514e7 : microsoft-todos/Microsoft.UI.Xaml.2.8_8.2501.31001.0_x86__8wekyb3d8bbwe_d8e5a058-a962-49d1-a201-a691c531ba6d.appx
831118f2f5cac8e29284e4ecc23bf4236436ccc2ea4a7c225c4946f8cfd29dd3 : microsoft-todos/Microsoft.VCLibs.140.00.UWPDesktop_14.0.33728.0_arm64__8wekyb3d8bbwe_8b19ae8f-1fe9-45e1-aed3-1672ee33bf71.appx
df3d87926a1d9780b7a76e145c3eae17f58e825fd7a9c1b2017608929da0d86f : microsoft-todos/Microsoft.VCLibs.140.00.UWPDesktop_14.0.33728.0_arm__8wekyb3d8bbwe_ba323f94-e23e-40ef-a6b5-917c577c939a.appx
077a3d1a5d0622bd3004dca85f5e192d6e98ec79b83d4aa06766759ea6c09c3d : microsoft-todos/Microsoft.VCLibs.140.00.UWPDesktop_14.0.33728.0_x64__8wekyb3d8bbwe_27ca12bc-f81d-45ff-95d0-12ad79f15735.appx
596c7175c0b0fa27ed292a44f4681f687720a77e34e007bf6c403d1940e46005 : microsoft-todos/Microsoft.VCLibs.140.00.UWPDesktop_14.0.33728.0_x86__8wekyb3d8bbwe_2a0007f4-9769-4709-9244-a28f54f70828.appx
c73d0f55dda331f9dcbefc99ff5a420b62120773d2917387639382aa478533ee : microsoft-todos/Microsoft.VCLibs.140.00_14.0.33519.0_arm64__8wekyb3d8bbwe_4aa2f4c4-ecd2-41d1-8089-304a95524359.appx
2c422523ff693689a84c109585cfa444143ac3b6b7a5cadf4858afc6a3cb750f : microsoft-todos/Microsoft.VCLibs.140.00_14.0.33519.0_arm__8wekyb3d8bbwe_f52e53fc-9664-4720-800e-a637f6f5c125.appx
9c17b521f9d690a1f504da5108ed6eec5669eb3a8fd1331eef43e40d84e74283 : microsoft-todos/Microsoft.VCLibs.140.00_14.0.33519.0_x64__8wekyb3d8bbwe_c452d4ef-2486-4efe-9c99-36b3d23e0160.appx
7ba6ea7bc32cd58b7e0683da588796086accfb74efb7a3e525e9f8014d2ad663 : microsoft-todos/Microsoft.VCLibs.140.00_14.0.33519.0_x86__8wekyb3d8bbwe_9683459a-02fa-4bd6-9ae6-af8ddfbeef35.appx
cb436b4a7435f6bdd486a53835c8444c23c10db8597f130c646e6c3c87075847 : setup.py
7475ddecb1a5cbd889050d734734a423183be5811555f41f9572a02e6b533513 : setupdevhelpers.py
a4d71f3c52cdbec3ddded73e6ea0c31e5ea228b7466179272ab8ffae2e0ce0a9 : update_package.py
364e01205533a0c6907fb9e8a8f70353985ae81ead35b71aade9a4253184f847 : xmltodict.py