Get some amount of Windows support
Dadoum

Dadoum commited on 2026-01-26 16:51:18
Showing 25 changed files, with 1058 additions and 496 deletions.

... ...
@@ -164,6 +164,12 @@ dependencies = [
164 164
  "windows-sys 0.60.2",
165 165
 ]
166 166
 
167
+[[package]]
168
+name = "anyhow"
169
+version = "1.0.100"
170
+source = "registry+https://github.com/rust-lang/crates.io-index"
171
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
172
+
167 173
 [[package]]
168 174
 name = "apple-account"
169 175
 version = "0.1.0"
... ...
@@ -183,12 +189,13 @@ dependencies = [
183 189
  "plist",
184 190
  "plist-macros",
185 191
  "rand",
186
- "reqwest 0.12.27",
192
+ "reqwest 0.13.1",
187 193
  "serde",
188 194
  "sha2",
189 195
  "srp",
190 196
  "sys-locale",
191 197
  "thiserror 2.0.17",
198
+ "uuid",
192 199
 ]
193 200
 
194 201
 [[package]]
... ...
@@ -217,6 +224,19 @@ dependencies = [
217 224
  "syn 1.0.109",
218 225
 ]
219 226
 
227
+[[package]]
228
+name = "async-compression"
229
+version = "0.4.36"
230
+source = "registry+https://github.com/rust-lang/crates.io-index"
231
+checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37"
232
+dependencies = [
233
+ "compression-codecs",
234
+ "compression-core",
235
+ "futures-core",
236
+ "pin-project-lite",
237
+ "tokio",
238
+]
239
+
220 240
 [[package]]
221 241
 name = "atomic-waker"
222 242
 version = "1.1.2"
... ...
@@ -329,6 +349,12 @@ dependencies = [
329 349
  "backtrace",
330 350
 ]
331 351
 
352
+[[package]]
353
+name = "base16ct"
354
+version = "1.0.0"
355
+source = "registry+https://github.com/rust-lang/crates.io-index"
356
+checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6"
357
+
332 358
 [[package]]
333 359
 name = "base64"
334 360
 version = "0.21.7"
... ...
@@ -542,6 +568,12 @@ dependencies = [
542 568
  "cc",
543 569
 ]
544 570
 
571
+[[package]]
572
+name = "cmov"
573
+version = "0.5.2"
574
+source = "registry+https://github.com/rust-lang/crates.io-index"
575
+checksum = "de0758edba32d61d1fd9f4d69491b47604b91ee2f7e6b33de7e54ca4ebe55dc3"
576
+
545 577
 [[package]]
546 578
 name = "colorchoice"
547 579
 version = "1.0.4"
... ...
@@ -558,6 +590,23 @@ dependencies = [
558 590
  "memchr",
559 591
 ]
560 592
 
593
+[[package]]
594
+name = "compression-codecs"
595
+version = "0.4.35"
596
+source = "registry+https://github.com/rust-lang/crates.io-index"
597
+checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2"
598
+dependencies = [
599
+ "compression-core",
600
+ "flate2",
601
+ "memchr",
602
+]
603
+
604
+[[package]]
605
+name = "compression-core"
606
+version = "0.4.31"
607
+source = "registry+https://github.com/rust-lang/crates.io-index"
608
+checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d"
609
+
561 610
 [[package]]
562 611
 name = "const-oid"
563 612
 version = "0.10.1"
... ...
@@ -614,15 +663,28 @@ version = "0.8.21"
614 663
 source = "registry+https://github.com/rust-lang/crates.io-index"
615 664
 checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
616 665
 
666
+[[package]]
667
+name = "crypto-bigint"
668
+version = "0.7.0-rc.22"
669
+source = "registry+https://github.com/rust-lang/crates.io-index"
670
+checksum = "053c3561863ce55e3226ecc48b08679f4b66cb1b92b9afb42c2c402dfe8b9b51"
671
+dependencies = [
672
+ "ctutils",
673
+ "getrandom 0.4.0-rc.1",
674
+ "num-traits",
675
+ "rand_core 0.10.0-rc-6",
676
+ "serdect",
677
+]
678
+
617 679
 [[package]]
618 680
 name = "crypto-common"
619
-version = "0.2.0-rc.8"
681
+version = "0.2.0-rc.13"
620 682
 source = "registry+https://github.com/rust-lang/crates.io-index"
621
-checksum = "e6165b8029cdc3e765b74d3548f85999ee799d5124877ce45c2c85ca78e4d4aa"
683
+checksum = "c7722afd27468475c9b6063dc03a57ef2ca833816981619f8ebe64d38d207eef"
622 684
 dependencies = [
623
- "getrandom 0.4.0-rc.0",
685
+ "getrandom 0.4.0-rc.1",
624 686
  "hybrid-array",
625
- "rand_core 0.10.0-rc-3",
687
+ "rand_core 0.10.0-rc-6",
626 688
 ]
627 689
 
628 690
 [[package]]
... ...
@@ -634,6 +696,15 @@ dependencies = [
634 696
  "cipher",
635 697
 ]
636 698
 
699
+[[package]]
700
+name = "ctutils"
701
+version = "0.4.0"
702
+source = "registry+https://github.com/rust-lang/crates.io-index"
703
+checksum = "1005a6d4446f5120ef475ad3d2af2b30c49c2c9c6904258e3bb30219bebed5e4"
704
+dependencies = [
705
+ "cmov",
706
+]
707
+
637 708
 [[package]]
638 709
 name = "cxx"
639 710
 version = "1.0.117"
