#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>


// Barebones purple_console clone - constructed with some reverse and forward engineering.
//
// No copyright or license. Feel free to share. Comments/Feedback welcome @http://newosxbook.com/forum/
// 
//
// To compile: gcc jurpleConsole.c -o jc -framework CoreFoundation -framework MobileDevice
//
// You will need to make MobileDevice.framework "public" - simplest way is to copy it (-pR)
// to Frameworks/ from PrivateFrameworks

#ifdef DEBUG
#define Dprintf fprintf
#else
#define Dprintf(...)
#endif

// These are obviously internal to Apple. The void * is likely some AMDDeviceRef * or something like that
// But since it's opaque anyway, just void * it..

extern int AMDeviceConnect (void *device);
extern int AMDeviceValidatePairing (void *device);
extern int AMDeviceStartSession (void *device);
extern int AMDeviceStopSession (void *device);
extern int AMDeviceDisconnect (void *device);

extern int AMDServiceConnectionReceive(void *device, char *buf,int size, int );

extern int AMDeviceSecureStartService(void *device,
                                CFStringRef serviceName, // One of the services in lockdown's Services.plist
                                int  flagsButZeroIsFineAlso,
				void *handle);

extern int AMDeviceNotificationSubscribe(void *, int , int , int, void **);

struct AMDeviceNotificationCallbackInformation {
	void 		*deviceHandle;
	uint32_t	msgType;
} ;


#define  BUFSIZE 128 // This is used by purpleConsole. So I figure I'll use it too.

void doDeviceConnect(void *deviceHandle)
{
 	int rc = AMDeviceConnect(deviceHandle);
	
	int fd;
	char buf[BUFSIZE]; 
		       

	if (rc) {
		fprintf (stderr, "AMDeviceConnect returned: %d\n", rc);
		exit(1);
	}

	rc = AMDeviceValidatePairing(deviceHandle);

	if (rc)
		{
		fprintf (stderr, "AMDeviceValidatePairing() returned: %d\n", rc);
		exit(2);

	}
	 
	rc = AMDeviceStartSession(deviceHandle);
	if (rc)
		{
		fprintf (stderr, "AMStartSession() returned: %d\n", rc);
		exit(2);

	}

	void *f;
	rc = AMDeviceSecureStartService(deviceHandle,
				  CFSTR("com.apple.syslog_relay"), // Any of the services in lockdown's services.plist
				  0,
				  &f);

	if (rc != 0)
	{

		fprintf(stderr, "Unable to start service -- Rc %d fd: %p\n", rc, f);
		exit(4);
	
	}


 	rc = AMDeviceStopSession(deviceHandle);
	if (rc != 0)
	{
		fprintf(stderr, "Unable to disconnect - rc is %d\n",rc);
		exit(4);

	}

 	rc = AMDeviceDisconnect(deviceHandle);


	if (rc != 0)
	{
		fprintf(stderr, "Unable to disconnect - rc is %d\n",rc);
		exit(5);
	}



	memset (buf, '\0', 128); // technically, buf[0] = '\0' is enough

	while ((rc = AMDServiceConnectionReceive(f,
					 buf,
					 BUFSIZE, 0) > 0))

	{
		// Messages are read to the buf (Framework uses a socket descriptor)
		// For syslog_relay, messages are just the raw ASL output (including
		// kernel.debug), which makes it easy for this sample code.
		// Other services use bplists, which would call for those nasty
		// CFDictionary and other APIs..
		
		printf("%s", buf);
		fflush(NULL);
		memset (buf, '\0', 128);
	
	}


} // end doDevice


void callback(struct AMDeviceNotificationCallbackInformation *CallbackInfo)
{


	void *deviceHandle = CallbackInfo->deviceHandle;
	Dprintf (stderr,"In callback, msgType is %d\n", CallbackInfo->msgType);

	switch (CallbackInfo->msgType)
	{

		case 1:
			Dprintf(stderr, "Device %p connected\n", deviceHandle);
			doDeviceConnect(deviceHandle);
			break;
		case 2:
			Dprintf(stderr, "Device %p disconnected\n", deviceHandle);
			break;
		case 3:
			Dprintf(stderr, "Unsubscribed\n");
			break;

		default:
			Dprintf(stderr, "Unknown message %d\n", CallbackInfo->msgType);
	}

}



int main (int argc, char **argv)
{

    void *subscribe;

    int rc = AMDeviceNotificationSubscribe(callback, 0,0,0, &subscribe);
    if (rc <0) {
		fprintf(stderr, "Unable to subscribe: AMDeviceNotificationSubscribe returned %d\n", rc);
		exit(1);
	}

 
   CFRunLoopRun();



}