//gcc -Wall -ggdb skypiax_auth.c -o skypiax_auth -lX11
#include <stdio.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#include <X11/Xatom.h>

struct SkypiaxHandles {
  Window skype_win;
  Display *disp;
  Window win;
  int api_connected;
  int fdesc[2];
};

XErrorHandler old_handler = 0;
int xerror = 0;
char *dispname;

int X11_errors_handler(Display * dpy, XErrorEvent * err)
{
  (void) dpy;

  xerror = err->error_code;
  printf("\n\nReceived error code %d from X Server on display '%s'\n\n", xerror,
         dispname);
  return 0;                     /*  ignore the error */
}

static void X11_errors_trap(void)
{
  xerror = 0;
  old_handler = XSetErrorHandler(X11_errors_handler);
}

static int X11_errors_untrap(void)
{
  XSetErrorHandler(old_handler);
  return (xerror != BadValue) && (xerror != BadWindow);
}

int skypiax_send_message(struct SkypiaxHandles *SkypiaxHandles, const char *message_P)
{

  Window w_P;
  Display *disp;
  Window handle_P;
  int ok;

  w_P = SkypiaxHandles->skype_win;
  disp = SkypiaxHandles->disp;
  handle_P = SkypiaxHandles->win;

  Atom atom1 = XInternAtom(disp, "SKYPECONTROLAPI_MESSAGE_BEGIN", False);
  Atom atom2 = XInternAtom(disp, "SKYPECONTROLAPI_MESSAGE", False);
  unsigned int pos = 0;
  unsigned int len = strlen(message_P);
  XEvent e;

  memset(&e, 0, sizeof(e));
  e.xclient.type = ClientMessage;
  e.xclient.message_type = atom1;   /*  leading message */
  e.xclient.display = disp;
  e.xclient.window = handle_P;
  e.xclient.format = 8;

  X11_errors_trap();
  do {
    unsigned int i;
    for (i = 0; i < 20 && i + pos <= len; ++i)
      e.xclient.data.b[i] = message_P[i + pos];
    XSendEvent(disp, w_P, False, 0, &e);

    e.xclient.message_type = atom2; /*  following messages */
    pos += i;
  } while (pos <= len);

  XSync(disp, False);
  ok = X11_errors_untrap();

  if (!ok)
    printf("Sending message failed with status %d\n", xerror);

  return 1;
}

int skypiax_present(struct SkypiaxHandles *SkypiaxHandles)
{
  Atom skype_inst = XInternAtom(SkypiaxHandles->disp, "_SKYPE_INSTANCE", True);

  Atom type_ret;
  int format_ret;
  unsigned long nitems_ret;
  unsigned long bytes_after_ret;
  unsigned char *prop;
  int status;

  X11_errors_trap();
  status =
    XGetWindowProperty(SkypiaxHandles->disp, DefaultRootWindow(SkypiaxHandles->disp),
                       skype_inst, 0, 1, False, XA_WINDOW, &type_ret, &format_ret,
                       &nitems_ret, &bytes_after_ret, &prop);

  X11_errors_untrap();
  /*  sanity check */
  if (status != Success || format_ret != 32 || nitems_ret != 1) {
    SkypiaxHandles->skype_win = (Window) - 1;
    printf("Skype instance not found on display '%s'\n", dispname);
    return 0;
  }

  SkypiaxHandles->skype_win = *(const unsigned long *) prop & 0xffffffff;
  printf("Skype instance found on display '%s', with id #%d\n", dispname,
         (unsigned int) SkypiaxHandles->skype_win);
  return 1;
}

void skypiax_clean_disp(void *data)
{

  int *dispptr;
  int disp;

  dispptr = data;
  disp = *dispptr;

  if (disp) {
    close(disp);
  } else {
  }
  usleep(1000);
}

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

  struct SkypiaxHandles SkypiaxHandles;
  char buf[512];
  Display *disp = NULL;
  Window root = -1;
  Window win = -1;

  if (argc == 2)
    dispname = argv[1];
  else
    dispname = ":0.0";

  disp = XOpenDisplay(dispname);
  if (!disp) {
    printf("Cannot open X Display '%s', exiting\n", dispname);
    return -1;
  }

  int xfd;
  xfd = XConnectionNumber(disp);

  SkypiaxHandles.disp = disp;

  if (skypiax_present(&SkypiaxHandles)) {
    root = DefaultRootWindow(disp);
    win =
      XCreateSimpleWindow(disp, root, 0, 0, 1, 1, 0,
                          BlackPixel(disp, DefaultScreen(disp)), BlackPixel(disp,
                                                                            DefaultScreen
                                                                            (disp)));

    SkypiaxHandles.win = win;

    snprintf(buf, 512, "NAME skypiax");

    if (!skypiax_send_message(&SkypiaxHandles, buf)) {
      printf
        ("Sending message failed - probably Skype crashed. Please run/restart Skype manually and launch skypiax_auth again\n");
      return -1;
    }

    snprintf(buf, 512, "PROTOCOL 6");
    if (!skypiax_send_message(&SkypiaxHandles, buf)) {
      printf
        ("Sending message failed - probably Skype crashed. Please run/restart Skype manually and launch skypiax_auth again\n");
      return -1;
    }

    /* perform an events loop */
    XEvent an_event;
    char buf[21];               /*  can't be longer */
    char buffer[17000];
    char *b;
    int i;

    b = buffer;

    while (1) {
      XNextEvent(disp, &an_event);
      switch (an_event.type) {
      case ClientMessage:

        if (an_event.xclient.format != 8)
          break;

        for (i = 0; i < 20 && an_event.xclient.data.b[i] != '\0'; ++i)
          buf[i] = an_event.xclient.data.b[i];

        buf[i] = '\0';

        strcat(buffer, buf);

        if (i < 20) {           /* last fragment */
          unsigned int howmany;

          howmany = strlen(b) + 1;

          printf("RECEIVED==> %s\n", b);
          memset(buffer, '\0', 17000);
        }

        break;
      default:
        break;
      }
    }
  } else {
    printf
      ("Skype client not found on display '%s'. Please run/restart Skype manually and launch skypiax_auth again\n\n\n",
       dispname);
    return -1;
  }
  return 0;

}