... ...
@@ -674,9 +745,9 @@ dependencies = [
674 745
 
675 746
 [[package]]
676 747
 name = "digest"
677
-version = "0.11.0-rc.5"
748
+version = "0.11.0-rc.9"
678 749
 source = "registry+https://github.com/rust-lang/crates.io-index"
679
-checksum = "ebf9423bafb058e4142194330c52273c343f8a5beb7176d052f0e73b17dd35b9"
750
+checksum = "bff8de092798697546237a3a701e4174fe021579faec9b854379af9bf1e31962"
680 751
 dependencies = [
681 752
  "block-buffer",
682 753
  "const-oid",
... ...
@@ -802,6 +873,10 @@ dependencies = [
802 873
  "windows-sys 0.61.2",
803 874
 ]
804 875
 
876
+[[package]]
877
+name = "fairplay"
878
+version = "0.1.0"
879
+
805 880
 [[package]]
806 881
 name = "fastrand"
807 882
 version = "2.3.0"
... ...
@@ -831,19 +906,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
831 906
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
832 907
 
833 908
 [[package]]
834
-name = "foreign-types"
835
-version = "0.3.2"
836
-source = "registry+https://github.com/rust-lang/crates.io-index"
837
-checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
838
-dependencies = [
839
- "foreign-types-shared",
840
-]
841
-
842
-[[package]]
843
-name = "foreign-types-shared"
844
-version = "0.1.1"
909
+name = "foldhash"
910
+version = "0.1.5"
845 911
 source = "registry+https://github.com/rust-lang/crates.io-index"
846
-checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
912
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
847 913
 
848 914
 [[package]]
849 915
 name = "form_urlencoded"
... ...
@@ -854,14 +920,6 @@ dependencies = [
854 920
  "percent-encoding",
855 921
 ]
856 922
 
857
-[[package]]
858
-name = "fpsd-proxy"
859
-version = "0.1.0"
860
-dependencies = [
861
- "dlopen2",
862
- "objc2",
863
-]
864
-
865 923
 [[package]]
866 924
 name = "fs_extra"
867 925
 version = "1.3.0"
... ...
@@ -986,15 +1044,16 @@ dependencies = [
986 1044
 
987 1045
 [[package]]
988 1046
 name = "getrandom"
989
-version = "0.4.0-rc.0"
1047
+version = "0.4.0-rc.1"
990 1048
 source = "registry+https://github.com/rust-lang/crates.io-index"
991
-checksum = "3b99f0d993a2b9b97b9a201193aa8ad21305cde06a3be9a7e1f8f4201e5cc27e"
1049
+checksum = "74f70a332ddf75e5e5e43284304179ba02f391f82f692f030b08a8378adf3c99"
992 1050
 dependencies = [
993 1051
  "cfg-if 1.0.4",
994 1052
  "libc",
995 1053
  "r-efi",
996
- "rand_core 0.10.0-rc-3",
1054
+ "rand_core 0.10.0-rc-6",
997 1055
  "wasip2",
1056
+ "wasip3",
998 1057
 ]
999 1058
 
1000 1059
 [[package]]
... ...
@@ -1093,6 +1152,15 @@ version = "0.12.3"
1093 1152
 source = "registry+https://github.com/rust-lang/crates.io-index"
1094 1153
 checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
1095 1154
 
1155
+[[package]]
1156
+name = "hashbrown"
1157
+version = "0.15.5"
1158
+source = "registry+https://github.com/rust-lang/crates.io-index"
1159
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
1160
+dependencies = [
1161
+ "foldhash",
1162
+]
1163
+
1096 1164
 [[package]]
1097 1165
 name = "hashbrown"
1098 1166
 version = "0.16.0"
... ...
@@ -1105,6 +1173,12 @@ version = "0.4.1"
1105 1173
 source = "registry+https://github.com/rust-lang/crates.io-index"
1106 1174
 checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
1107 1175
 
1176
+[[package]]
1177
+name = "heck"
1178
+version = "0.5.0"
1179
+source = "registry+https://github.com/rust-lang/crates.io-index"
1180
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
1181
+
1108 1182
 [[package]]
1109 1183
 name = "hermit-abi"
1110 1184
 version = "0.1.19"
... ...
@@ -1128,9 +1202,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
1128 1202
 
1129 1203
 [[package]]
1130 1204
 name = "hmac"
1131
-version = "0.13.0-rc.3"
1205
+version = "0.13.0-rc.4"
1132 1206
 source = "registry+https://github.com/rust-lang/crates.io-index"
1133
-checksum = "f1c597ac7d6cc8143e30e83ef70915e7f883b18d8bec2e2b2bce47f5bbb06d57"
1207
+checksum = "d9956e202a691c5c86c60303a421f66f93f44b29433407b7c43cf2bebadc750e"
1134 1208
 dependencies = [
1135 1209
  "digest",
1136 1210
 ]
... ...
@@ -1290,28 +1364,11 @@ dependencies = [
1290 1364
  "http 1.3.1",
1291 1365
  "hyper 1.7.0",
1292 1366
  "hyper-util",
1293
- "rustls 0.23.33",
1367
+ "rustls 0.23.36",
1294 1368
  "rustls-pki-types",
1295 1369
  "tokio",
1296 1370
  "tokio-rustls 0.26.4",
1297 1371
  "tower-service",
1298
- "webpki-roots 1.0.4",
1299
-]
1300
-
1301
-[[package]]
1302
-name = "hyper-tls"
1303
-version = "0.6.0"
1304
-source = "registry+https://github.com/rust-lang/crates.io-index"
1305
-checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
1306
-dependencies = [
1307
- "bytes",
1308
- "http-body-util",
1309
- "hyper 1.7.0",
1310
- "hyper-util",
1311
- "native-tls",
1312
- "tokio",
1313
- "tokio-native-tls",
1314
- "tower-service",
1315 1372
 ]
1316 1373
 
1317 1374
 [[package]]
... ...
@@ -1450,6 +1507,12 @@ dependencies = [
1450 1507
  "zerovec",
1451 1508
 ]
1452 1509
 
1510
+[[package]]
1511
+name = "id-arena"
1512
+version = "2.3.0"
1513
+source = "registry+https://github.com/rust-lang/crates.io-index"
1514
+checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
1515
+
1453 1516
 [[package]]
1454 1517
 name = "idna"
1455 1518
 version = "1.1.0"
... ...
@@ -1490,6 +1553,8 @@ checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
1490 1553
 dependencies = [
1491 1554
  "equivalent",
1492 1555
  "hashbrown 0.16.0",
1556
+ "serde",
1557
+ "serde_core",
1493 1558
 ]
1494 1559
 
1495 1560
 [[package]]
... ...
@@ -1643,6 +1708,12 @@ version = "1.3.0"
1643 1708
 source = "registry+https://github.com/rust-lang/crates.io-index"
1644 1709
 checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
1645 1710
 
1711
+[[package]]
1712
+name = "leb128fmt"
1713
+version = "0.1.0"
1714
+source = "registry+https://github.com/rust-lang/crates.io-index"
1715
+checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
1716
+
1646 1717
 [[package]]
1647 1718
 name = "libc"
1648 1719
 version = "0.2.176"
... ...
@@ -1659,6 +1730,14 @@ dependencies = [
1659 1730
  "winapi",
1660 1731
 ]
1661 1732
 
1733
+[[package]]
1734
+name = "library-coreadi"
1735
+version = "0.1.0"
1736
+dependencies = [
1737
+ "adi",
1738
+ "dlopen2",
1739
+]
1740
+
1662 1741
 [[package]]
1663 1742
 name = "lief"
1664 1743
 version = "0.17.1"
... ...
@@ -1824,23 +1903,6 @@ dependencies = [
1824 1903
  "cxx",
1825 1904
 ]
1826 1905
 
1827
-[[package]]
1828
-name = "native-tls"
1829
-version = "0.2.14"
1830
-source = "registry+https://github.com/rust-lang/crates.io-index"
1831
-checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
1832
-dependencies = [
1833
- "libc",
1834
- "log",
1835
- "openssl",
1836
- "openssl-probe 0.1.6",
1837
- "openssl-sys",
1838
- "schannel",
1839
- "security-framework 2.11.1",
1840
- "security-framework-sys",
1841
- "tempfile",
1842
-]
1843
-
1844 1906
 [[package]]
1845 1907
 name = "nom"
1846 1908
 version = "5.1.3"
... ...
@@ -1896,21 +1958,6 @@ dependencies = [
1896 1958
  "autocfg",
1897 1959
 ]
1898 1960
 
1899
-[[package]]
1900
-name = "objc2"
1901
-version = "0.6.3"
1902
-source = "registry+https://github.com/rust-lang/crates.io-index"
1903
-checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05"
1904
-dependencies = [
1905
- "objc2-encode",
1906
-]
1907
-
1908
-[[package]]
1909
-name = "objc2-encode"
1910
-version = "4.1.0"
1911
-source = "registry+https://github.com/rust-lang/crates.io-index"
1912
-checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
1913
-
1914 1961
 [[package]]
1915 1962
 name = "object"
1916 1963
 version = "0.37.3"
... ...
@@ -1932,56 +1979,12 @@ version = "1.70.1"
1932 1979
 source = "registry+https://github.com/rust-lang/crates.io-index"
1933 1980
 checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
1934 1981
 
1935
-[[package]]
1936
-name = "openssl"
1937
-version = "0.10.75"
1938
-source = "registry+https://github.com/rust-lang/crates.io-index"
1939
-checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328"
1940
-dependencies = [
1941
- "bitflags 2.10.0",
1942
- "cfg-if 1.0.4",
1943
- "foreign-types",
1944
- "libc",
1945
- "once_cell",
1946
- "openssl-macros",
1947
- "openssl-sys",
1948
-]
1949
-
1950
-[[package]]
1951
-name = "openssl-macros"
1952
-version = "0.1.1"
1953
-source = "registry+https://github.com/rust-lang/crates.io-index"
1954
-checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
1955
-dependencies = [
1956
- "proc-macro2",
1957
- "quote",
1958
- "syn 2.0.107",
1959
-]
1960
-
1961
-[[package]]
1962
-name = "openssl-probe"
1963
-version = "0.1.6"
1964
-source = "registry+https://github.com/rust-lang/crates.io-index"
1965
-checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
1966
-
1967 1982
 [[package]]
1968 1983
 name = "openssl-probe"
1969 1984
 version = "0.2.0"
1970 1985
 source = "registry+https://github.com/rust-lang/crates.io-index"
1971 1986
 checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391"
1972 1987
 
1973
-[[package]]
1974
-name = "openssl-sys"
1975
-version = "0.9.111"
1976
-source = "registry+https://github.com/rust-lang/crates.io-index"
1977
-checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
1978
-dependencies = [
1979
- "cc",
1980
- "libc",
1981
- "pkg-config",
1982
- "vcpkg",
1983
-]
1984
-
1985 1988
 [[package]]
1986 1989
 name = "ouroboros"
1987 1990
 version = "0.18.5"
... ...
@@ -1999,7 +2002,7 @@ version = "0.18.5"
1999 2002
 source = "registry+https://github.com/rust-lang/crates.io-index"
2000 2003
 checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0"
2001 2004
 dependencies = [
2002
- "heck",
2005
+ "heck 0.4.1",
2003 2006
  "proc-macro2",
2004 2007
  "proc-macro2-diagnostics",
2005 2008
  "quote",
... ...
@@ -2014,9 +2017,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
2014 2017
 
2015 2018
 [[package]]
2016 2019
 name = "pbkdf2"
2017
-version = "0.13.0-rc.5"
2020
+version = "0.13.0-rc.8"
2018 2021
 source = "registry+https://github.com/rust-lang/crates.io-index"
2019
-checksum = "c015873c38594dfb7724f90b2ed912a606697393bda2d39fd83c2394301f808a"
2022
+checksum = "626220f48328b90cad8393e99e9ef80503970e6e86e77f32f7e42227972e7c2c"
2020 2023
 dependencies = [
2021 2024
  "digest",
2022 2025
  "hmac",
... ...
@@ -2046,12 +2049,6 @@ version = "0.1.0"
2046 2049
 source = "registry+https://github.com/rust-lang/crates.io-index"
2047 2050
 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
2048 2051
 
2049
-[[package]]
2050
-name = "pkg-config"
2051
-version = "0.3.32"
2052
-source = "registry+https://github.com/rust-lang/crates.io-index"
2053
-checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
2054
-
2055 2052
 [[package]]
2056 2053
 name = "plain"
2057 2054
 version = "0.2.3"
... ...
@@ -2128,6 +2125,16 @@ dependencies = [
2128 2125
  "zerocopy",
2129 2126
 ]
2130 2127
 
2128
+[[package]]
2129
+name = "prettyplease"
2130
+version = "0.2.37"
2131
+source = "registry+https://github.com/rust-lang/crates.io-index"
2132
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
2133
+dependencies = [
2134
+ "proc-macro2",
2135
+ "syn 2.0.107",
2136
+]
2137
+
2131 2138
 [[package]]
2132 2139
 name = "proc-macro-error"
2133 2140
 version = "1.0.4"
... ...
@@ -2210,7 +2217,7 @@ dependencies = [
2210 2217
  "quinn-proto",
2211 2218
  "quinn-udp",
2212 2219
  "rustc-hash 2.1.1",
2213
- "rustls 0.23.33",
2220
+ "rustls 0.23.36",
2214 2221
  "socket2 0.6.1",
2215 2222
  "thiserror 2.0.17",
2216 2223
  "tokio",
... ...
@@ -2231,7 +2238,7 @@ dependencies = [
2231 2238
  "rand",
2232 2239
  "ring",
2233 2240
  "rustc-hash 2.1.1",
2234
- "rustls 0.23.33",
2241
+ "rustls 0.23.36",
2235 2242
  "rustls-pki-types",
2236 2243
  "slab",
2237 2244
  "thiserror 2.0.17",
... ...
@@ -2300,9 +2307,9 @@ dependencies = [
2300 2307
 
2301 2308
 [[package]]
2302 2309
 name = "rand_core"
2303
-version = "0.10.0-rc-3"
2310
+version = "0.10.0-rc-6"
2304 2311
 source = "registry+https://github.com/rust-lang/crates.io-index"
2305
-checksum = "f66ee92bc15280519ef199a274fe0cafff4245d31bc39aaa31c011ad56cb1f05"
2312
+checksum = "70765ff7112b0fb2d272d24d9a2f907fc206211304328fe58b2db15a5649ef28"
2306 2313
 
2307 2314
 [[package]]
2308 2315
 name = "regex"
... ...
@@ -2382,54 +2389,10 @@ dependencies = [
2382 2389
  "wasm-bindgen",
2383 2390
  "wasm-bindgen-futures",
2384 2391
  "web-sys",
2385
- "webpki-roots 0.25.4",
2392
+ "webpki-roots",
2386 2393
  "winreg",
2387 2394
 ]
2388 2395
 
2389
-[[package]]
2390
-name = "reqwest"
2391
-version = "0.12.27"
2392
-source = "registry+https://github.com/rust-lang/crates.io-index"
2393
-checksum = "8e893f6bece5953520ddbb3f8f46f3ef36dd1fef4ee9b087c4b4a725fd5d10e4"
2394
-dependencies = [
2395
- "base64 0.22.1",
2396
- "bytes",
2397
- "encoding_rs",
2398
- "futures-core",
2399
- "h2 0.4.12",
2400
- "http 1.3.1",
2401
- "http-body 1.0.1",
2402
- "http-body-util",
2403
- "hyper 1.7.0",
2404
- "hyper-rustls 0.27.7",
2405
- "hyper-tls",
2406
- "hyper-util",
2407
- "js-sys",
2408
- "log",
2409
- "mime",
2410
- "native-tls",
2411
- "percent-encoding",
2412
- "pin-project-lite",
2413
- "quinn",
2414
- "rustls 0.23.33",
2415
- "rustls-pki-types",
2416
- "serde",
2417
- "serde_json",
2418
- "serde_urlencoded",
2419
- "sync_wrapper 1.0.2",
2420
- "tokio",
2421
- "tokio-native-tls",
2422
- "tokio-rustls 0.26.4",
2423
- "tower",
2424
- "tower-http",
2425
- "tower-service",
2426
- "url",
2427
- "wasm-bindgen",
2428
- "wasm-bindgen-futures",
2429
- "web-sys",
2430
- "webpki-roots 1.0.4",
2431
-]
2432
-
2433 2396
 [[package]]
2434 2397
 name = "reqwest"
2435 2398
 version = "0.13.1"
... ...
@@ -2453,9 +2416,11 @@ dependencies = [
2453 2416
  "percent-encoding",
2454 2417
  "pin-project-lite",
2455 2418
  "quinn",
2456
- "rustls 0.23.33",
2419
+ "rustls 0.23.36",
2457 2420
  "rustls-pki-types",
2458 2421
  "rustls-platform-verifier",
2422
+ "serde",
2423
+ "serde_urlencoded",
2459 2424
  "sync_wrapper 1.0.2",
2460 2425
  "tokio",
2461 2426
  "tokio-rustls 0.26.4",
... ...
@@ -2527,13 +2492,12 @@ dependencies = [
2527 2492
 
2528 2493
 [[package]]
2529 2494
 name = "rustls"
2530
-version = "0.23.33"
2495
+version = "0.23.36"
2531 2496
 source = "registry+https://github.com/rust-lang/crates.io-index"
2532
-checksum = "751e04a496ca00bb97a5e043158d23d66b5aabf2e1d5aa2a0aaebb1aafe6f82c"
2497
+checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
2533 2498
 dependencies = [
2534 2499
  "aws-lc-rs",
2535 2500
  "once_cell",
2536
- "ring",
2537 2501
  "rustls-pki-types",
2538 2502
  "rustls-webpki 0.103.7",
2539 2503
  "subtle",
... ...
@@ -2546,10 +2510,10 @@ version = "0.8.3"
2546 2510
 source = "registry+https://github.com/rust-lang/crates.io-index"
2547 2511
 checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
2548 2512
 dependencies = [
2549
- "openssl-probe 0.2.0",
2513
+ "openssl-probe",
2550 2514
  "rustls-pki-types",
2551 2515
  "schannel",
2552
- "security-framework 3.5.1",
2516
+ "security-framework",
2553 2517
 ]
2554 2518
 
2555 2519
 [[package]]
... ...
@@ -2582,11 +2546,11 @@ dependencies = [
2582 2546
  "jni",
2583 2547
  "log",
2584 2548
  "once_cell",
2585
- "rustls 0.23.33",
2549
+ "rustls 0.23.36",
2586 2550
  "rustls-native-certs",
2587 2551
  "rustls-platform-verifier-android",
2588 2552
  "rustls-webpki 0.103.7",
2589
- "security-framework 3.5.1",
2553
+ "security-framework",
2590 2554
  "security-framework-sys",
2591 2555
  "webpki-root-certs",
2592 2556
  "windows-sys 0.61.2",
... ...
@@ -2680,19 +2644,6 @@ dependencies = [
2680 2644
  "untrusted",
2681 2645
 ]
2682 2646
 
2683
-[[package]]
2684
-name = "security-framework"
2685
-version = "2.11.1"
2686
-source = "registry+https://github.com/rust-lang/crates.io-index"
2687
-checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
2688
-dependencies = [
2689
- "bitflags 2.10.0",
2690
- "core-foundation 0.9.4",
2691
- "core-foundation-sys",
2692
- "libc",
2693
- "security-framework-sys",
2694
-]
2695
-
2696 2647
 [[package]]
2697 2648
 name = "security-framework"
2698 2649
 version = "3.5.1"
... ...
@@ -2777,11 +2728,21 @@ dependencies = [
2777 2728
  "serde",
2778 2729
 ]
2779 2730
 
2731
+[[package]]
2732
+name = "serdect"
2733
+version = "0.4.2"
2734
+source = "registry+https://github.com/rust-lang/crates.io-index"
2735
+checksum = "9af4a3e75ebd5599b30d4de5768e00b5095d518a79fefc3ecbaf77e665d1ec06"
2736
+dependencies = [
2737
+ "base16ct",
2738
+ "serde",
2739
+]
2740
+
2780 2741
 [[package]]
2781 2742
 name = "sha1"
2782
-version = "0.11.0-rc.3"
2743
+version = "0.11.0-rc.4"
2783 2744
 source = "registry+https://github.com/rust-lang/crates.io-index"
2784
-checksum = "aa1ae819b9870cadc959a052363de870944a1646932d274a4e270f64bf79e5ef"
2745
+checksum = "9c777f0a122a53fddb0beb6e706771197000b8eb5c9f42b5b850f450ef48c788"
2785 2746
 dependencies = [
2786 2747
  "cfg-if 1.0.4",
2787 2748
  "cpufeatures",
... ...
@@ -2790,9 +2751,9 @@ dependencies = [
2790 2751
 
2791 2752
 [[package]]
2792 2753
 name = "sha2"
2793
-version = "0.11.0-rc.3"
2754
+version = "0.11.0-rc.4"
2794 2755
 source = "registry+https://github.com/rust-lang/crates.io-index"
2795
-checksum = "19d43dc0354d88b791216bb5c1bfbb60c0814460cc653ae0ebd71f286d0bd927"
2756
+checksum = "7535f94fa3339fe9e5e9be6260a909e62af97f6e14b32345ccf79b92b8b81233"
2796 2757
 dependencies = [
2797 2758
  "cfg-if 1.0.4",
2798 2759
  "cpufeatures",
... ...
@@ -2857,12 +2818,13 @@ dependencies = [
2857 2818
 
2858 2819
 [[package]]
2859 2820
 name = "srp"
2860
-version = "0.7.0-pre.0"
2861
-source = "git+https://github.com/RustCrypto/PAKEs#ce6ac89ba36ed72becdd62146ac5f52882ff9f12"
2821
+version = "0.7.0-rc.1"
2822
+source = "registry+https://github.com/rust-lang/crates.io-index"
2823
+checksum = "a0b7d0b0d27b2475e779315c09af698ac9f6d40016bc011bf3fa0f0054ce38ed"
2862 2824
 dependencies = [
2825
+ "crypto-bigint",
2826
+ "crypto-common",
2863 2827
  "digest",
2864
- "lazy_static",
2865
- "num-bigint",
2866 2828
  "subtle",
2867 2829
 ]
2868 2830
 
... ...
@@ -2899,9 +2861,11 @@ dependencies = [
2899 2861
  "android-coreadi",
2900 2862
  "apple-account",
2901 2863
  "base64 0.22.1",
2864
+ "dlopen2",
2902 2865
  "elf-loader",
2903 2866
  "env_logger 0.11.8",
2904 2867
  "libc",
2868
+ "library-coreadi",
2905 2869
  "reqwest 0.13.1",
2906 2870
  "tokio",
2907 2871
  "xcode",
... ...
@@ -3217,16 +3181,6 @@ dependencies = [
3217 3181
  "syn 2.0.107",
3218 3182
 ]
3219 3183
 
3220
-[[package]]
3221
-name = "tokio-native-tls"
3222
-version = "0.3.1"
3223
-source = "registry+https://github.com/rust-lang/crates.io-index"
3224
-checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
3225
-dependencies = [
3226
- "native-tls",
3227
- "tokio",
3228
-]
3229
-
3230 3184
 [[package]]
3231 3185
 name = "tokio-rustls"
3232 3186
 version = "0.24.1"
... ...
@@ -3243,7 +3197,7 @@ version = "0.26.4"
3243 3197
 source = "registry+https://github.com/rust-lang/crates.io-index"
3244 3198
 checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
3245 3199
 dependencies = [
3246
- "rustls 0.23.33",
3200
+ "rustls 0.23.36",
3247 3201
  "tokio",
3248 3202
 ]
3249 3203
 
... ...
@@ -3281,13 +3235,18 @@ version = "0.6.8"
3281 3235
 source = "registry+https://github.com/rust-lang/crates.io-index"
3282 3236
 checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
3283 3237
 dependencies = [
3238
+ "async-compression",
3284 3239
  "bitflags 2.10.0",
3285 3240
  "bytes",
3241
+ "futures-core",
3286 3242
  "futures-util",
3287 3243
  "http 1.3.1",
3288 3244
  "http-body 1.0.1",
3245
+ "http-body-util",
3289 3246
  "iri-string",
3290 3247
  "pin-project-lite",
3248
+ "tokio",
3249
+ "tokio-util",
3291 3250
  "tower",
3292 3251
  "tower-layer",
3293 3252
  "tower-service",
... ...
@@ -3354,6 +3313,12 @@ version = "0.1.14"
3354 3313
 source = "registry+https://github.com/rust-lang/crates.io-index"
3355 3314
 checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
3356 3315
 
3316
+[[package]]
3317
+name = "unicode-xid"
3318
+version = "0.2.6"
3319
+source = "registry+https://github.com/rust-lang/crates.io-index"
3320
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
3321
+
3357 3322
 [[package]]
3358 3323
 name = "universal-hash"
3359 3324
 version = "0.6.0-rc.4"
... ...
@@ -3401,10 +3366,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3401 3366
 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
3402 3367
 
3403 3368
 [[package]]
3404
-name = "vcpkg"
3405
-version = "0.2.15"
3369
+name = "uuid"
3370
+version = "1.19.0"
3406 3371
 source = "registry+https://github.com/rust-lang/crates.io-index"
3407
-checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
3372
+checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
3373
+dependencies = [
3374
+ "getrandom 0.3.4",
3375
+ "js-sys",
3376
+ "wasm-bindgen",
3377
+]
3408 3378
 
3409 3379
 [[package]]
3410 3380
 name = "vec_map"
... ...
@@ -3449,7 +3419,16 @@ version = "1.0.1+wasi-0.2.4"
3449 3419
 source = "registry+https://github.com/rust-lang/crates.io-index"
3450 3420
 checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
3451 3421
 dependencies = [
3452
- "wit-bindgen",
3422
+ "wit-bindgen 0.46.0",
3423
+]
3424
+
3425
+[[package]]
3426
+name = "wasip3"
3427
+version = "0.3.1+wasi-0.3.0-rc-2025-09-16"
3428
+source = "registry+https://github.com/rust-lang/crates.io-index"
3429
+checksum = "87ba4be47b1d11244670d11857eee0758a8f2c39aea64d80b78c1ce29b4642cd"
3430
+dependencies = [
3431
+ "wit-bindgen 0.48.1",
3453 3432
 ]
3454 3433
 
3455 3434
 [[package]]
... ...
@@ -3524,6 +3503,40 @@ dependencies = [
3524 3503
  "unicode-ident",
3525 3504
 ]
3526 3505
 
3506
+[[package]]
3507
+name = "wasm-encoder"
3508
+version = "0.241.2"
3509
+source = "registry+https://github.com/rust-lang/crates.io-index"
3510
+checksum = "e01164c9dda68301e34fdae536c23ed6fe90ce6d97213ccc171eebbd3d02d6b8"
3511
+dependencies = [
3512
+ "leb128fmt",
3513
+ "wasmparser",
3514
+]
3515
+
3516
+[[package]]
3517
+name = "wasm-metadata"
3518
+version = "0.241.2"
3519
+source = "registry+https://github.com/rust-lang/crates.io-index"
3520
+checksum = "876fe286f2fa416386deedebe8407e6f19e0b5aeaef3d03161e77a15fa80f167"
3521
+dependencies = [
3522
+ "anyhow",
3523
+ "indexmap 2.12.0",
3524
+ "wasm-encoder",
3525
+ "wasmparser",
3526
+]
3527
+
3528
+[[package]]
3529
+name = "wasmparser"
3530
+version = "0.241.2"
3531
+source = "registry+https://github.com/rust-lang/crates.io-index"
3532
+checksum = "46d90019b1afd4b808c263e428de644f3003691f243387d30d673211ee0cb8e8"
3533
+dependencies = [
3534
+ "bitflags 2.10.0",
3535
+ "hashbrown 0.15.5",
3536
+ "indexmap 2.12.0",
3537
+ "semver",
3538
+]
3539
+
3527 3540
 [[package]]
3528 3541
 name = "web-sys"
3529 3542
 version = "0.3.81"
... ...
@@ -3559,15 +3572,6 @@ version = "0.25.4"
3559 3572
 source = "registry+https://github.com/rust-lang/crates.io-index"
3560 3573
 checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
3561 3574
 
3562
-[[package]]
3563
-name = "webpki-roots"
3564
-version = "1.0.4"
3565
-source = "registry+https://github.com/rust-lang/crates.io-index"
3566
-checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
3567
-dependencies = [
3568
- "rustls-pki-types",
3569
-]
3570
-
3571 3575
 [[package]]
3572 3576
 name = "which"
3573 3577
 version = "3.1.1"
... ...
@@ -3988,6 +3992,94 @@ version = "0.46.0"
3988 3992
 source = "registry+https://github.com/rust-lang/crates.io-index"
3989 3993
 checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
3990 3994
 
3995
+[[package]]
3996
+name = "wit-bindgen"
3997
+version = "0.48.1"
3998
+source = "registry+https://github.com/rust-lang/crates.io-index"
3999
+checksum = "7f8c2adb5f74ac9395bc3121c99a1254bf9310482c27b13f97167aedb5887138"
4000
+dependencies = [
4001
+ "wit-bindgen-rust-macro",
4002
+]
4003
+
4004
+[[package]]
4005
+name = "wit-bindgen-core"
4006
+version = "0.48.1"
4007
+source = "registry+https://github.com/rust-lang/crates.io-index"
4008
+checksum = "9b881a098cae03686d7a0587f8f306f8a58102ad8da8b5599100fbe0e7f5800b"
4009
+dependencies = [
4010
+ "anyhow",
4011
+ "heck 0.5.0",
4012
+ "wit-parser",
4013
+]
4014
+
4015
+[[package]]
4016
+name = "wit-bindgen-rust"
4017
+version = "0.48.1"
4018
+source = "registry+https://github.com/rust-lang/crates.io-index"
4019
+checksum = "69667efa439a453e1d50dac939c6cab6d2c3ac724a9d232b6631dad2472a5b70"
4020
+dependencies = [
4021
+ "anyhow",
4022
+ "heck 0.5.0",
4023
+ "indexmap 2.12.0",
4024
+ "prettyplease",
4025
+ "syn 2.0.107",
4026
+ "wasm-metadata",
4027
+ "wit-bindgen-core",
4028
+ "wit-component",
4029
+]
4030
+
4031
+[[package]]
4032
+name = "wit-bindgen-rust-macro"
4033
+version = "0.48.1"
4034
+source = "registry+https://github.com/rust-lang/crates.io-index"
4035
+checksum = "eae2e22cceb5d105d52326c07e3e67603a861cc7add70fc467f7cc7ec5265017"
4036
+dependencies = [
4037
+ "anyhow",
4038
+ "prettyplease",
4039
+ "proc-macro2",
4040
+ "quote",
4041
+ "syn 2.0.107",
4042
+ "wit-bindgen-core",
4043
+ "wit-bindgen-rust",
4044
+]
4045
+
4046
+[[package]]
4047
+name = "wit-component"
4048
+version = "0.241.2"
4049
+source = "registry+https://github.com/rust-lang/crates.io-index"
4050
+checksum = "1fd0c57df25e7ee612d946d3b7646c1ddb2310f8280aa2c17e543b66e0812241"
4051
+dependencies = [
4052
+ "anyhow",
4053
+ "bitflags 2.10.0",
4054
+ "indexmap 2.12.0",
4055
+ "log",
4056
+ "serde",
4057
+ "serde_derive",
4058
+ "serde_json",
4059
+ "wasm-encoder",
4060
+ "wasm-metadata",
4061
+ "wasmparser",
4062
+ "wit-parser",
4063
+]
4064
+
4065
+[[package]]
4066
+name = "wit-parser"
4067
+version = "0.241.2"
4068
+source = "registry+https://github.com/rust-lang/crates.io-index"
4069
+checksum = "09ef1c6ad67f35c831abd4039c02894de97034100899614d1c44e2268ad01c91"
4070
+dependencies = [
4071
+ "anyhow",
4072
+ "id-arena",
4073
+ "indexmap 2.12.0",
4074
+ "log",
4075
+ "semver",
4076
+ "serde",
4077
+ "serde_derive",
4078
+ "serde_json",
4079
+ "unicode-xid",
4080
+ "wasmparser",
4081
+]
4082
+
3991 4083
 [[package]]
3992 4084
 name = "writeable"
3993 4085
 version = "0.6.1"
... ...
@@ -4002,7 +4094,11 @@ dependencies = [
4002 4094
  "apple-account",
4003 4095
  "plist",
4004 4096
  "plist-macros",
4005
- "reqwest 0.12.27",
4097
+ "reqwest 0.13.1",
4098
+ "serde",
4099
+ "sys-locale",
4100
+ "thiserror 2.0.17",
4101
+ "uuid",
4006 4102
 ]
4007 4103
 
4008 4104
 [[package]]
... ...
@@ -1,6 +1,6 @@
1 1
 [workspace]
2 2
 resolver = "2"
3
-members = ["adi", "adid-proxy", "android-coreadi", "apple-account", "apple-codesign-quick", "elf-loader", "fpsd-proxy", "plist-macros", "purple-builder", "super-sideloader", "xcode"]
3
+members = ["adi", "adid-proxy", "android-coreadi", "apple-account", "apple-codesign-quick", "elf-loader", "fairplay", "library-coreadi", "plist-macros", "purple-builder", "super-sideloader", "xcode"]
4 4
 
5 5
 [workspace.dependencies]
6 6
 adi = { path = "adi" }
... ...
@@ -8,18 +8,19 @@ adid-proxy = { path = "adid-proxy" }
8 8
 android-coreadi = { path = "android-coreadi" }
9 9
 apple-account = { path = "apple-account" }
10 10
 elf-loader = { path = "elf-loader" }
11
+library-coreadi = { path = "library-coreadi" }
11 12
 plist-macros = { path = "plist-macros" }
12 13
 xcode = { path = "xcode" }
13 14
 
14 15
 plist = "1"
15
-reqwest = { version = "0.12", features = ["rustls-tls"] }
16
+reqwest = { version = "0.13", features = ["rustls", "query", "gzip"] }
16 17
 thiserror = "2"
17 18
 tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
18 19
 
19 20
 aes = "0.9.0-rc.2"
20 21
 aes-gcm = "0.11.0-rc.2"
21 22
 cbc = { version = "0.2.0-rc.2", features = ["alloc"] }
22
-hmac = "0.13.0-rc.3"
23
-pbkdf2 = "0.13.0-rc.5"
24
-sha1 = "0.11.0-rc.3"
25
-sha2 = "0.11.0-rc.3"
23
+hmac = "0.13.0-rc.4"
24
+pbkdf2 = "0.13.0-rc.8"
25
+sha1 = "0.11.0-rc.4"
26
+sha2 = "0.11.0-rc.4"
... ...
@@ -14,6 +14,7 @@ use bincode::serde::{decode_from_slice, encode_to_vec};
14 14
 use std::ffi::CStr;
15 15
 use std::marker::PhantomData;
16 16
 use std::mem::MaybeUninit;
17
+use std::ptr::null;
17 18
 use std::slice;
18 19
 
19 20
 #[repr(C)]
... ...
@@ -78,6 +79,15 @@ impl<T: CoreADIProxy + ?Sized> CoreADIProxyHelper for T {
78 79
     }
79 80
 }
80 81
 
82
+#[inline]
83
+unsafe fn option_slice_from_raw_parts<'lt, T>(data: *const T, len: usize) -> Option<&'lt [T]> {
84
+    if len > 0 {
85
+        Some(unsafe { slice::from_raw_parts(data, len) })
86
+    } else {
87
+        None
88
+    }
89
+}
90
+
81 91
 impl<T: CoreADIProxyHelper + ?Sized> CoreADIADIProxy for T {
82 92
     fn initialize(&self) -> ADIResult<()> {
83 93
         let err = self.dispatch_message(InitializeMessage {})?;
... ...
@@ -106,7 +116,7 @@ impl<T: CoreADIProxyHelper + ?Sized> CoreADIADIProxy for T {
106 116
             Err(err.into())
107 117
         } else {
108 118
             Ok(ADIBuffer(unsafe {
109
-                slice::from_raw_parts(
119
+                option_slice_from_raw_parts(
110 120
                     adi_accounts_ptr.assume_init(),
111 121
                     adi_accounts_len.assume_init() as usize,
112 122
                 )
... ...
@@ -221,10 +231,16 @@ impl<T: CoreADIProxyHelper + ?Sized> CoreADIADIProxy for T {
221 231
         } else {
222 232
             Ok((
223 233
                 ADIBuffer(unsafe {
224
-                    slice::from_raw_parts(mid_ptr.assume_init(), mid_len.assume_init() as usize)
234
+                    option_slice_from_raw_parts(
235
+                        mid_ptr.assume_init(),
236
+                        mid_len.assume_init() as usize,
237
+                    )
225 238
                 }),
226 239
                 ADIBuffer(unsafe {
227
-                    slice::from_raw_parts(srm_ptr.assume_init(), srm_len.assume_init() as usize)
240
+                    option_slice_from_raw_parts(
241
+                        srm_ptr.assume_init(),
242
+                        srm_len.assume_init() as usize,
243
+                    )
228 244
                 }),
229 245
             ))
230 246
         }
... ...
@@ -255,27 +271,32 @@ impl<T: CoreADIProxyHelper + ?Sized> CoreADIADIProxy for T {
255 271
         } else {
256 272
             Ok((
257 273
                 ADIBuffer(unsafe {
258
-                    slice::from_raw_parts(mid_ptr.assume_init(), mid_len.assume_init() as usize)
274
+                    option_slice_from_raw_parts(
275
+                        mid_ptr.assume_init(),
276
+                        mid_len.assume_init() as usize,
277
+                    )
259 278
                 }),
260 279
                 ADIBuffer(unsafe {
261
-                    slice::from_raw_parts(otp_ptr.assume_init(), otp_len.assume_init() as usize)
280
+                    option_slice_from_raw_parts(
281
+                        otp_ptr.assume_init(),
282
+                        otp_len.assume_init() as usize,
283
+                    )
262 284
                 }),
263 285
             ))
264 286
         }
265 287
     }
266 288
 
267 289
     fn dispose(&self, buffer: ADIBuffer<'_, u8>) -> ADIResult<()> {
268
-        let buf = buffer.0;
290
+        let (ptr, _len) = match buffer.0 {
291
+            Some(buf) => (buf.as_ptr(), buf.len() as _),
292
+            None => (null(), 0u32),
293
+        };
269 294
         std::mem::forget(buffer);
270
-        let err = self.dispatch_message(StorageDisposeMessage { ptr: buf.as_ptr() })?;
295
+        let err = self.dispatch_message(StorageDisposeMessage { ptr })?;
271 296
         if err < 0 {
272 297
             let err = err.into();
273 298
             #[cfg(test)]
274
-            {
275
-                let ptr = buf.as_ptr();
276
-                let len = buf.len();
277
-                log::warn!("Leak of buf {ptr:p} of size {len}: {err:?}");
278
-            }
299
+            log::warn!("Leak of buf {ptr:p} of size {_len}: {err:?}");
279 300
             Err(err)
280 301
         } else {
281 302
             Ok(())
... ...
@@ -283,20 +304,16 @@ impl<T: CoreADIProxyHelper + ?Sized> CoreADIADIProxy for T {
283 304
     }
284 305
 
285 306
     fn dispose_account_storage(&self, buffer: ADIBuffer<'_, CoreADIAccount>) -> ADIResult<()> {
286
-        let buf = buffer.0;
307
+        let (ptr, len) = match buffer.0 {
308
+            Some(buf) => (buf.as_ptr(), buf.len() as _),
309
+            None => (null(), 0u32),
310
+        };
287 311
         std::mem::forget(buffer);
288
-        let err = self.dispatch_message(AllProvisionedAcctStorageDisposeMessage {
289
-            ptr: buf.as_ptr(),
290
-            len: buf.len() as u32,
291
-        })?;
312
+        let err = self.dispatch_message(AllProvisionedAcctStorageDisposeMessage { ptr, len })?;
292 313
         if err < 0 {
293 314
             let err = err.into();
294 315
             #[cfg(test)]
295
-            {
296
-                let ptr = buf.as_ptr();
297
-                let len = buf.len();
298 316
             log::warn!("Leak of buf {ptr:p} of size {len}: {err:?}");
299
-            }
300 317
             Err(err)
301 318
         } else {
302 319
             Ok(())
... ...
@@ -384,7 +401,7 @@ impl<T: CoreADIADIProxy> ADIProxy for T {
384 401
     fn get_all_provisioned_accounts(&self) -> ADIResult<Vec<ADIAccount>> {
385 402
         let coreadi_accounts = <Self as CoreADIADIProxy>::get_all_provisioned_accounts(self)?;
386 403
         let accounts: Vec<ADIAccount> = unsafe {
387
-            coreadi_accounts.0.iter().map(
404
+            (&*coreadi_accounts).iter().map(
388 405
                 |&CoreADIAccount {
389 406
                      ds_id,
390 407
                      mid_len,
... ...
@@ -435,7 +452,7 @@ impl<T: CoreADIADIProxy> ADIProxy for T {
435 452
 
436 453
     fn synchronize(&self, ds_id: i64, sim: &[u8]) -> ADIResult<(Vec<u8>, Vec<u8>)> {
437 454
         let (mid_buf, srm_buf) = <Self as CoreADIADIProxy>::synchronize(self, ds_id, sim)?;
438
-        let (mid, srm) = (mid_buf.0.to_vec(), srm_buf.0.to_vec());
455
+        let (mid, srm) = (mid_buf.to_vec(), srm_buf.to_vec());
439 456
         self.dispose(mid_buf).and_then(|()| self.dispose(srm_buf))?;
440 457
         Ok((mid, srm))
441 458
     }
... ...
@@ -446,7 +463,7 @@ impl<T: CoreADIADIProxy> ADIProxy for T {
446 463
 
447 464
     fn request_otp(&self, ds_id: i64) -> ADIResult<(Vec<u8>, Vec<u8>)> {
448 465
         let (mid_buf, otp_buf) = <Self as CoreADIADIProxy>::request_otp(self, ds_id)?;
449
-        let (mid, otp) = (mid_buf.0.to_vec(), otp_buf.0.to_vec());
466
+        let (mid, otp) = (mid_buf.to_vec(), otp_buf.to_vec());
450 467
         self.dispose(mid_buf).and_then(|()| self.dispose(otp_buf))?;
451 468
         Ok((mid.into(), otp.into()))
452 469
     }
... ...
@@ -3,6 +3,7 @@ use serde::{Serialize, Serializer};
3 3
 use std::ffi::CStr;
4 4
 use std::fmt::{Debug, Display};
5 5
 use std::marker::PhantomData;
6
+use std::ops::Deref;
6 7
 
7 8
 #[derive(Copy, Clone, Debug, PartialEq)]
8 9
 pub enum EncryptionType {
... ...
@@ -198,7 +199,18 @@ pub struct ADIPayload<Message: ADIMessage> {
198 199
     pub payload: Message,
199 200
 }
200 201
 
201
-pub struct ADIBuffer<'lt, T>(pub &'lt [T]);
202
+pub struct ADIBuffer<'lt, T>(pub Option<&'lt [T]>);
203
+
204
+impl<'lt, T> Deref for ADIBuffer<'lt, T> {
205
+    type Target = [T];
206
+
207
+    fn deref(&self) -> &Self::Target {
208
+        match self.0 {
209
+            Some(arr) => arr,
210
+            None => &[],
211
+        }
212
+    }
213
+}
202 214
 
203 215
 pub struct ADIProvisioningSession<'lt> {
204 216
     pub val: u32,
... ...
@@ -93,7 +93,17 @@ pub type off64_t = libc::c_longlong;
93 93
 #[cfg(target_pointer_width = "64")]
94 94
 pub type off64_t = i64;
95 95
 
96
+#[cfg(target_pointer_width = "64")]
97
+pub type mode_t = u32;
96 98
 #[cfg(target_pointer_width = "32")]
97 99
 pub type mode_t = u16;
98 100
 
99 101
 pub type time_t = libc::c_long;
102
+
103
+pub type suseconds_t = i64;
104
+
105
+#[repr(C)]
106
+pub struct timeval {
107
+    pub tv_sec: time_t,
108
+    pub tv_usec: suseconds_t,
109
+}
... ...
@@ -53,7 +53,7 @@ pub unsafe fn fstat(fildes: libc::c_int, buf: *mut stat) -> libc::c_int {
53 53
 }
54 54
 
55 55
 #[linux_cc]
56
-pub unsafe extern "C" fn open(path: *const libc::c_char, oflag: libc::c_int) -> libc::c_int {
56
+pub unsafe fn open(path: *const libc::c_char, oflag: libc::c_int) -> libc::c_int {
57 57
     let mut local_flag = 0;
58 58
 
59 59
     if oflag & 0b11 == O_WRONLY {
... ...
@@ -1,6 +1,193 @@
1
-pub use libc::{
2
-    chmod, close, free, fstat, ftruncate, gettimeofday, lstat, malloc, mkdir, open, read, strncpy,
3
-    umask, write,
1
+#![allow(unsafe_op_in_unsafe_fn)]
2
+#![allow(non_camel_case_types)]
3
+
4
+// TODO: finish that
5
+
6
+use crate::linux_libc::*;
7
+use elf_loader::linux_cc;
8
+use std::ffi::{CStr, CString};
9
+use std::mem;
10
+use std::mem::MaybeUninit;
11
+
12
+pub type __time32_t = libc::c_long;
13
+pub type __time64_t = libc::c_longlong;
14
+
15
+#[repr(C)]
16
+#[derive(Debug, Copy, Clone)]
17
+pub struct _timespec64 {
18
+    pub tv_sec: __time64_t,
19
+    pub tv_nsec: libc::c_long,
20
+}
21
+
22
+#[repr(C)]
23
+#[derive(Debug, Copy, Clone)]
24
+pub struct _timespec32 {
25
+    pub tv_sec: __time32_t,
26
+    pub tv_nsec: libc::c_long,
27
+}
28
+
29
+// Import a few windows functions that are not defined in libc
30
+unsafe extern "C" {
31
+    fn _errno() -> *mut libc::c_int;
32
+    fn _timespec64_get(__ts: *mut _timespec64, __base: libc::c_int) -> libc::c_int;
33
+    fn _timespec32_get(__ts: *mut _timespec32, __base: libc::c_int) -> libc::c_int;
34
+    fn _chsize(handle: i64, length: u64) -> usize;
35
+}
36
+
37
+#[linux_cc]
38
+pub unsafe fn chmod(path: *const libc::c_char, mode: mode_t) -> libc::c_int {
39
+    libc::chmod(linux_path_to_windows(path).as_ptr(), mem::transmute(mode))
40
+}
41
+
42
+#[linux_cc]
43
+pub unsafe fn close(fd: libc::c_int) -> libc::c_int {
44
+    libc::close(fd)
45
+}
46
+
47
+#[linux_cc]
48
+pub unsafe fn errno() -> *mut libc::c_int {
49
+    // HACK we should also translate the errnos, but eh it works fine right now
50
+    _errno()
51
+}
52
+
53
+#[linux_cc]
54
+pub unsafe fn free(p: *mut libc::c_void) {
55
+    libc::free(p)
56
+}
57
+
58
+#[linux_cc]
59
+pub unsafe fn ftruncate(handle: i64, length: u64) -> usize {
60
+    _chsize(handle, length)
61
+}
62
+
63
+#[linux_cc]
64
+pub unsafe fn gettimeofday(tp: *mut timeval, tz: *mut libc::c_void) -> libc::c_int {
65
+    let mut time = MaybeUninit::uninit();
66
+
67
+    #[cfg(target_pointer_width = "32")]
68
+    let result = _timespec32_get(time.as_mut_ptr(), 1);
69
+    #[cfg(target_pointer_width = "64")]
70
+    let result = _timespec64_get(time.as_mut_ptr(), 1);
71
+
72
+    let time = time.assume_init();
73
+    *tp = timeval {
74
+        tv_sec: time.tv_sec as _,
75
+        tv_usec: (time.tv_nsec / 1000) as _,
4 76
     };
5 77
 
6
-todo!();
78
+    if result == 1 { 0 } else { 1 }
79
+}
80
+
81
+#[linux_cc]
82
+pub unsafe fn malloc(size: libc::size_t) -> *mut libc::c_void {
83
+    libc::malloc(size)
84
+}
85
+
86
+#[linux_cc]
87
+pub unsafe fn mkdir(path: *const libc::c_char, mode: mode_t) -> libc::c_int {
88
+    libc::mkdir(linux_path_to_windows(path).as_ptr())
89
+}
90
+
91
+#[linux_cc]
92
+pub unsafe fn read(fd: libc::c_int, buf: *mut libc::c_void, count: libc::c_uint) -> libc::c_int {
93
+    libc::read(fd, buf, count)
94
+}
95
+
96
+#[linux_cc]
97
+pub unsafe fn strncpy(
98
+    dst: *mut libc::c_char,
99
+    src: *const libc::c_char,
100
+    n: libc::size_t,
101
+) -> *mut libc::c_char {
102
+    libc::strncpy(dst, src, n)
103
+}
104
+
105
+#[linux_cc]
106
+pub unsafe fn umask(mask: usize) -> usize {
107
+    // Wrong but works fine.
108
+    mask
109
+}
110
+
111
+#[linux_cc]
112
+pub unsafe fn write(fd: libc::c_int, buf: *const libc::c_void, count: libc::c_uint) -> libc::c_int {
113
+    libc::write(fd, buf, count)
114
+}
115
+
116
+#[inline]
117
+unsafe fn local_stat_to_linux(buf: libc::stat) -> stat {
118
+    stat {
119
+        st_dev: buf.st_dev as _,
120
+        st_ino: buf.st_ino as _,
121
+        st_nlink: buf.st_nlink as _,
122
+        st_mode: 0o777 as _,
123
+        st_uid: buf.st_uid as _,
124
+        st_gid: buf.st_gid as _,
125
+        st_rdev: buf.st_rdev as _,
126
+        st_size: buf.st_size as _,
127
+        st_atime: (buf.st_atime / 10000000) as _,
128
+        // st_atime_nsec: buf.st_atime_nsec as _,
129
+        st_mtime: (buf.st_mtime / 10000000) as _,
130
+        // st_mtime_nsec: buf.st_mtime_nsec as _,
131
+        st_ctime: (buf.st_ctime / 10000000) as _,
132
+        // st_ctime_nsec: buf.st_ctime_nsec as _,
133
+        ..mem::zeroed()
134
+    }
135
+}
136
+
137
+#[inline]
138
+unsafe fn linux_path_to_windows(path: *const libc::c_char) -> CString {
139
+    let path = CStr::from_ptr(path)
140
+        .to_str()
141
+        .unwrap() // Safety: it's a path.
142
+        .chars()
143
+        .map(|x| match x {
144
+            '/' => '\\',
145
+            c => c,
146
+        })
147
+        .collect::<String>();
148
+
149
+    let path = path.trim_start_matches("\\\\?\\").to_string();
150
+
151
+    // Safety: no null will be generated by our processing.
152
+    CString::new(path).unwrap()
153
+}
154
+
155
+#[linux_cc]
156
+pub unsafe fn lstat(path: *const libc::c_char, buf: *mut stat) -> libc::c_int {
157
+    let mut local_buf = mem::zeroed();
158
+    let return_code = libc::stat(linux_path_to_windows(path).as_ptr(), &mut local_buf);
159
+    *buf = local_stat_to_linux(local_buf);
160
+    return_code
161
+}
162
+
163
+#[linux_cc]
164
+pub unsafe fn fstat(fildes: libc::c_int, buf: *mut stat) -> libc::c_int {
165
+    let mut local_buf = mem::zeroed();
166
+    let return_code = libc::fstat(fildes, &mut local_buf);
167
+    *buf = local_stat_to_linux(local_buf);
168
+    return_code
169
+}
170
+
171
+#[linux_cc]
172
+pub unsafe fn open(path: *const libc::c_char, oflag: libc::c_int) -> libc::c_int {
173
+    let mut local_flag = 0x8000;
174
+
175
+    if oflag & 0b11 == O_WRONLY {
176
+        local_flag = libc::O_RDWR;
177
+    }
178
+    if oflag & 0b11 == O_RDWR {
179
+        local_flag = libc::O_RDWR;
180
+    }
181
+    if oflag & 0b11 == O_RDONLY {
182
+        local_flag = libc::O_RDWR;
183
+    }
184
+
185
+    if oflag & O_CREAT != 0 {
186
+        local_flag |= libc::O_CREAT;
187
+    }
188
+    if oflag & O_NOFOLLOW != 0 {
189
+        // local_flag |= libc::O_NOFOLLOW;
190
+    }
191
+
192
+    libc::open(linux_path_to_windows(path).as_ptr(), local_flag)
193
+}
... ...
@@ -23,9 +23,9 @@ serde = { version = "1.0.228", features = ["derive"] }
23 23
 chrono = "0.4.42"
24 24
 iana-time-zone = "0.1.64"
25 25
 sys-locale = "0.3.2"
26
-srp = { git = "https://github.com/RustCrypto/PAKEs" }
27
-# srp = "0.7"
26
+srp = "0.7.0-rc.1"
28 27
 rand = "0.9.2"
29 28
 hex = "0.4.3"
30 29
 bytes = "1.10.1"
31 30
 log = "0.4.29"
31
+uuid = { version = "1.19.0", features = ["v4"] }
... ...
@@ -27,12 +27,9 @@ use log::trace;
27 27
 use plist::{Dictionary, Value};
28 28
 use plist_macros::{array, dict};
29 29
 pub use post_data::*;
30
-use reqwest::{Certificate, Method, RequestBuilder};
30
+use reqwest::{Certificate, Method};
31 31
 use sha2::{Digest, Sha256};
32
-use srp::client::SrpClient;
33
-use srp::groups::G_2048;
34
-use srp::types::SrpAuthError;
35
-use std::fmt::{Display, Formatter};
32
+use srp::groups::G2048;
36 33
 use thiserror::Error;
37 34
 pub use url_switch::*;
38 35
 
... ...
@@ -126,58 +123,6 @@ pub async fn http_session_with_custom_bag_url<'lt>(
126 123
     HTTPSession::new(http_session, url_bag)
127 124
 }
128 125
 
129
-pub struct Token<'lt, 'lt2, 'adi> {
130
-    http_session: &'lt AnisetteHTTPSession<'lt2, 'adi>,
131
-    alt_dsid: String,
132
-    token: String,
133
-}
134
-
135
-#[derive(Debug)]
136
-pub enum TokenError {
137
-    Expired,
138
-    ADI(ADIError),
139
-}
140
-
141
-impl Display for TokenError {
142
-    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
143
-        match self {
144
-            TokenError::Expired => {
145
-                write!(f, "The session has expired.")
146
-            }
147
-            TokenError::ADI(error) => error.fmt(f),
148
-        }
149
-    }
150
-}
151
-
152
-impl std::error::Error for TokenError {}
153
-
154
-impl<'lt, 'lt2, 'adi> Token<'lt, 'lt2, 'adi> {
155
-    pub fn new(
156
-        http_session: &'lt AnisetteHTTPSession<'lt2, 'adi>,
157
-        alt_dsid: String,
158
-        token: String,
159
-    ) -> Result<Self, TokenError> {
160
-        // TODO check the validity of the token.
161
-        Ok(Self {
162
-            http_session,
163
-            alt_dsid,
164
-            token,
165
-        })
166
-    }
167
-
168
-    pub fn authenticated_request_builder(
169
-        &self,
170
-        method: Method,
171
-        url: &str,
172
-    ) -> Result<RequestBuilder, TokenError> {
173
-        Ok(self
174
-            .http_session
175
-            .anisette_request_builder(GRANDSLAM_DSID, method, url)
176
-            .map_err(TokenError::ADI)?
177
-            .header("X-Apple-I-Identity-Id", &self.alt_dsid)
178
-            .header("X-Apple-GS-Token", &self.token))
179
-    }
180
-}
181 126
 #[derive(Debug)]
182 127
 pub enum AuthOutcome {
183 128
     Success(Dictionary),
... ...
@@ -203,7 +148,7 @@ pub enum AuthError {
203 148
     #[error("Invalid server response structure (censor private data before reporting): {0:?}")]
204 149
     Structure(Dictionary),
205 150
     #[error("Authentication protocol failure: {0}")]
206
-    SRP(#[from] SrpAuthError),
151
+    SRP(#[from] srp::AuthError),
207 152
     #[error("Unknown server protocol: {0}")]
208 153
     UnknownProtocol(String),
209 154
     #[error("Decryption error: {0}")]
... ...
@@ -303,13 +248,14 @@ pub async fn login(
303 248
     let cpd = build_client_provided_data(http_session)?;
304 249
 
305 250
     // TODO: implement s4k, if some day someone needs that.
306
-    let srp_client = SrpClient::<Sha256>::new_with_options(&G_2048, true);
251
+    let srp_client = srp::Client::<G2048, Sha256>::new_with_options(false);
307 252
     let a: [u8; 256] = rand::random();
308 253
     let a_pub = srp_client.compute_public_ephemeral(&a);
309 254
 
310 255
     let request_plist = dict! {
311 256
         "Header": dict!{
312
-            "Version": "1.0.1"
257
+            "Version": "1.0.1",
258
+            // "GSClientIdentifier": "AppleIDAuthSupport",
313 259
         },
314 260
         "Request": dict!{
315 261
             "A2k": Value::Data(a_pub),
... ...
@@ -324,7 +270,7 @@ pub async fn login(
324 270
     };
325 271
 
326 272
     let response = http_session
327
-        .anisette_request_builder(GRANDSLAM_DSID, Method::POST, gs_service_url)?
273
+        .anisette_request_builder(Method::POST, gs_service_url)?
328 274
         .body(dict_to_body(request_plist))
329 275
         .send()
330 276
         .await?
... ...
@@ -392,7 +338,7 @@ pub async fn login(
392 338
     };
393 339
 
394 340
     let verifier =
395
-        srp_client.process_reply_rfc5054(&a, apple_id.as_bytes(), &processed_password, salt, b)?;
341
+        srp_client.process_reply(&a, apple_id.as_bytes(), &processed_password, salt, b)?;
396 342
 
397 343
     let cpd = build_client_provided_data(http_session)?;
398 344
 
... ...
@@ -410,7 +356,7 @@ pub async fn login(
410 356
     };
411 357
 
412 358
     let response = http_session
413
-        .anisette_request_builder(GRANDSLAM_DSID, Method::POST, gs_service_url)?
359
+        .anisette_request_builder(Method::POST, gs_service_url)?
414 360
         .body(dict_to_body(request_plist))
415 361
         .send()
416 362
         .await?
... ...
@@ -7,10 +7,12 @@ use aes_gcm::aead::{Aead, Payload};
7 7
 use aes_gcm::{AesGcm, KeyInit};
8 8
 use base64::Engine;
9 9
 use base64::prelude::BASE64_STANDARD;
10
+use chrono::{Local, SecondsFormat};
10 11
 use hmac::{Hmac, Mac};
11 12
 use log::{trace, warn};
12 13
 use plist::{Dictionary, Value};
13 14
 use plist_macros::{array, dict};
15
+use reqwest::header::{HeaderValue, InvalidHeaderValue};
14 16
 use reqwest::{Method, RequestBuilder};
15 17
 use serde::Deserialize;
16 18
 use sha2::Sha256;
... ...
@@ -26,6 +28,14 @@ pub struct Token {
26 28
     pub token: String,
27 29
 }
28 30
 
31
+impl TryFrom<&Token> for HeaderValue {
32
+    type Error = InvalidHeaderValue;
33
+
34
+    fn try_from(token: &Token) -> Result<Self, Self::Error> {
35
+        HeaderValue::from_str(token.token.as_str())
36
+    }
37
+}
38
+
29 39
 #[derive(Debug)]
30 40
 pub struct AuthToken {
31 41
     pub alt_dsid: String,
... ...
@@ -164,8 +174,7 @@ impl<'a, 'b> AuthenticatedHTTPSession<'a, 'b> {
164 174
     }
165 175
 
166 176
     pub fn anisette_request_builder(&self, method: Method, url: &str) -> ADIResult<RequestBuilder> {
167
-        self.http_session
168
-            .anisette_request_builder(GRANDSLAM_DSID, method, url)
177
+        self.http_session.anisette_request_builder(method, url)
169 178
     }
170 179
 
171 180
     pub fn authenticated_request_builder(
... ...
@@ -174,16 +183,21 @@ impl<'a, 'b> AuthenticatedHTTPSession<'a, 'b> {
174 183
         url: &str,
175 184
     ) -> ADIResult<RequestBuilder> {
176 185
         self.anisette_request_builder(method, url).map(|builder| {
186
+            let client_time = Local::now()
187
+                .to_utc()
188
+                .to_rfc3339_opts(SecondsFormat::Secs, true);
189
+
190
+            let timezone = iana_time_zone::get_timezone().unwrap_or_else(|_| "UTC".to_string());
191
+            let locale = sys_locale::get_locale().unwrap_or_else(|| String::from("en-US"));
192
+            let apple_locale = locale.replace('-', "_");
193
+
177 194
             builder
178
-                // .header(
179
-                //     "X-Apple-Identity-Token",
180
-                //     self.auth_token.identity_token.as_str(),
181
-                // )
182
-                // .header("X-Apple-I-Identity-Id", self.auth_token.alt_dsid.as_str())
183
-                // .header("X-Apple-DSID", self.auth_token.alt_dsid.as_str())
184
-                // .header("X-Apple-GS-Token", self.auth_token.idms_token.as_str())
185
-                // .header("X-Apple-I-CK-Presence", "false")
195
+                .header("Accept-Language", locale)
196
+                .header("X-Apple-I-Identity-Id", self.auth_token.alt_dsid.as_str())
186 197
                 .header("X-Apple-HB-Token", self.hb_token.as_str())
198
+                .header("X-Apple-Locale", apple_locale)
199
+                .header("X-Apple-I-Client-Time", client_time)
200
+                .header("X-Apple-I-TimeZone", timezone)
187 201
         })
188 202
     }
189 203
 
... ...
@@ -234,10 +248,10 @@ impl<'a, 'b> AuthenticatedHTTPSession<'a, 'b> {
234 248
         };
235 249
 
236 250
         let mut request_body = Vec::new();
237
-        plist::to_writer_xml(&mut request_body, &request_plist).unwrap();
251
+        plist::to_writer_xml(&mut request_body, &request_plist).expect("Serializing plist failed?");
238 252
 
239 253
         let response = http_session
240
-            .anisette_request_builder(GRANDSLAM_DSID, Method::POST, gs_service_url)
254
+            .anisette_request_builder(Method::POST, gs_service_url)
241 255
             .map_err(AppTokenRequestError::Anisette)?
242 256
             .body(request_body)
243 257
             .send()
... ...
@@ -318,7 +332,12 @@ impl<'a, 'b> AuthenticatedHTTPSession<'a, 'b> {
318 332
             .bytes()
319 333
             .await?;
320 334
 
321
-        Ok(plist::from_bytes(&response).map_err(AuthenticatedRequestError::InvalidResponse)?)
335
+        let dict =
336
+            plist::from_bytes(&response).map_err(AuthenticatedRequestError::InvalidResponse)?;
337
+
338
+        parse_status(&dict)?;
339
+
340
+        Ok(dict)
322 341
     }
323 342
 
324 343
     pub async fn validate_code(
... ...
@@ -1,33 +1,26 @@
1
-use crate::grandslam::{Token, TokenError};
1
+use crate::grandslam::AuthenticatedHTTPSession;
2 2
 use crate::http_session::{AppleError, parse_status};
3
+use crate::plist_request::plist_to_body;
4
+use adi::proxy::ADIError;
3 5
 use plist::{Dictionary, Value};
4 6
 use reqwest::Method;
5 7
 use serde::Serialize;
6
-use std::fmt::{Display, Formatter};
8
+use thiserror::Error;
7 9
 
8
-#[derive(Debug)]
10
+#[derive(Debug, Error)]
9 11
 pub enum PostDataError {
10
-    Serialization(plist::Error),
11
-    Reqwest(reqwest::Error),
12
-    Apple(AppleError),
13
-    Token(TokenError),
14
-    URLNotFound,
12
+    #[error("Serialization failure! {0}")]
13
+    Serialization(#[from] plist::Error),
14
+    #[error("Failed to perform HTTP request! {0}")]
15
+    Network(#[from] reqwest::Error),
16
+    #[error("Failed to perform the request! {0}")]
17
+    Anisette(#[from] ADIError),
18
+    #[error("Failed to perform the request! {0}")]
19
+    Apple(#[from] AppleError),
20
+    #[error("Invalid URL bag.")]
21
+    InvalidURLBag,
15 22
 }
16 23
 
17
-impl Display for PostDataError {
18
-    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
19
-        match self {
20
-            PostDataError::Serialization(err) => write!(f, "Serialization failure! {err}"),
21
-            PostDataError::Reqwest(err) => write!(f, "Failed to perform HTTP request! {err}"),
22
-            PostDataError::Apple(err) => err.fmt(f),
23
-            PostDataError::Token(err) => err.fmt(f),
24
-            PostDataError::URLNotFound => write!(f, "Endpoint not found"),
25
-        }
26
-    }
27
-}
28
-
29
-impl std::error::Error for PostDataError {}
30
-
31 24
 #[derive(Debug, Default, Serialize)]
32 25
 pub struct DeviceData {
33 26
     #[serde(rename = "circleStatus")]
... ...
@@ -51,7 +44,7 @@ pub struct DeviceData {
51 44
     pub serial_number: Option<String>,
52 45
 }
53 46
 
54
-impl Token<'_, '_, '_> {
47
+impl AuthenticatedHTTPSession<'_, '_> {
55 48
     pub async fn post_data(&self, device_data: DeviceData) -> Result<(), PostDataError> {
56 49
         let post_data = plist::to_value(&device_data).map_err(PostDataError::Serialization)?;
57 50
         let post_data_url = self
... ...
@@ -59,27 +52,18 @@ impl Token<'_, '_, '_> {
59 52
             .url_bag()
60 53
             .get("postData")
61 54
             .and_then(Value::as_string)
62
-            .ok_or(PostDataError::URLNotFound)?;
63
-
64
-        let mut request_plist = Dictionary::new();
65
-        request_plist.insert("Request".into(), post_data);
66
-
67
-        let mut request_body = Vec::<u8>::new();
68
-        plist::to_writer_xml(&mut request_body, &request_plist)
69
-            .expect("Cannot write request plist??");
55
+            .ok_or(PostDataError::InvalidURLBag)?;
70 56
 
71
-        let request = self
72
-            .authenticated_request_builder(Method::POST, post_data_url)
73
-            .map_err(PostDataError::Token)?
74
-            .body(request_body);
57
+        let mut request = Dictionary::new();
58
+        request.insert("Request".into(), post_data);
75 59
 
76
-        let response = request
60
+        let response = self
61
+            .authenticated_request_builder(Method::POST, post_data_url)?
62
+            .body(plist_to_body(request.into()))
77 63
             .send()
78
-            .await
79
-            .map_err(PostDataError::Reqwest)?
64
+            .await?
80 65
             .bytes()
81
-            .await
82
-            .map_err(PostDataError::Reqwest)?;
66
+            .await?;
83 67
 
84 68
         let status: Dictionary =
85 69
             plist::from_bytes(&response).map_err(PostDataError::Serialization)?;
... ...
@@ -73,14 +73,14 @@ pub fn parse_status(status_dict: &Dictionary) -> Result<(), AppleError> {
73 73
 
74 74
 /// Abstracts a session with Apple servers.
75 75
 pub struct BasicHTTPSession<'lt> {
76
-    client: Client,
77
-    device: Device,
78
-    application_info: BundleInformation<'lt>,
76
+    pub client: Client,
77
+    pub device: Device,
78
+    pub application_info: BundleInformation<'lt>,
79 79
 }
80 80
 
81 81
 pub struct HTTPSession<'lt> {
82
-    http_session: BasicHTTPSession<'lt>,
83
-    url_bag: URLBag,
82
+    pub http_session: BasicHTTPSession<'lt>,
83
+    pub url_bag: URLBag,
84 84
 }
85 85
 
86 86
 #[derive(Debug)]
... ...
@@ -151,8 +151,10 @@ impl<'lt> BasicHTTPSession<'lt> {
151 151
             headers
152 152
         };
153 153
 
154
-        let client_builder = client_builder.default_headers(headers);
155 154
         let client = client_builder
155
+            .default_headers(headers)
156
+            // .connection_verbose(true)
157
+            // .http1_title_case_headers()
156 158
             .build()
157 159
             .map_err(HTTPSessionCreationError::Reqwest)?;
158 160
 
... ...
@@ -214,8 +216,8 @@ impl<'lt> HTTPSession<'lt> {
214 216
 }
215 217
 
216 218
 pub struct AnisetteHTTPSession<'lt, 'adi> {
217
-    http_session: HTTPSession<'lt>,
218
-    adi_proxy: &'adi dyn ADIProxy,
219
+    pub http_session: HTTPSession<'lt>,
220
+    pub adi_proxy: &'adi dyn ADIProxy,
219 221
 }
220 222
 
221 223
 impl<'lt, 'adi> AnisetteHTTPSession<'lt, 'adi> {
... ...
@@ -234,12 +236,12 @@ impl<'lt, 'adi> AnisetteHTTPSession<'lt, 'adi> {
234 236
         self.http_session.request_builder(method, url)
235 237
     }
236 238
 
237
-    pub fn anisette_request_builder(
238
-        &self,
239
-        ds_id: i64,
240
-        method: Method,
241
-        url: &str,
242
-    ) -> ADIResult<RequestBuilder> {
239
+    pub fn anisette_request_builder(&self, method: Method, url: &str) -> ADIResult<RequestBuilder> {
240
+        let mut request_builder = self.simple_request_builder(method, url);
241
+
242
+        if self.adi_proxy.is_machine_provisioned(-2)? {
243
+            let ds_id = -2;
244
+
243 245
             let (mid_b, otp_b) = self.adi_proxy.request_otp(ds_id)?;
244 246
 
245 247
             let mid = BASE64_STANDARD.encode(mid_b);
... ...
@@ -247,11 +249,29 @@ impl<'lt, 'adi> AnisetteHTTPSession<'lt, 'adi> {
247 249
 
248 250
             let rinfo = self.adi_proxy.get_idms_routing(ds_id)?;
249 251
 
250
-        Ok(self
251
-            .simple_request_builder(method, url)
252
+            request_builder = request_builder
252 253
                 .header("X-Apple-I-MD", otp)
253 254
                 .header("X-Apple-I-MD-M", mid)
254
-            .header("X-Apple-I-MD-RINFO", rinfo))
255
+                .header("X-Apple-I-MD-RINFO", rinfo);
256
+        }
257
+
258
+        if self.adi_proxy.is_machine_provisioned(-1)? {
259
+            let ds_id = -1;
260
+
261
+            let (mid_b, otp_b) = self.adi_proxy.request_otp(ds_id)?;
262
+
263
+            let mid = BASE64_STANDARD.encode(mid_b);
264
+            let otp = BASE64_STANDARD.encode(otp_b);
265
+
266
+            let rinfo = self.adi_proxy.get_idms_routing(ds_id)?;
267
+
268
+            request_builder = request_builder
269
+                .header("X-Apple-I-AMD", otp)
270
+                .header("X-Apple-I-AMD-M", mid)
271
+                .header("X-Apple-I-AMD-RINFO", rinfo);
272
+        }
273
+
274
+        Ok(request_builder)
255 275
     }
256 276
 
257 277
     pub fn device(&self) -> &Device {
... ...
@@ -12,6 +12,7 @@ use plist::{Dictionary, Value};
12 12
 use plist_macros::dict;
13 13
 use reqwest::Method;
14 14
 use thiserror::Error;
15
+use uuid::uuid;
15 16
 
16 17
 pub async fn http_session<'lt>(
17 18
     device: Device,
... ...
@@ -132,6 +133,8 @@ pub async fn login(
132 133
         })
133 134
         .collect::<Vec<_>>();
134 135
 
136
+    let uuid = uuid::Uuid::new_v4();
137
+
135 138
     let request_plist = dict! {
136 139
         "appleId": apple_id,
137 140
         "attempt": attempt,
... ...
@@ -145,19 +148,20 @@ pub async fn login(
145 148
             "paid": ""
146 149
         },
147 150
         // "kbsync": vec![],
148
-        "uuid": "3E72931A-9BFE-46EB-9EEA-3383CE091615", // TODO
151
+        "uuid": uuid.to_string().to_ascii_uppercase(),
149 152
         "why": "signIn",
150 153
     };
151 154
 
152 155
     let mut request_body = Vec::new();
153 156
     plist::to_writer_xml(&mut request_body, &request_plist).unwrap();
154 157
 
155
-    // println!("request_body: {}", String::from_utf8(request_body).unwrap());
158
+    println!("request_body: {}", String::from_utf8(request_body).unwrap());
156 159
 
157 160
     let signature = todo!();
161
+    // TODO: check if we need to sign the request following the sign-sap-request url bag key.
158 162
 
159 163
     let response = http_session
160
-        .anisette_request_builder(-2, Method::POST, authenticate_url)?
164
+        .anisette_request_builder(Method::POST, authenticate_url)?
161 165
         .body(request_body)
162 166
         .header("Content-Type", "application/x-apple-plist")
163 167
         // .header("X-Apple-ActionSignature", signature)
... ...
@@ -1,4 +1,28 @@
1 1
 mod elf_hash_table;
2 2
 pub mod elf_library;
3 3
 
4
-pub use sysv64::{sysv64 as linux_cc, sysv64_type as linux_fn};
4
+pub use sysv64::sysv64 as linux_cc;
5
+
6
+#[cfg(target_arch = "x86_64")]
7
+#[macro_export]
8
+macro_rules! linux_fn {
9
+    (unsafe fn ($($args:tt)*) $(-> $ret:ty)?) => {
10
+        unsafe extern "sysv64" fn($($args)*) $(-> $ret)?
11
+    };
12
+
13
+    (fn ($($args:tt)*) $(-> $ret:ty)?) => {
14
+        extern "sysv64" fn($($args)*) $(-> $ret)?
15
+    };
16
+}
17
+
18
+#[cfg(not(target_arch = "x86_64"))]
19
+#[macro_export]
20
+macro_rules! linux_fn {
21
+    (unsafe fn ($($args:tt)*) $(-> $ret:ty)?) => {
22
+        unsafe extern "C" fn($($args)*) $(-> $ret)?
23
+    };
24
+
25
+    (fn ($($args:tt)*) $(-> $ret:ty)?) => {
26
+        extern "C" fn($($args)*) $(-> $ret)?
27
+    };
28
+}
... ...
@@ -1,4 +0,0 @@
1
-fn main() {
2
-    let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
3
-    println!("cargo:rustc-env=TARGET_ARCH={}", arch);
4
-}
... ...
@@ -4,8 +4,6 @@ use syn::{parse_macro_input, ItemFn, Signature, TypeBareFn};
4 4
 
5 5
 #[proc_macro_attribute]
6 6
 pub fn sysv64(_attr: TokenStream, item: TokenStream) -> TokenStream {
7
-    let arch = env!("TARGET_ARCH");
8
-
9 7
     let ItemFn {
10 8
         attrs,
11 9
         vis,
... ...
@@ -26,42 +24,13 @@ pub fn sysv64(_attr: TokenStream, item: TokenStream) -> TokenStream {
26 24
         block,
27 25
     } = parse_macro_input!(item as ItemFn);
28 26
 
29
-    if arch == "x86_64" {
30 27
     quote! {
28
+        #[cfg(target_arch = "x86_64")]
31 29
         #(#attrs)*
32 30
         #vis #constness #asyncness #unsafety extern "sysv64" #fn_token #ident #generics (#inputs) #variadic #output #block
33
-        }
34
-    } else {
35
-        quote! {
31
+
32
+        #[cfg(not(target_arch = "x86_64"))]
36 33
         #(#attrs)*
37 34
         #vis #constness #asyncness #unsafety extern "C" #fn_token #ident #generics (#inputs) #variadic #output #block
38
-        }
39 35
     }.into()
40 36
 }
41
-
42
-#[proc_macro]
43
-pub fn sysv64_type(input: TokenStream) -> TokenStream {
44
-    let arch = env!("TARGET_ARCH");
45
-
46
-    let TypeBareFn {
47
-        lifetimes: _,
48
-        unsafety,
49
-        abi: _,
50
-        fn_token,
51
-        paren_token: _,
52
-        inputs,
53
-        variadic,
54
-        output,
55
-    } = parse_macro_input!(input as TypeBareFn);
56
-
57
-    if arch == "x86_64" {
58
-        quote! {
59
-            #unsafety extern "sysv64" #fn_token (#inputs) #variadic #output
60
-        }
61
-    } else {
62
-        quote! {
63
-            #unsafety extern "C" #fn_token (#inputs) #variadic #output
64
-        }
65
-    }
66
-    .into()
67
-}
... ...
@@ -0,0 +1,6 @@
1
+[package]
2
+name = "fairplay"
3
+version = "0.1.0"
4
+edition = "2024"
5
+
6
+[dependencies]
... ...
@@ -0,0 +1 @@
1
+pub trait FairPlayProxy {}
... ...
@@ -0,0 +1,8 @@
1
+[package]
2
+name = "library-coreadi"
3
+version = "0.1.0"
4
+edition = "2024"
5
+
6
+[dependencies]
7
+adi.workspace = true
8
+dlopen2 = "0.8.2"
... ...
@@ -0,0 +1,24 @@
1
+use adi::core_adi::{CoreADIADIProxy, CoreADIParameters, CoreADIProxy};
2
+use dlopen2::symbor::{Library, Symbol};
3
+
4
+type CoreADIDispatcherFn = extern "C" fn(u32, *const CoreADIParameters) -> i32;
5
+
6
+pub struct LibraryCoreADIProxy<'lt>(Symbol<'lt, CoreADIDispatcherFn>);
7
+
8
+impl<'lt> LibraryCoreADIProxy<'lt> {
9
+    pub fn new(library: &'lt Library) -> Result<Self, dlopen2::Error> {
10
+        unsafe { library.symbol("vdfut768ig").map(LibraryCoreADIProxy) }
11
+    }
12
+}
13
+
14
+impl Drop for LibraryCoreADIProxy<'_> {
15
+    fn drop(&mut self) {
16
+        let _ = self.finalize();
17
+    }
18
+}
19
+
20
+impl CoreADIProxy for LibraryCoreADIProxy<'_> {
21
+    unsafe fn dispatch(&self, function_code: u32, parameters: *const CoreADIParameters) -> i32 {
22
+        self.0(function_code, parameters)
23
+    }
24
+}
... ...
@@ -18,3 +18,7 @@ base64 = "0.22.1"
18 18
 
19 19
 [target.'cfg(target_os = "macos")'.dependencies]
20 20
 adid-proxy.workspace = true
21
+
22
+[target.'cfg(target_os = "windows")'.dependencies]
23
+library-coreadi.workspace = true
24
+dlopen2 = "0.8.2"
... ...
@@ -6,6 +6,7 @@ use apple_account::grandslam::{AuthOutcome, AuthenticatedHTTPSession};
6 6
 use apple_account::http_session::AnisetteHTTPSession;
7 7
 use apple_account::{grandslam, itunes};
8 8
 use std::{env, fs};
9
+use xcode::{ViewDeveloperAction, XcodeSession, XCODE_BUNDLE_INFORMATION, XCODE_TOKEN_IDENTIFIER};
9 10
 
10 11
 async fn grandslam_test(
11 12
     proxy: &dyn ADIProxy,
... ...
@@ -24,7 +25,7 @@ async fn grandslam_test(
24 25
     // println!("{:#02X?}", proxy.get_all_provisioned_accounts());
25 26
 
26 27
     let http_session = AnisetteHTTPSession::new(
27
-        grandslam::http_session(device, APPLE_TV_BUNDLE_INFORMATION).await?,
28
+        grandslam::http_session(device, XCODE_BUNDLE_INFORMATION).await?,
28 29
         proxy,
29 30
     );
30 31
 
... ...
@@ -40,25 +41,6 @@ async fn grandslam_test(
40 41
         | AuthOutcome::SecondaryActionRequired(Some(server_provided_data), _) => {
41 42
             match grandslam::parse_tokens_from_server_provided_data(server_provided_data) {
42 43
                 Some((auth_token, tokens)) => {
43
-                    /*
44
-                    let Some((_, token)) = tokens
45
-                        .iter()
46
-                        .find(|(name, _)| name == "com.apple.gs.idms.pet")
47
-                    else {
48
-                        todo!()
49
-                    };
50
-
51
-                    itunes_test(proxy, apple_id, token.token.as_str()).await?;
52
-                    // */
53
-
54
-                    let url = http_session
55
-                        .url_bag()
56
-                        .get("fetchUserInfo")
57
-                        .unwrap()
58
-                        .as_string()
59
-                        .unwrap()
60
-                        .to_owned();
61
-
62 44
                     let (_, hb_token) = tokens
63 45
                         .iter()
64 46
                         .find(|(name, _)| name == "com.apple.gs.idms.hb")
... ...
@@ -67,19 +49,13 @@ async fn grandslam_test(
67 49
                     let http_session =
68 50
                         AuthenticatedHTTPSession::new(http_session, auth_token, hb_token.clone());
69 51
 
70
-                    let text = http_session
71
-                        .authenticated_request_builder(reqwest::Method::GET, url.as_str())?
72
-                        .send()
73
-                        .await?
74
-                        .text()
75
-                        .await?;
52
+                    let xcode_token = http_session.get_app_token(XCODE_TOKEN_IDENTIFIER).await?;
76 53
 
77
-                    println!("{}", text);
78
-
79
-                    // let xcode_token = http_session
80
-                    //    .get_app_token("com.apple.gs.xcode.auth")
81
-                    //    .await?;
82
-                    // println!("token: {:?}", xcode_token);
54
+                    let session = XcodeSession::new(http_session, xcode_token);
55
+                    let developer = session
56
+                        .perform_developer_action(ViewDeveloperAction {})
57
+                        .await??;
58
+                    println!("{:?}", developer);
83 59
                 }
84 60
                 None => {
85 61
                     let AuthOutcome::SecondaryActionRequired(_, _secondary_action) = auth_outcome
... ...
@@ -105,19 +81,56 @@ async fn itunes_test(
105 81
     password: &str,
106 82
 ) -> Result<(), Box<dyn std::error::Error>> {
107 83
     let device = Device {
108
-        device_model: "Mac16,8".to_string(),
84
+        device_model: "MacBookPro13,2".to_string(),
109 85
         operating_system_information: "macOS;15.6.1;24G90".to_string(),
110 86
         device_uuid: "A8B31C86-359B-4D95-8950-BA5DD8FFC46F".to_string(),
111 87
     };
112 88
 
113 89
     let http_session = AnisetteHTTPSession::new(
114
-        itunes::http_session(device, APPLE_TV_BUNDLE_INFORMATION).await?,
90
+        grandslam::http_session(device, APPLE_TV_BUNDLE_INFORMATION).await?,
115 91
         proxy,
116 92
     );
117 93
 
118
-    let auth_outcome = itunes::login(&http_session, apple_id, password, 1).await?;
94
+    if !proxy.is_machine_provisioned(grandslam::GRANDSLAM_DSID)? {
95
+        grandslam::provision(&http_session).await?;
96
+    }
97
+
98
+    // Here we should fetch_auth_mode first to know if we should log in with a password.
99
+    let auth_outcome = grandslam::login(&http_session, apple_id, password).await?;
119 100
 
120
-    println!("{:?}", auth_outcome);
101
+    match &auth_outcome {
102
+        AuthOutcome::Success(server_provided_data)
103
+        | AuthOutcome::SecondaryActionRequired(Some(server_provided_data), _) => {
104
+            match grandslam::parse_tokens_from_server_provided_data(server_provided_data) {
105
+                Some((_, tokens)) => {
106
+                    let Some((_, pet)) = tokens
107
+                        .iter()
108
+                        .find(|(name, _)| name == "com.apple.gs.idms.pet")
109
+                    else {
110
+                        todo!()
111
+                    };
112
+
113
+                    let device = http_session.http_session.http_session.device;
114
+                    let http_session = AnisetteHTTPSession::new(
115
+                        itunes::http_session(device, APPLE_TV_BUNDLE_INFORMATION).await?,
116
+                        proxy,
117
+                    );
118
+                    itunes::login(&http_session, apple_id, pet.token.as_str(), 1).await?;
119
+                }
120
+                None => {
121
+                    let AuthOutcome::SecondaryActionRequired(_, _secondary_action) = auth_outcome
122
+                    else {
123
+                        panic!("Apple did not return tokens even though it didn't require any action from us??");
124
+                    };
125
+                    todo!()
126
+                }
127
+            }
128
+        }
129
+        AuthOutcome::SecondaryActionRequired(_, _)
130
+        | AuthOutcome::AnisetteResyncRequired(_)
131
+        | AuthOutcome::AnisetteReprovisionRequired
132
+        | AuthOutcome::UrlSwitchingRequired(_) => todo!(),
133
+    }
121 134
 
122 135
     Ok(())
123 136
 }
... ...
@@ -126,14 +139,22 @@ async fn itunes_test(
126 139
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
127 140
     env_logger::init();
128 141
 
142
+    let apple_id = env::var("APPLE_ID")?;
143
+    let password = env::var("APPLE_PASSWORD")?;
144
+
145
+    // Just re-use system Anisette if available
129 146
     #[cfg(target_os = "macos")]
130
-    {
147
+    // on Intel Macs, we could also use a CoreADI.framework taken from an old iTunes version.
131 148
     let proxy = adid_proxy::ADIdProxy::connect();
132
-        let code = proxy.generate_2fa_code(-2)?;
133
-        println!("{}", code);
134
-    }
149
+
150
+    #[cfg(target_os = "windows")]
151
+    let library = dlopen2::symbor::Library::open("C:\\Program Files\\iTunes\\CoreADI64.dll")?;
152
+    #[cfg(target_os = "windows")]
153
+    let proxy = library_coreadi::LibraryCoreADIProxy::new(&library)?;
154
+
155
+    #[cfg(not(any(target_os = "windows", target_os = "macos")))]
135 156
     let proxy = {
136
-        let core_adi_data = fs::read("nodistrib/lib/arm64-v8a/libCoreADI.so")?;
157
+        let core_adi_data = fs::read("nodistrib/lib/x86_64/libCoreADI.so")?;
137 158
         let proxy = AndroidCoreADIProxy::load_library(core_adi_data)?;
138 159
 
139 160
         proxy.set_android_id("0123456789012345")?;
... ...
@@ -142,11 +163,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
142 163
         proxy
143 164
     };
144 165
 
145
-    let apple_id = env::var("APPLE_ID")?;
146
-    let password = env::var("APPLE_PASSWORD")?;
166
+    println!("{:#?}", proxy.get_all_provisioned_accounts());
147 167
 
148 168
     grandslam_test(&proxy, &apple_id, &password).await?;
149
-    // itunes_test(&proxy, apple_id, password).await?;
169
+    // itunes_test(&proxy, &apple_id, &password).await?;
150 170
 
151 171
     Ok(())
152 172
 }
... ...
@@ -10,3 +10,8 @@ plist.workspace = true
10 10
 plist-macros.workspace = true
11 11
 
12 12
 reqwest.workspace = true
13
+thiserror.workspace = true
14
+
15
+serde = { version = "1.0.228", features = ["derive"] }
16
+sys-locale = "0.3.2"
17
+uuid = { version = "1.19", features = ["v4"] }
13 18
\ No newline at end of file
... ...
@@ -0,0 +1,29 @@
1
+use crate::DeveloperAction;
2
+use crate::DeveloperActionBase;
3
+use crate::{impl_developer_action, impl_developer_action_base};
4
+use plist::Dictionary;
5
+use serde::{Deserialize, Serialize};
6
+
7
+#[derive(Debug, Serialize)]
8
+pub struct ViewDeveloperAction {}
9
+impl_developer_action_base!(ViewDeveloperAction, "viewDeveloper.action");
10
+
11
+#[derive(Debug, Deserialize)]
12
+#[serde(rename_all = "camelCase")]
13
+pub struct Developer {
14
+    pub developer_id: String,
15
+    pub person_id: u64,
16
+    pub first_name: String,
17
+    pub last_name: String,
18
+    pub ds_first_name: String,
19
+    pub ds_last_name: String,
20
+    pub email: String,
21
+    pub developer_status: String, // TODO: DeveloperStatus enum
22
+}
23
+
24
+#[derive(Debug, Deserialize)]
25
+pub struct DeveloperView {
26
+    pub teams: Vec<()>,
27
+    pub developer: Developer,
28
+}
29
+impl_developer_action!(ViewDeveloperAction, DeveloperView);
... ...
@@ -1,5 +1,18 @@
1
+mod developer_actions;
2
+
3
+use adi::proxy::{ADIError, ADIResult};
1 4
 use apple_account::bundle_information::BundleInformation;
2
-use apple_account::grandslam::AppTokenIdentifier;
5
+use apple_account::grandslam::{
6
+    AppTokenIdentifier, AuthenticatedHTTPSession, GRANDSLAM_DSID, Token,
7
+};
8
+use apple_account::http_session::{AnisetteHTTPSession, HTTPSessionCreationError};
9
+use apple_account::plist_request::plist_to_body;
10
+use plist::{Dictionary, Value};
11
+use plist_macros::{array, dict};
12
+use reqwest::{Method, RequestBuilder};
13
+use serde::{Deserialize, Serialize};
14
+use std::fmt::Display;
15
+use thiserror::Error;
3 16
 
4 17
 /// From Xcode 16.4
5 18
 pub const XCODE_BUNDLE_INFORMATION: BundleInformation = BundleInformation {
... ...
@@ -10,3 +23,170 @@ pub const XCODE_BUNDLE_INFORMATION: BundleInformation = BundleInformation {
10 23
 
11 24
 pub const XCODE_TOKEN_IDENTIFIER: AppTokenIdentifier =
12 25
     AppTokenIdentifier("com.apple.gs.xcode.auth");
26
+
27
+const CLIENT_ID: &str = "XABBG36SBA";
28
+const PROTOCOL_VERSION: &str = "QH65B2";
29
+
30
+pub enum PlatformType {
31
+    IOS,
32
+    TvOS,
33
+    WatchOS,
34
+}
35
+
36
+impl From<&PlatformType> for &'static str {
37
+    fn from(value: &PlatformType) -> &'static str {
38
+        match value {
39
+            PlatformType::IOS => "ios",
40
+            PlatformType::TvOS => "tvos",
41
+            PlatformType::WatchOS => "watchos",
42
+        }
43
+    }
44
+}
45
+
46
+impl Display for PlatformType {
47
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48
+        let str: &'static str = self.into();
49
+        write!(f, "{}", str)
50
+    }
51
+}
52
+
53
+pub trait DeveloperActionBase: Serialize + Sized {
54
+    fn action() -> &'static str;
55
+
56
+    fn request(&self) -> Dictionary {
57
+        plist::to_value(self)
58
+            .expect("Serialization should never fail.")
59
+            .into_dictionary()
60
+            .expect("Usage error: every DeveloperAction is a dictionary.")
61
+    }
62
+}
63
+
64
+pub trait DeveloperAction: DeveloperActionBase {
65
+    type Result;
66
+
67
+    fn parse_response(value: Dictionary) -> Self::Result;
68
+}
69
+
70
+#[derive(Debug, Error)]
71
+pub enum XcodeError {
72
+    #[error("Failed to perform the demanded action: {} ({status_code})", user_string.as_ref().or(result_string.as_ref()).unwrap_or(&"(null)".to_string()))]
73
+    DeveloperPortal {
74
+        status_code: u64,
75
+        user_string: Option<String>,
76
+        result_string: Option<String>,
77
+    },
78
+
79
+    #[error("Failed to perform the demanded action: {0}")]
80
+    Network(#[from] reqwest::Error),
81
+    #[error("Failed to perform the demanded action: {0}")]
82
+    Anisette(#[from] ADIError),
83
+    #[error("Failed to perform the demanded action: {0}")]
84
+    Parsing(#[from] plist::Error),
85
+}
86
+
87
+pub struct XcodeSession<'a, 'b> {
88
+    pub http_session: AuthenticatedHTTPSession<'a, 'b>,
89
+    pub token: Token,
90
+}
91
+
92
+impl<'a, 'b> XcodeSession<'a, 'b> {
93
+    pub fn new(http_session: AuthenticatedHTTPSession<'a, 'b>, token: Token) -> Self {
94
+        Self {
95
+            http_session,
96
+            token,
97
+        }
98
+    }
99
+
100
+    pub async fn perform_developer_action_base<T: DeveloperActionBase>(
101
+        &self,
102
+        developer_action: T,
103
+    ) -> Result<Dictionary, XcodeError> {
104
+        let locale = sys_locale::get_locale()
105
+            .unwrap_or_else(|| String::from("en-US"))
106
+            .replace('-', "_");
107
+
108
+        let url = format!(
109
+            "https://developerservices2.apple.com/services/{PROTOCOL_VERSION}/{}",
110
+            T::action()
111
+        );
112
+        let request_id = uuid::Uuid::new_v4();
113
+
114
+        let mut base_request = dict! {
115
+            "clientId": CLIENT_ID,
116
+            "protocolVersion": PROTOCOL_VERSION,
117
+            "requestId": request_id.to_string(),
118
+            "userLocale": array![locale],
119
+        };
120
+
121
+        base_request.clone_from(&developer_action.request());
122
+
123
+        let response = self
124
+            .http_session
125
+            .authenticated_request_builder(Method::POST, url.as_str())?
126
+            .header("Content-Type", "text/x-xml-plist")
127
+            .header("Accept", "text/x-xml-plist")
128
+            .header("X-Apple-App-Info", XCODE_TOKEN_IDENTIFIER.0)
129
+            .header("X-Apple-GS-Token", &self.token)
130
+            .header("X-Xcode-Version", "16.4 (16F6)")
131
+            .query(&[("clientId", CLIENT_ID)])
132
+            .body(plist_to_body(base_request.into()))
133
+            .send()
134
+            .await?
135
+            .bytes()
136
+            .await?;
137
+
138
+        let response: Dictionary = plist::from_bytes(&response)?;
139
+        match response
140
+            .get("statusCode")
141
+            .and_then(Value::as_unsigned_integer)
142
+        {
143
+            None | Some(0) => Ok(response),
144
+            Some(status_code) => Err(XcodeError::DeveloperPortal {
145
+                status_code,
146
+                user_string: response
147
+                    .get("userString")
148
+                    .and_then(Value::as_string)
149
+                    .map(ToString::to_string),
150
+                result_string: response
151
+                    .get("resultString")
152
+                    .and_then(Value::as_string)
153
+                    .map(ToString::to_string),
154
+            }),
155
+        }
156
+    }
157
+
158
+    pub async fn perform_developer_action<T: DeveloperAction>(
159
+        &self,
160
+        developer_action: T,
161
+    ) -> Result<T::Result, XcodeError> {
162
+        Ok(T::parse_response(
163
+            self.perform_developer_action_base(developer_action).await?,
164
+        ))
165
+    }
166
+}
167
+
168
+#[macro_export]
169
+macro_rules! impl_developer_action_base {
170
+    ($name: ty, $action: literal) => {
171
+        impl DeveloperActionBase for $name {
172
+            fn action() -> &'static str {
173
+                $action
174
+            }
175
+        }
176
+    };
177
+}
178
+
179
+#[macro_export]
180
+macro_rules! impl_developer_action {
181
+    ($name: ty, $result: ty) => {
182
+        impl DeveloperAction for $name {
183
+            type Result = Result<$result, plist::Error>;
184
+
185
+            fn parse_response(value: Dictionary) -> Result<$result, plist::Error> {
186
+                plist::from_value(&value.into())
187
+            }
188
+        }
189
+    };
190
+}
191
+
192
+pub use developer_actions::*;
13 193