mirror of
https://github.com/Sneed-Group/pypush-plus-plus
synced 2024-10-30 08:27:52 +00:00
remove cloudkit stuff not ready yet
This commit is contained in:
parent
3be5a17b0f
commit
cbe553d369
4 changed files with 0 additions and 1029 deletions
|
@ -1,60 +0,0 @@
|
||||||
import sys
|
|
||||||
sys.path.append(".")
|
|
||||||
|
|
||||||
import requests
|
|
||||||
import uuid
|
|
||||||
import plistlib
|
|
||||||
from base64 import b64encode, b64decode
|
|
||||||
import json
|
|
||||||
import random
|
|
||||||
import icloud.gsa as gsa
|
|
||||||
import icloud.cloudkit as cloudkit
|
|
||||||
import icloud
|
|
||||||
|
|
||||||
from rich.logging import RichHandler
|
|
||||||
import logging
|
|
||||||
logging.basicConfig(
|
|
||||||
level=logging.INFO, format="%(message)s", datefmt="[%X]", handlers=[RichHandler()]
|
|
||||||
)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
CONFIG_PATH = "config/cloudkit.json"
|
|
||||||
# See if we have a search party token saved
|
|
||||||
import os
|
|
||||||
if os.path.exists(CONFIG_PATH):
|
|
||||||
logging.info("Using saved config...")
|
|
||||||
#print("Found search party token!")
|
|
||||||
with open(CONFIG_PATH, "r") as f:
|
|
||||||
j = json.load(f)
|
|
||||||
cloudkit_token = j["cloudkit_token"]
|
|
||||||
ds_prs_id = j["ds_prs_id"]
|
|
||||||
mme_token = j["mme_token"]
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Prompt for username and password
|
|
||||||
USERNAME = input("Username: ")
|
|
||||||
PASSWORD = input("Password: ")
|
|
||||||
|
|
||||||
r = icloud.login(USERNAME, PASSWORD, delegates=["com.apple.mobileme"])
|
|
||||||
|
|
||||||
cloudkit_token = r['delegates']['com.apple.mobileme']['service-data']['tokens']['cloudKitToken']
|
|
||||||
mme_token = r['delegates']['com.apple.mobileme']['service-data']['tokens']['mmeAuthToken']
|
|
||||||
#ds_prs_id = r['delegates']['com.apple.mobileme']['service-data']['appleAccountInfo']['dsPrsID'] # This can also be obtained from the grandslam response
|
|
||||||
ds_prs_id = r['dsid']
|
|
||||||
|
|
||||||
logging.info("Logged in!")
|
|
||||||
|
|
||||||
with open(CONFIG_PATH, "w") as f:
|
|
||||||
json.dump({
|
|
||||||
"cloudkit_token": cloudkit_token,
|
|
||||||
"ds_prs_id": ds_prs_id,
|
|
||||||
"mme_token": mme_token,
|
|
||||||
}, f, indent=4)
|
|
||||||
|
|
||||||
logging.debug("CloudKit token: ", cloudkit_token)
|
|
||||||
|
|
||||||
ck = cloudkit.CloudKit(ds_prs_id, cloudkit_token, mme_token, sandbox=True)
|
|
||||||
ck.container("iCloud.dev.jjtech.experiments.cktest").save_record(cloudkit.Record(uuid.uuid4(), "ToDoItem", {"title": "Test"}))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
|
@ -1,594 +0,0 @@
|
||||||
syntax = "proto2";
|
|
||||||
|
|
||||||
// Based on https://gist.github.com/horrorho/9c0ff20055b854f4298750cfad635fe2
|
|
||||||
// and https://github.com/horrorho/InflatableDonkey/blob/master/src/main/resources/cloud_kit.proto
|
|
||||||
message RequestOperation {
|
|
||||||
message Header {
|
|
||||||
enum ContainerEnvironment {
|
|
||||||
PRODUCTION = 1;
|
|
||||||
SANDBOX = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Database {
|
|
||||||
PRIVATE_DB = 1;
|
|
||||||
PUBLIC_DB = 2;
|
|
||||||
SHARED_DB = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum IsolationLevel {
|
|
||||||
ZONE = 1;
|
|
||||||
OPERATION = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional string userToken = 1;
|
|
||||||
optional string applicationContainer = 2;
|
|
||||||
optional string applicationBundle = 3;
|
|
||||||
optional string applicationVersion = 4;
|
|
||||||
optional uint64 applicationConfigVersion = 5;
|
|
||||||
optional uint64 globalConfigVersion = 6;
|
|
||||||
optional Identifier deviceIdentifier = 7;
|
|
||||||
optional string deviceSoftwareVersion = 8;
|
|
||||||
optional string deviceHardwareVersion = 9;
|
|
||||||
optional string deviceLibraryName = 10;
|
|
||||||
optional string deviceLibraryVersion = 11;
|
|
||||||
optional string deviceFlowControlKey = 12;
|
|
||||||
optional uint64 deviceFlowControlBudget = 13;
|
|
||||||
optional uint64 deviceFlowControlBudgetCap = 14;
|
|
||||||
optional float deviceFlowControlRegeneration = 15;
|
|
||||||
optional uint64 deviceProtocolVersion = 16;
|
|
||||||
optional Locale locale = 17;
|
|
||||||
optional string mmcsProtocolVersion = 18;
|
|
||||||
optional ContainerEnvironment applicationContainerEnvironment = 19;
|
|
||||||
optional bytes clientChangeToken = 20;
|
|
||||||
optional string deviceAssignedName = 21;
|
|
||||||
optional string deviceHardwareID = 22;
|
|
||||||
optional Database targetDatabase = 23;
|
|
||||||
optional string userIDContainerID = 24;
|
|
||||||
optional IsolationLevel isolationLevel = 25;
|
|
||||||
optional int32 unk1 = 29;
|
|
||||||
optional string unk2 = 32; // UUID
|
|
||||||
optional string deviceSerial = 33;
|
|
||||||
optional int32 unk3 = 34;
|
|
||||||
optional int32 unk4 = 35;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Header header = 1;
|
|
||||||
optional Operation request = 2;
|
|
||||||
optional ZoneRetrieveRequest zoneRetrieveRequest = 201;
|
|
||||||
optional RecordSaveRequest recordSaveRequest = 210;
|
|
||||||
optional RecordRetrieveRequest recordRetrieveRequest = 211;
|
|
||||||
optional QueryRetrieveRequest queryRetrieveRequest = 220;
|
|
||||||
}
|
|
||||||
|
|
||||||
message RecordSaveRequest {
|
|
||||||
optional Record record = 1;
|
|
||||||
optional int32 unk1 = 2; // 1
|
|
||||||
optional int32 unk2 = 6; // 2
|
|
||||||
}
|
|
||||||
|
|
||||||
message ResponseOperation {
|
|
||||||
message Result {
|
|
||||||
message Error {
|
|
||||||
message Client {
|
|
||||||
enum Code {
|
|
||||||
UNKNOWN = 1;
|
|
||||||
EXPIRED_APP_CONFIG = 2;
|
|
||||||
EXPIRED_GLOBAL_CONFIG = 3;
|
|
||||||
BAD_SYNTAX = 4;
|
|
||||||
FORBIDDEN = 5;
|
|
||||||
THROTTLED = 6;
|
|
||||||
REFUSED = 7;
|
|
||||||
NOT_SUPPORTED = 8;
|
|
||||||
EXISTS = 9;
|
|
||||||
REQUEST_ALREADY_PROCESSED = 10;
|
|
||||||
BAD_AUTH_TOKEN = 11;
|
|
||||||
NEEDS_AUTHENTICATION = 12;
|
|
||||||
MESCAL_SIGNATURE_MISSING = 13;
|
|
||||||
INVALID_MESCAL_SIGNATURE = 14;
|
|
||||||
OP_LOCK_FAILURE = 15;
|
|
||||||
ATOMIC_FAILURE = 16;
|
|
||||||
RESET_NEEDED = 17;
|
|
||||||
FIELDS_PER_TYPE_LIMIT_EXCEEDED = 18;
|
|
||||||
TYPE_BUSY = 19;
|
|
||||||
NUM_RECORD_TYPES_LIMIT_EXCEEDED = 20;
|
|
||||||
INVALID_CONTAINER = 21;
|
|
||||||
INVALID_RECORD_TYPE_NAME = 22;
|
|
||||||
INVALID_FIELD_NAME = 23;
|
|
||||||
INVALID_FIELD_VALUE = 24;
|
|
||||||
INVALID_IDENTIFIER = 25;
|
|
||||||
FIELD_NOT_QUERYABLE = 26;
|
|
||||||
FIELD_NOT_SORTABLE = 27;
|
|
||||||
QUERY_FILTER_LIMIT_EXCEEDED = 28;
|
|
||||||
QUERY_FILTER_VALUES_LIMIT_EXCEEDED = 29;
|
|
||||||
MEMBERSHIP_QUERY_LIMIT_EXCEEDED = 30;
|
|
||||||
NOTIFICATION_ADDITIONAL_FIELD_LIMIT_EXCEEDED = 31;
|
|
||||||
NOTIFICATION_ADDITIONAL_FIELD_INVALID_TYPE = 32;
|
|
||||||
SUBSCRIPTION_LIMIT_EXCEEDED = 33;
|
|
||||||
UNIQUE_TRIGGER_LIMIT_EXCEEDED = 34;
|
|
||||||
TRIGGER_SUBSCRIPTION_BINDING_TYPE_MISMATCH = 35;
|
|
||||||
ZONE_SIZE_LIMIT_EXCEEDED = 36;
|
|
||||||
ZONE_COUNT_LIMIT_EXCEEDED = 37;
|
|
||||||
UNIQUE_FIELD_FAILURE = 38;
|
|
||||||
VALIDATING_REFERENCE_ERROR = 39;
|
|
||||||
FULL_RESET_NEEDED = 40;
|
|
||||||
ALREADY_SHARED = 41;
|
|
||||||
EMAIL_OUT_OF_NETWORK = 42;
|
|
||||||
DUPLICATE_SUBSCRIPTION = 43;
|
|
||||||
EXPIRED_PUT_RECEIPT = 46;
|
|
||||||
QUOTA_EXCEEDED = 47;
|
|
||||||
ZONE_NOT_FOUND = 48;
|
|
||||||
INVALID_BUNDLE_ID = 49;
|
|
||||||
UNSUPPORTED_DEVICE = 50;
|
|
||||||
BLACK_LISTED = 51;
|
|
||||||
RECORD_PROTECTION_INFO_TAG_MISMATCH = 52;
|
|
||||||
ZONE_PROTECTION_INFO_TAG_MISMATCH = 53;
|
|
||||||
ASSET_SIZE_LIMIT_EXCEEDED = 54;
|
|
||||||
BATCH_OPERATION_LIMIT_EXCEEDED = 55;
|
|
||||||
REQUEST_SIZE_LIMIT_EXCEEDED = 56;
|
|
||||||
RECORD_SIZE_LIMIT_EXCEEDED = 57;
|
|
||||||
DATABASE_COMMIT_SIZE_EXCEEDED = 58;
|
|
||||||
USER_DELETED_DATA_FOR_ZONE = 59;
|
|
||||||
STALE_RECORD_UPDATE = 60;
|
|
||||||
SHARE_PARTICIPANT_LIMIT_EXCEEDED = 61;
|
|
||||||
SHARE_PARTICIPANT_ERROR = 62;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Code type = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Server {
|
|
||||||
enum Code {
|
|
||||||
UNKNOWN = 1;
|
|
||||||
OVERLOADED = 2;
|
|
||||||
NOT_FOUND = 3;
|
|
||||||
CONTAINER_UNAVAILABLE = 4;
|
|
||||||
MESCAL_SIGNATURE_PARSE_ERROR = 6;
|
|
||||||
ZONE_BUSY = 7;
|
|
||||||
ZONE_UNAVAILABLE = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Code type = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Extension {
|
|
||||||
optional string extensionName = 1;
|
|
||||||
optional uint32 typeCode = 2;
|
|
||||||
optional bytes extensionPayload = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Client clientError = 1;
|
|
||||||
optional Server serverError = 2;
|
|
||||||
optional Extension extensionError = 7;
|
|
||||||
optional int32 retryAfterSeconds = 3;
|
|
||||||
optional string errorDescription = 4;
|
|
||||||
optional string errorKey = 5;
|
|
||||||
optional string errorInternal = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Code {
|
|
||||||
SUCCESS = 1;
|
|
||||||
PARTIAL = 2;
|
|
||||||
FAILURE = 3;
|
|
||||||
INDETERMINATE = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Code code = 1;
|
|
||||||
optional Error error = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional uint32 operationCost = 1;
|
|
||||||
optional Operation response = 2;
|
|
||||||
optional Result result = 3;
|
|
||||||
optional ZoneRetrieveResponse zoneRetrieveResponse = 201;
|
|
||||||
optional RecordRetrieveResponse recordRetrieveResponse = 211;
|
|
||||||
optional QueryRetrieveResponse queryRetrieveResponse = 220;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Operation {
|
|
||||||
enum Type {
|
|
||||||
NONE_TYPE = 0;
|
|
||||||
ZONE_SAVE_TYPE = 200;
|
|
||||||
ZONE_RETRIEVE_TYPE = 201;
|
|
||||||
ZONE_DELETE_TYPE = 202;
|
|
||||||
ZONE_RETRIEVE_CHANGES_TYPE = 203;
|
|
||||||
RECORD_SAVE_TYPE = 210;
|
|
||||||
RECORD_RETRIEVE_TYPE = 211;
|
|
||||||
RECORD_RETRIEVE_VERSIONS_TYPE = 212;
|
|
||||||
RECORD_RETRIEVE_CHANGES_TYPE = 213;
|
|
||||||
RECORD_DELETE_TYPE = 214;
|
|
||||||
QUERY_RETRIEVE_TYPE = 220;
|
|
||||||
ASSET_UPLOAD_TOKEN_RETRIEVE_TYPE = 230;
|
|
||||||
CONTAINER_DELETE_TYPE = 240;
|
|
||||||
CONTAINER_RESET_TYPE = 241;
|
|
||||||
CONTAINER_SCHEMA_PROMOTION_TYPE = 242;
|
|
||||||
USER_AVAILABLE_QUOTA_TYPE = 243;
|
|
||||||
WEB_AUTH_TOKEN_RETRIEVE_TYPE = 250;
|
|
||||||
SUBSCRIPTION_CREATE_TYPE = 300;
|
|
||||||
SUBSCRIPTION_RETRIEVE_TYPE = 301;
|
|
||||||
SUBSCRIPTION_DELETE_TYPE = 302;
|
|
||||||
USER_RETRIEVE_TYPE = 400;
|
|
||||||
USER_QUERYTYPE = 401;
|
|
||||||
USER_PRIVACY_SETTINGS_RETRIEVE_TYPE = 402;
|
|
||||||
USER_PRIVACY_SETTINGS_UPDATE_TYPE = 403;
|
|
||||||
USER_PRIVACY_SETTINGS_RESET_TYPE = 404;
|
|
||||||
USER_PRIVACY_SETTINGS_BATCH_LOOKUP = 405;
|
|
||||||
SHARE_SAVE_TYPE = 500;
|
|
||||||
SHARE_RETRIEVE_TYPE = 501;
|
|
||||||
SHARE_DELETE_TYPE = 502;
|
|
||||||
SHARE_ACCEPT_TYPE = 503;
|
|
||||||
SHARE_TOKEN_SAVE_TYPE = 510;
|
|
||||||
SHARE_TOKEN_RETRIEVE_TYPE = 511;
|
|
||||||
SHARE_TOKEN_DELETE_TYPE = 512;
|
|
||||||
POST_COMMENT_TYPE = 600;
|
|
||||||
GET_COMMENTS_TYPE = 601;
|
|
||||||
GET_COMMENT_TYPE = 613;
|
|
||||||
DELETE_COMMENT_TYPE = 602;
|
|
||||||
LIKE_TYPE = 610;
|
|
||||||
UNLIKE_TYPE = 611;
|
|
||||||
GET_LIKES_TYPE = 612;
|
|
||||||
PULSE_TYPE = 700;
|
|
||||||
PUSH_REGISTER_TYPE = 800;
|
|
||||||
PUSH_UNREGISTER_TYPE = 801;
|
|
||||||
PUSH_BADGE_TYPE = 802;
|
|
||||||
PUSH_SYNC_TYPE = 803;
|
|
||||||
PUSH_READ_TYPE = 804;
|
|
||||||
MESCAL_SIGNATURE_TYPE = 1000;
|
|
||||||
MESCAL_SESSION_INFO_TYPE = 1001;
|
|
||||||
MESCAL_CERTIFICATE_TYPE = 1002;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional string operationUUID = 1;
|
|
||||||
optional Type type = 2;
|
|
||||||
optional bool synchronousMode = 3;
|
|
||||||
optional bool last = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// /api/client/zone/retrieve
|
|
||||||
message ZoneRetrieveRequest {
|
|
||||||
optional RecordZoneIdentifier zoneIdentifier = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ZoneRetrieveResponse {
|
|
||||||
message ZoneSummary {
|
|
||||||
optional Zone targetZone = 1;
|
|
||||||
optional bytes currentServerContinuationToken = 2;
|
|
||||||
optional bytes clientChangeToken = 3;
|
|
||||||
optional int32 deviceCount = 4;
|
|
||||||
optional int64 assetQuotaUsage = 5;
|
|
||||||
optional int64 metadataQuotaUsage = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
repeated ZoneSummary zoneSummary = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// /api/client/record/retrieve
|
|
||||||
message RecordRetrieveRequest {
|
|
||||||
message RetrieveAssetURL {
|
|
||||||
enum Type {
|
|
||||||
PUBLISHED_URL = 1;
|
|
||||||
STREAMING_URL = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional RequestedFields assetFields = 1;
|
|
||||||
optional int64 requestedTTL = 2;
|
|
||||||
optional Type type = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional RecordIdentifier recordIdentifier = 1;
|
|
||||||
optional RequestedFields requestedFields = 2;
|
|
||||||
optional string versionETag = 3;
|
|
||||||
optional string clientVersionETag = 4;
|
|
||||||
optional RetrieveAssetURL getAssetURL = 5;
|
|
||||||
optional AssetsToDownload assetsToDownload = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
message RecordRetrieveResponse {
|
|
||||||
optional Record record = 1;
|
|
||||||
optional bool clientVersionETagMatch = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// /api/client/query/retrieve
|
|
||||||
message QueryRetrieveRequest {
|
|
||||||
optional Query query = 1;
|
|
||||||
optional bytes continuationMarker = 2;
|
|
||||||
optional uint32 limit = 3;
|
|
||||||
optional RecordZoneIdentifier zoneIdentifier = 4;
|
|
||||||
optional RequestedFields requestedFields = 5;
|
|
||||||
optional AssetsToDownload assetsToDownload = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
message QueryRetrieveResponse {
|
|
||||||
message QueryResult {
|
|
||||||
enum Type {
|
|
||||||
ID_AND_ETAG = 1;
|
|
||||||
FULL_RECORD = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional RecordIdentifier identifier = 1;
|
|
||||||
optional string etag = 2;
|
|
||||||
optional Type type = 3;
|
|
||||||
optional Record record = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
repeated QueryResult queryResults = 1;
|
|
||||||
optional bytes continuationMarker = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Query {
|
|
||||||
message Filter {
|
|
||||||
enum Type {
|
|
||||||
EQUALS = 1;
|
|
||||||
NOT_EQUALS = 2;
|
|
||||||
IN = 3;
|
|
||||||
NOT_IN = 4;
|
|
||||||
LESS_THAN = 5;
|
|
||||||
LESS_THAN_OR_EQUALS = 6;
|
|
||||||
GREATER_THAN = 7;
|
|
||||||
GREATER_THAN_OR_EQUALS = 8;
|
|
||||||
NEAR = 9;
|
|
||||||
CONTAINS_ALL_TOKENS = 10;
|
|
||||||
CONTAINS_ANY_TOKENS = 11;
|
|
||||||
LIST_CONTAINS = 12;
|
|
||||||
LIST_NOT_CONTAINS = 13;
|
|
||||||
LIST_CONTAINS_ANY = 14;
|
|
||||||
LIST_NOT_CONTAINS_ANY = 15;
|
|
||||||
BEGINS_WITH = 16;
|
|
||||||
NOT_BEGINS_WITH = 17;
|
|
||||||
LIST_MEMBER_BEGINS_WITH = 18;
|
|
||||||
NOT_LIST_MEMBER_BEGINS_WITH = 19;
|
|
||||||
LIST_CONTAINS_ALL = 20;
|
|
||||||
LIST_NOT_CONTAINS_ALL = 21;
|
|
||||||
UNKNOWN = 22;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Record.Field.Identifier fieldName = 1;
|
|
||||||
optional Record.Field.Value fieldValue = 2;
|
|
||||||
optional Location.Bound bounds = 3;
|
|
||||||
optional Type type = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Sort {
|
|
||||||
enum Order {
|
|
||||||
ASCENDING = 1;
|
|
||||||
DESCENDING = 2;
|
|
||||||
UNKNOWN = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Record.Field.Identifier fieldName= 1;
|
|
||||||
optional Order order = 2;
|
|
||||||
optional Location.Coordinate coordinate = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum QueryOperator {
|
|
||||||
AND = 1;
|
|
||||||
OR = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
repeated Record.Type types = 1;
|
|
||||||
repeated Filter filters = 2;
|
|
||||||
repeated Sort sorts = 3;
|
|
||||||
optional bool distinct = 4;
|
|
||||||
optional QueryOperator queryOperator = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Date {
|
|
||||||
optional double time = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Asset {
|
|
||||||
optional string owner = 1;
|
|
||||||
optional bytes signature = 2;
|
|
||||||
optional bytes header = 3;
|
|
||||||
optional uint64 size = 4;
|
|
||||||
optional string downloadToken = 5;
|
|
||||||
optional bytes downloadRequest = 6;
|
|
||||||
optional string derivedContentType = 7;
|
|
||||||
optional string contentBaseURL = 8;
|
|
||||||
optional string requestor = 9;
|
|
||||||
optional RecordIdentifier recordID = 10;
|
|
||||||
optional string uploadReceipt = 11;
|
|
||||||
optional bytes data = 12;
|
|
||||||
optional string downloadBaseURL = 13;
|
|
||||||
optional uint64 downloadURLExpiration = 14;
|
|
||||||
optional ProtectionInfo protectionInfo = 15;
|
|
||||||
optional bytes referenceSignature = 17;
|
|
||||||
optional uint64 downloadTokenExpiration = 18;
|
|
||||||
}
|
|
||||||
|
|
||||||
message DateStatistics {
|
|
||||||
optional Date creation = 1;
|
|
||||||
optional Date modification = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Location {
|
|
||||||
message Bound {
|
|
||||||
optional double radius = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Coordinate {
|
|
||||||
optional double lattitude = 1;
|
|
||||||
optional double longitude = 2;
|
|
||||||
optional double horizontalAccuracy = 3;
|
|
||||||
optional double altitude = 4;
|
|
||||||
optional double verticalAccuracy = 5;
|
|
||||||
optional double course = 6;
|
|
||||||
optional double speed = 7;
|
|
||||||
optional Date timestamp = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Coordinate coordinate = 1;
|
|
||||||
optional Bound bounds = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Package {
|
|
||||||
optional Asset manifest = 1;
|
|
||||||
repeated Asset sections = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Record {
|
|
||||||
message Field {
|
|
||||||
message Identifier {
|
|
||||||
optional string name = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Value {
|
|
||||||
enum Type {
|
|
||||||
BYTES_TYPE = 1;
|
|
||||||
DATE_TYPE = 2;
|
|
||||||
STRING_TYPE = 3;
|
|
||||||
LOCATION_TYPE = 4;
|
|
||||||
REFERENCE_TYPE =5;
|
|
||||||
ASSET_TYPE = 6;
|
|
||||||
INT64_TYPE = 7;
|
|
||||||
DOUBLE_TYPE = 8;
|
|
||||||
EMPTY_LIST = 9;
|
|
||||||
DATE_LIST_TYPE = 10;
|
|
||||||
BYTES_LIST_TYPE = 11;
|
|
||||||
LOCATION_LIST_TYPE = 12;
|
|
||||||
REFERENCE_LIST_TYPE = 13;
|
|
||||||
ASSET_LIST_TYPE = 14;
|
|
||||||
STRING_LIST_TYPE = 15;
|
|
||||||
LIST_TYPE = 16;
|
|
||||||
INT64_LIST_TYPE = 17;
|
|
||||||
DOUBLE_LIST_TYPE = 18;
|
|
||||||
PACKAGE_TYPE = 19;
|
|
||||||
ENCRYPTED_BYTES_TYPE = 20;
|
|
||||||
ENCRYPTED_BYTES_LIST_TYPE = 21;
|
|
||||||
UNKNOWN = 22;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Type type = 1;
|
|
||||||
optional bytes bytesValue = 2;
|
|
||||||
optional int64 signedValue = 4;
|
|
||||||
optional double doubleValue = 5;
|
|
||||||
optional Date dateValue = 6;
|
|
||||||
optional string stringValue = 7;
|
|
||||||
optional Location.Coordinate locationValue = 8;
|
|
||||||
optional Reference referenceValue = 9;
|
|
||||||
optional Asset assetValue = 10;
|
|
||||||
repeated Value listValues = 11;
|
|
||||||
optional Package packageValue = 12;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Identifier identifier = 1;
|
|
||||||
optional Value value = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Reference {
|
|
||||||
enum Type {
|
|
||||||
OWNING = 1;
|
|
||||||
WEAK = 2;
|
|
||||||
VALIDATING = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional Type type = 1;
|
|
||||||
optional RecordIdentifier recordIdentifier = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Type {
|
|
||||||
optional string name = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
optional string etag= 1;
|
|
||||||
optional RecordIdentifier recordIdentifier = 2;
|
|
||||||
optional Record.Type type = 3;
|
|
||||||
optional Identifier createdBy = 4;
|
|
||||||
optional DateStatistics timeStatistics = 5;
|
|
||||||
repeated Record.Field recordField = 7;
|
|
||||||
optional ShareIdentifier shareId = 8;;
|
|
||||||
optional Identifier modifiedBy = 9;
|
|
||||||
repeated string conflictLoserEtag = 10;
|
|
||||||
optional string modifiedByDevice = 11;
|
|
||||||
repeated Record.Field pluginFields = 12;
|
|
||||||
optional ProtectionInfo protectionInfo = 13;
|
|
||||||
optional uint32 permission = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
message RequestedFields {
|
|
||||||
repeated Record.Field.Identifier fields = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message AssetsToDownload {
|
|
||||||
optional bool allAssets = 1;
|
|
||||||
optional RequestedFields assetFields = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Locale {
|
|
||||||
optional string languageCode = 1;
|
|
||||||
optional string regionCode = 2;
|
|
||||||
repeated string enabledKeyboards = 3;
|
|
||||||
optional string activeKeyboard = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ShareIdentifier {
|
|
||||||
optional Identifier value = 1;
|
|
||||||
optional RecordZoneIdentifier zoneIdentifier = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message RecordIdentifier {
|
|
||||||
optional Identifier value = 1;
|
|
||||||
optional RecordZoneIdentifier zoneIdentifier = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message RecordZoneIdentifier {
|
|
||||||
optional Identifier value = 1;
|
|
||||||
optional Identifier ownerIdentifier = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Identifier {
|
|
||||||
enum Type {
|
|
||||||
RECORD = 1;
|
|
||||||
DEVICE = 2;
|
|
||||||
SUBSCRIPTION = 3;
|
|
||||||
SHARE = 4;
|
|
||||||
COMMENT = 5;
|
|
||||||
RECORD_ZONE = 6;
|
|
||||||
USER = 7;
|
|
||||||
}
|
|
||||||
optional string name = 1;
|
|
||||||
optional Type type = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ProtectionInfo {
|
|
||||||
optional bytes protectionInfo = 1;
|
|
||||||
optional string protectionInfoTag = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Zone {
|
|
||||||
optional RecordZoneIdentifier zoneIdentifier = 1;
|
|
||||||
optional string etag= 2;
|
|
||||||
optional ProtectionInfo protectionInfo = 3;
|
|
||||||
optional ProtectionInfo recordProtectionInfo = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
message FileTokens {
|
|
||||||
repeated FileToken fileTokens = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message FileToken {
|
|
||||||
optional bytes signature = 1;
|
|
||||||
optional string downloadToken = 2;
|
|
||||||
optional uint64 size = 3;
|
|
||||||
optional bytes referenceSignature = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message EncryptedAttributes {
|
|
||||||
optional string relativePath = 1;
|
|
||||||
optional string domain = 2;
|
|
||||||
optional uint64 birth = 3;
|
|
||||||
optional uint64 modified = 4;
|
|
||||||
optional uint64 statusChanged = 5;
|
|
||||||
optional uint64 size = 6;
|
|
||||||
optional uint32 groupID = 7;
|
|
||||||
optional uint32 userID = 8;
|
|
||||||
optional uint32 mode = 9;
|
|
||||||
optional uint64 sizeBeforeCopy = 10;
|
|
||||||
optional bytes linkTarget = 11;
|
|
||||||
optional bytes encryptionKey = 12;
|
|
||||||
optional bytes sha256Signature = 13;
|
|
||||||
optional int32 domainOrdinal = 14;
|
|
||||||
optional int32 flags = 15;
|
|
||||||
optional int32 contentEncodingMethod = 16;
|
|
||||||
optional int32 contentCompressionMethod = 17;
|
|
||||||
}
|
|
|
@ -1,227 +0,0 @@
|
||||||
import dataclasses
|
|
||||||
import logging
|
|
||||||
import random
|
|
||||||
import typing
|
|
||||||
import uuid
|
|
||||||
from typing import Literal
|
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from . import _utils, cloudkit_pb2, gsa
|
|
||||||
|
|
||||||
logger = logging.getLogger("cloudkit")
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
|
||||||
class Record:
|
|
||||||
name: uuid.UUID
|
|
||||||
type: str
|
|
||||||
fields: dict[str, typing.Any]
|
|
||||||
|
|
||||||
|
|
||||||
class CloudKit:
|
|
||||||
def __init__(
|
|
||||||
self, dsid: str, cloudkit_token: str, mme_token: str, sandbox: bool = False
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Represents a CloudKit user.
|
|
||||||
`dsid`: The user's DSID.
|
|
||||||
`cloudkit_token`: `cloudKitToken` from the `com.apple.mobileme` delegate.
|
|
||||||
`mme_token`: `mmeAuthToken` from the `com.apple.mobileme` delegate.
|
|
||||||
`sandbox`: Whether to use the CloudKit sandbox environment.
|
|
||||||
"""
|
|
||||||
self.dsid = dsid
|
|
||||||
self.cloudkit_token = cloudkit_token
|
|
||||||
self.mme_token = mme_token
|
|
||||||
self.sandbox = sandbox
|
|
||||||
|
|
||||||
def container(
|
|
||||||
self,
|
|
||||||
container: str,
|
|
||||||
scope: Literal["PUBLIC"] | Literal["PRIVATE"] | Literal["SHARED"] = "PUBLIC",
|
|
||||||
) -> "CloudKitContainer":
|
|
||||||
"""
|
|
||||||
Convenience method for creating a CloudKitContainer object.
|
|
||||||
"""
|
|
||||||
return CloudKitContainer(container, self, scope)
|
|
||||||
|
|
||||||
|
|
||||||
class CloudKitContainer:
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
container: str,
|
|
||||||
user: CloudKit,
|
|
||||||
scope: Literal["PUBLIC"] | Literal["PRIVATE"] | Literal["SHARED"] = "PUBLIC",
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Represents a CloudKit container.
|
|
||||||
container: The CloudKit container ID. (e.g. "iCloud.dev.jjtech.experiments.cktest")
|
|
||||||
user: The CloudKit user to use for authentication.
|
|
||||||
scope: The CloudKit database scope to use.
|
|
||||||
"""
|
|
||||||
self.container = container
|
|
||||||
self.user = user
|
|
||||||
self.scope = scope
|
|
||||||
self.user_id = self._fetch_user_id()
|
|
||||||
|
|
||||||
def _fetch_user_id(self):
|
|
||||||
headers = {
|
|
||||||
"x-cloudkit-containerid": self.container,
|
|
||||||
"x-cloudkit-bundleid": ".".join(
|
|
||||||
self.container.split(".")[1:]
|
|
||||||
), # Remove the "iCloud." prefix
|
|
||||||
"x-cloudkit-databasescope": self.scope,
|
|
||||||
"x-cloudkit-environment": "Sandbox" if self.user.sandbox else "Production",
|
|
||||||
"accept": "application/x-protobuf",
|
|
||||||
"x-apple-operation-id": random.randbytes(8).hex(),
|
|
||||||
"x-apple-request-uuid": str(uuid.uuid4()).upper(),
|
|
||||||
}
|
|
||||||
|
|
||||||
headers.update(gsa.generate_anisette_headers())
|
|
||||||
|
|
||||||
r = requests.post(
|
|
||||||
"https://gateway.icloud.com/setup/setup/ck/v1/ckAppInit",
|
|
||||||
params={"container": self.container},
|
|
||||||
headers=headers,
|
|
||||||
auth=(self.user.dsid, self.user.mme_token),
|
|
||||||
verify=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.debug("Got app init response: ", r.content)
|
|
||||||
return r.json()["cloudKitUserId"]
|
|
||||||
|
|
||||||
def save_record(
|
|
||||||
self, record: Record, zone: str = "_defaultZone", owner: str = "_defaultOwner"
|
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Saves a record to the container.
|
|
||||||
"""
|
|
||||||
logger.info(f"Saving record {record.name} to {self.container}")
|
|
||||||
|
|
||||||
headers = {
|
|
||||||
"x-cloudkit-authtoken": self.user.cloudkit_token,
|
|
||||||
"x-cloudkit-userid": self.user_id,
|
|
||||||
"x-cloudkit-containerid": self.container,
|
|
||||||
"x-cloudkit-bundleid": ".".join(
|
|
||||||
self.container.split(".")[1:]
|
|
||||||
), # Remove the "iCloud." prefix
|
|
||||||
"x-cloudkit-databasescope": self.scope,
|
|
||||||
"x-cloudkit-environment": "Sandbox" if self.user.sandbox else "Production",
|
|
||||||
"accept": "application/x-protobuf",
|
|
||||||
"content-type": 'application/x-protobuf; desc="https://gateway.icloud.com:443/static/protobuf/CloudDB/CloudDBClient.desc"; messageType=RequestOperation; delimited=true',
|
|
||||||
"x-apple-operation-id": random.randbytes(8).hex(),
|
|
||||||
"x-apple-request-uuid": str(uuid.uuid4()).upper(),
|
|
||||||
"user-agent": "CloudKit/2060.11 (22F82)",
|
|
||||||
}
|
|
||||||
|
|
||||||
headers.update(gsa.generate_anisette_headers())
|
|
||||||
|
|
||||||
body = _build_record_save_request(
|
|
||||||
record, self.container, self.user.sandbox, self.scope, zone, owner
|
|
||||||
)
|
|
||||||
r = requests.post(
|
|
||||||
"https://gateway.icloud.com/ckdatabase/api/client/record/save",
|
|
||||||
headers=headers,
|
|
||||||
data=body,
|
|
||||||
verify=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
_parse_response(r.content) # Will raise an exception if the response is an error
|
|
||||||
|
|
||||||
def _parse_response(response: bytes):
|
|
||||||
from io import BytesIO
|
|
||||||
length, read = _utils.ULEB128.decode_reader(BytesIO(response))
|
|
||||||
if length + read < len(response):
|
|
||||||
logger.warning(f"Response is longer than expected: {length + read} < {len(response)} (multiple messages?)")
|
|
||||||
response = response[read:length+read]
|
|
||||||
|
|
||||||
try:
|
|
||||||
r = cloudkit_pb2.ResponseOperation.FromString(response)
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Failed to parse response: {e} {response.hex()}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
if r.result.code != cloudkit_pb2.ResponseOperation.Result.Code.SUCCESS:
|
|
||||||
if r.result.code == cloudkit_pb2.ResponseOperation.Result.Code.FAILURE:
|
|
||||||
raise Exception(f"CloudKit request failed: {r.result.error.errorDescription}")
|
|
||||||
else:
|
|
||||||
raise Exception("Unknown CloudKit error")
|
|
||||||
|
|
||||||
|
|
||||||
def _build_record_save_request(
|
|
||||||
record: Record,
|
|
||||||
container: str,
|
|
||||||
sandbox: bool = False,
|
|
||||||
database: Literal["PUBLIC"] | Literal["PRIVATE"] | Literal["SHARED"] = "PUBLIC",
|
|
||||||
zone: str = "_defaultZone",
|
|
||||||
owner: str = "_defaultOwner",
|
|
||||||
):
|
|
||||||
hardware_id = uuid.uuid4() # Generate a new hardware ID for each request?
|
|
||||||
operation_uuid = uuid.uuid4() # Generate a new operation UUID for each request?
|
|
||||||
record_id = uuid.uuid4() # Generate a new record ID for each request?
|
|
||||||
|
|
||||||
request = cloudkit_pb2.RequestOperation()
|
|
||||||
request.header.applicationContainer = container
|
|
||||||
request.header.applicationContainerEnvironment = (
|
|
||||||
cloudkit_pb2.RequestOperation.Header.ContainerEnvironment.SANDBOX
|
|
||||||
if sandbox
|
|
||||||
else cloudkit_pb2.RequestOperation.Header.ContainerEnvironment.PRODUCTION
|
|
||||||
)
|
|
||||||
|
|
||||||
request.header.deviceHardwareID = str(hardware_id).upper()
|
|
||||||
|
|
||||||
if database == "PUBLIC":
|
|
||||||
request.header.targetDatabase = (
|
|
||||||
cloudkit_pb2.RequestOperation.Header.Database.PUBLIC_DB
|
|
||||||
)
|
|
||||||
elif database == "PRIVATE":
|
|
||||||
request.header.targetDatabase = (
|
|
||||||
cloudkit_pb2.RequestOperation.Header.Database.PRIVATE_DB
|
|
||||||
)
|
|
||||||
elif database == "SHARED":
|
|
||||||
request.header.targetDatabase = (
|
|
||||||
cloudkit_pb2.RequestOperation.Header.Database.SHARED_DB
|
|
||||||
)
|
|
||||||
|
|
||||||
request.header.isolationLevel = (
|
|
||||||
cloudkit_pb2.RequestOperation.Header.IsolationLevel.ZONE
|
|
||||||
)
|
|
||||||
|
|
||||||
request.request.operationUUID = str(operation_uuid).upper()
|
|
||||||
request.request.type = cloudkit_pb2.Operation.Type.RECORD_SAVE_TYPE
|
|
||||||
request.request.last = True
|
|
||||||
|
|
||||||
request.recordSaveRequest.record.recordIdentifier.value.name = str(
|
|
||||||
record_id
|
|
||||||
).upper()
|
|
||||||
request.recordSaveRequest.record.recordIdentifier.value.type = (
|
|
||||||
cloudkit_pb2.Identifier.Type.RECORD
|
|
||||||
)
|
|
||||||
|
|
||||||
request.recordSaveRequest.record.recordIdentifier.zoneIdentifier.value.name = zone
|
|
||||||
request.recordSaveRequest.record.recordIdentifier.zoneIdentifier.value.type = (
|
|
||||||
cloudkit_pb2.Identifier.Type.RECORD_ZONE
|
|
||||||
)
|
|
||||||
|
|
||||||
request.recordSaveRequest.record.recordIdentifier.zoneIdentifier.ownerIdentifier.name = (
|
|
||||||
owner
|
|
||||||
)
|
|
||||||
request.recordSaveRequest.record.recordIdentifier.zoneIdentifier.ownerIdentifier.type = (
|
|
||||||
cloudkit_pb2.Identifier.Type.USER
|
|
||||||
)
|
|
||||||
|
|
||||||
request.recordSaveRequest.record.type.name = record.type
|
|
||||||
|
|
||||||
for key, value in record.fields.items():
|
|
||||||
request.recordSaveRequest.record.recordField.append(cloudkit_pb2.Record.Field())
|
|
||||||
request.recordSaveRequest.record.recordField[-1].identifier.name = key
|
|
||||||
request.recordSaveRequest.record.recordField[
|
|
||||||
-1
|
|
||||||
].value.type = cloudkit_pb2.Record.Field.Value.Type.STRING_TYPE
|
|
||||||
request.recordSaveRequest.record.recordField[-1].value.stringValue = value
|
|
||||||
|
|
||||||
len_bytes = _utils.ULEB128.encode(len(request.SerializeToString()))
|
|
||||||
|
|
||||||
return len_bytes + request.SerializeToString()
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue