libimobiledevice / libimobiledevice

A cross-platform protocol library to communicate with iOS devices
https://libimobiledevice.org
GNU Lesser General Public License v2.1
6.68k stars 1.34k forks source link

How to sync contacts in newer devices? #1443

Closed AiXanadu closed 1 year ago

AiXanadu commented 1 year ago

I have an iPhone13 Pro (iOS16.4.1) device, I want to get the contact list in the device, how should I do it?

The ideviceinfo command shows the following: ideviceinfo.exe -q com.apple.mobile.tethered_sync -x

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>Contacts</key>
        <dict>
                <key>DisableTethered</key>
                <false/>
                <key>SyncingOS</key>
                <string>Windows</string>
        </dict>
</dict>
</plist>

I start by callingmobilesync_start:

auto        vAnchors = mobilesync_anchors_new(nullptr, xdm_device_contacts_current_native_anchors().data());
auto        vSyncType = MOBILESYNC_SYNC_TYPE_SLOW;
uint64_t    vDeviceDataClassVersion = 106;
char*       vErrorDescription = nullptr;
mobilesync_start(handle_mobile_sync, "com.apple.Contacts", vAnchors, 106, &vSyncType , &vDeviceDataClassVersion, &vErrorDescription);

then it sends:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
    <string>SDMessageSyncDataClassWithDevice</string>
    <string>com.apple.Contacts</string>
    <string>---</string>
    <string>2023-05-08 03-25-28 +0800</string>
    <integer>106</integer>
    <string>___EmptyParameterString___</string>
</array>
</plist>

device returned:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
    <string>DLMessageDisconnect</string>
    <string>Unknown Message Received</string>
</array>
</plist>

How can I synchronize this operation?

mexmer commented 1 year ago

it's missing version exchange, unless you filtered it out.

when you start mobilesync service you start with

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>DLMessageVersionExchange</string>
<integer>400</integer>
<integer>100</integer>
</array>
</plist>

and receive

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>DLMessageDeviceReady</string>
</array>
</plist>

then you start syncing

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>SDMessageSyncDataClassWithDevice</string>
<string>com.apple.Contacts</string>
<string>---</string>
<string>2023-05-09 17-32-06 +0100</string>
<integer>106</integer>
<string>___EmptyParameterString___</string>
</array>
</plist>

and response is like

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>SDMessageSyncDataClassWithComputer</string>
<string>com.apple.Contacts</string>
<string>2023-05-09 17-31-32 +0100</string>
<string>May 09 2023 17:32:06 +0200</string>
<string>SDSyncTypeSlow</string>
<integer>106</integer>
<string>___EmptyParameterString___</string>
</array>
</plist>

and then you continue

AiXanadu commented 1 year ago

I think it has switched versions, because the version swap function has been executed when the client is created. `LIBIMOBILEDEVICE_API mobilesync_error_t mobilesync_client_new(idevice_t device, lockdownd_service_descriptor_t service, mobilesync_client_t client) { if (!device || !service || service->port == 0 || !client || client) return MOBILESYNC_E_INVALID_ARG;

device_link_service_client_t dlclient = NULL;
mobilesync_error_t ret = mobilesync_error(device_link_service_client_new(device, service, &dlclient));
if (ret != MOBILESYNC_E_SUCCESS) {
    return ret;
}

mobilesync_client_t client_loc = (mobilesync_client_t) malloc(sizeof(struct mobilesync_client_private));
client_loc->parent = dlclient;
client_loc->direction = MOBILESYNC_SYNC_DIR_DEVICE_TO_COMPUTER;
client_loc->data_class = NULL;

/* perform handshake */
ret = mobilesync_error(device_link_service_version_exchange(dlclient, MSYNC_VERSION_INT1, MSYNC_VERSION_INT2));
if (ret != MOBILESYNC_E_SUCCESS) {
    debug_info("version exchange failed, error %d", ret);
    mobilesync_client_free(client_loc);
    return ret;
}

*client = client_loc;

return ret;

} ` Do I need to manually add the SSL connection function interface?

mexmer commented 1 year ago

service client connection is automatically set to ssl, if services start specifies it uses ssl, you don't need to to that explicitly.

and yes, you are right mobilesync_client_new sends the version exchange message

so if you go with functions (in mobilesync.h) proper order of functions should be mobilesync_client_new - create client mobilesync_anchors_new - create synchronization anchor mobilesync_start - start synchronization mobilesync_get_all_records_from_device - send command to receive all records mobilesync_receive_changes - receive record - this is called in cycle, pay attentio to is_last_record value mobilesync_finish

i don't use mobilesync function, since we have own implementation of sychronization and communicate with service directly.

AiXanadu commented 1 year ago

Maybe that's how it goes. But when I call mobilesync_start the data returned is as I said above. DLMessageDisconnect

mexmer commented 1 year ago

if you run it with debug output, do you see, that version exchange is processed?

or you might try just instead of mobilesync_client_new, try direct communication. i will try next week compile simple tool using mobilesync functions.

i'm testing on my ipad, which has 16.4.1(a) and iphone, also 16.4.1(a), and have no issues, but as i said, it's my own code, although it sends in most cases same stuff, as libimobile does. it's reverse engineered from usb capture.

mexmer commented 1 year ago

hey, i just made simple code to retrieve contacts using mobilesync function, running on io 16.4.1(a) code will need some polishing, but i can retrieve contacts from internal memory without issues with it. it was compiled on windows, but should work on linux too.

#include <iostream>
#include "libimobiledevice/mobilesync.h"
#include "libimobiledevice/libimobiledevice.h"
#include <sys/timeb.h>

int main()
{
    idevice_set_debug_level(1);
    idevice_t device = nullptr;
    auto ret = idevice_new(&device, "00008030-000115A202F9802E");
    if (ret == IDEVICE_E_SUCCESS && idevice_new)
    {
        lockdownd_client_t client = nullptr;
        auto lret = lockdownd_client_new_with_handshake(device, &client, "synctest");
        if (lret == LOCKDOWN_E_SUCCESS && client)
        {
            plist_t val = nullptr;
            auto vret = lockdownd_get_value(client, "com.apple.mobile.tethered_sync", nullptr, &val);
            if (vret == LOCKDOWN_E_SUCCESS && val)
            {
                char* plistbuf = nullptr;
                uint32_t plistlen = 0;
                auto pret = plist_to_xml(val, &plistbuf, &plistlen);
                if (pret == PLIST_ERR_SUCCESS && plistlen)
                {
                    printf(plistbuf);
                    free(plistbuf);
                }
                plist_free(val);
            }
            lockdownd_client_free(client);
        }
        mobilesync_client_t syncclient = nullptr;
        auto mret = mobilesync_client_start_service(device, &syncclient, "synctest");
        if (mret == MOBILESYNC_E_SUCCESS && syncclient)
        {
            char anchor[200];
            __time64_t ltime;
            struct tm* ltm;
            _time64(&ltime);
            ltm = _localtime64(&ltime);
            strftime(anchor, 200 - 1, "%Y-%m-%d %H-%M-%S", ltm);

            char buffertz[20];
            struct __timeb64 ltmz;
            _ftime64(&ltmz);
            sprintf(buffertz, " %c%02d%02d", ltmz.timezone > 0 ? '-' : '+', abs(ltmz.timezone / 60), abs(ltmz.timezone % 60));
            strcat(anchor, buffertz);

            mobilesync_sync_type_t synctype = MOBILESYNC_SYNC_TYPE_SLOW;
            uint64_t syncversion = 0;
            char* errormsg = nullptr;
            auto anchors = mobilesync_anchors_new(nullptr, anchor);
            if ((mret = mobilesync_start(syncclient, "com.apple.Contacts", anchors, 106, &synctype, &syncversion, &errormsg)) == MOBILESYNC_E_SUCCESS)
            {
                if (mobilesync_get_all_records_from_device(syncclient) == MOBILESYNC_E_SUCCESS)
                {
                    plist_t entities = nullptr;
                    uint8_t islast = 0;
                    while (mobilesync_receive_changes(syncclient, &entities, &islast, nullptr) == MOBILESYNC_E_SUCCESS)
                    {
                        if (entities)
                        {
                            char* plistbuf = nullptr;
                            uint32_t plistlen = 0;
                            auto pret = plist_to_xml(entities, &plistbuf, &plistlen);
                            if (pret == PLIST_ERR_SUCCESS && plistlen)
                            {
                                printf(plistbuf);
                                free(plistbuf);
                            }
                            plist_free(entities);
                        }
                        mobilesync_acknowledge_changes_from_device(syncclient);
                        if (islast)
                            break;
                    }
                }
            }
            else
                printf("error %d, %s", mret, errormsg);
            mobilesync_anchors_free(anchors);
            mobilesync_client_free(syncclient);
        }
        idevice_free(device);
    }
}
AiXanadu commented 1 year ago

@mexmer Thank you very much, I used your code and successfully read the contacts!

mexmer commented 1 year ago

i think there should be disconnect message, or cancel at end, and other stuff, to avoid leaving phone in udesired state. so take it just as concept.