/*
 *  Copyright (C) 2004 Girish Ramakrishnan All Rights Reserved.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 * USA.
 */

// $Id: tdedocker.cpp,v 1.24 2005/02/04 10:25:46 cs19713 Exp $

#include <tqdir.h>
#include <tqfile.h>
#include <tqtextcodec.h>
#include <tqtextstream.h>
#include <tqstring.h>

#include <tdecmdlineargs.h>
#include <tdeconfig.h>
#include <tdelocale.h>

#include "trace.h"
#include "traylabelmgr.h"
#include "tdedocker.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define TMPFILE_PREFIX TQString("/tmp/tdedocker.")

TDEDocker::TDEDocker()
  : TDEApplication(), mTrayLabelMgr(NULL)
{
  // Set ourselves up to be called from the application loop
  connect(&mInitTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(doInit()));
  mInitTimer.start(0, true);

  // Required so that the saved config is correctly loaded
  // (see TrayLabelMgr::doRestoreSession() for usage)
  if (TDEApplication::kApplication()->isRestored())
  {
    TDEApplication::kApplication()->sessionConfig();
  }
}

void TDEDocker::doInit()
{
  INIT_TRACE();
  /*
   * Detect and transfer control to previous instance (if one exists)
   * _TDEDOCKER_RUNNING is a X Selection. We start out by trying to locate the
   * selection owner. If someone else owns it, transfer control to that
   * instance of TDEDocker
   */
  Display *display = TQPaintDevice::x11AppDisplay();
  Atom tdedocker = XInternAtom(display, "_TDEDOCKER_RUNNING", False);
  Window prev_instance = XGetSelectionOwner(display, tdedocker);

  if (prev_instance == None)
  {
    mSelectionOwner = XCreateSimpleWindow(display, tqt_xrootwin(), 1, 1, 1, 1, 1, 1, 1);
    XSetSelectionOwner(display, tdedocker, mSelectionOwner, CurrentTime);
    TRACE("Selection owner set to 0x%x", (unsigned) mSelectionOwner);
    mTrayLabelMgr = TrayLabelMgr::instance();
  }
  else
    notifyPreviousInstance(prev_instance);  // does not return
}

void TDEDocker::notifyPreviousInstance(Window prevInstance)
{
  Display *display = TQPaintDevice::x11AppDisplay();

  TRACE("Notifying previous instance [%x]", (unsigned) prevInstance);

  // Dump all arguments in temporary file
  TQFile f(TMPFILE_PREFIX + TQString().setNum(getpid()));
  if (!f.open(IO_WriteOnly)) return;
  TQTextStream s(&f);

  // Its normal to use TDEDocker in startup scripts. We could be getting restored
  // from a session at the same time, so in such case pass along the info.
  if (isRestored())
  {
    s << TDECmdLineArgs::appName() << " --restore-internal";
  }
  else
  {
    TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();
    s << TDECmdLineArgs::appName();

    // Options
    if (args->isSet("b"))
    {
      s << " -b";
    }
    if (args->isSet("d"))
    {
      s << " -d";
    }
    if (args->isSet("e"))
    {
      s << " -e";
    }
    if (args->isSet("f"))
    {
      s << " -f";
    }
    if (args->isSet("i"))
    {
      s << " -i " << args->getOption("i");
    }
    if (args->isSet("m"))
    {
      s << " -m";
    }
    if (args->isSet("o"))
    {
      s << " -o";
    }
    if (args->isSet("p"))
    {
      s << " -p " << args->getOption("p");
    }
    if (args->isSet("q"))
    {
      s << " -q";
    }
    if (args->isSet("t"))
    {
      s << " -t";
    }
    if (args->isSet("w"))
    {
      s << " -w " << args->getOption("w");
    }

    // Arguments
    for (int i = 0; i < args->count(); i++)
    {
      s << " " << args->arg(i);
    }
  }
  f.close();

  /*
   * Now tell our previous instance that we came to pass. Actually, it can
   * figure it out itself using PropertyNotify events but this is a lot nicer
   */
  XClientMessageEvent dock_event;
  memset(&dock_event, 0, sizeof(XClientMessageEvent));
  dock_event.display = display;
  dock_event.window = prevInstance;
  dock_event.send_event = True;
  dock_event.type = ClientMessage;
  dock_event.message_type = 0x220679; // it all started this day
  dock_event.format = 8;
  dock_event.data.l[0] = 0xBABE;  // love letter ;)
  dock_event.data.l[1] = getpid();
  XSendEvent(display, prevInstance, False, 0, (XEvent *) &dock_event);
  XSync(display, False);

  quit();
}

/*
 * The X11 Event filter called by TQt. Look out for ClientMessage events from
 * our new instance
 */
bool TDEDocker::x11EventFilter(XEvent * event)
{
  if (event->type == ClientMessage)
  {
    // look for requests from a new instance of tdedocker
    XClientMessageEvent *client = (XClientMessageEvent *) event;
    if (!(client->message_type == 0x220679 && client->data.l[0] == 0xBABE))
      return false;

    TRACE("ClientMessage from PID=%ld. SelOwn=0x%x",
          client->data.l[1], (unsigned) mSelectionOwner);
    char tmp[50];
    struct stat buf;
    sprintf(tmp, TQString(TMPFILE_PREFIX+"%ld").local8Bit(), client->data.l[1]);
    if (stat(tmp, &buf) || (getuid()!=buf.st_uid))
    {
     /*
      * We make sure that the owner of this process and the owner of the file
      * are the same. This will prevent someone from executing arbitrary
      * programs by sending client message. Of course, you can send a message
      * only if you are authenticated to the X session and have permission to
      * create files in TMPFILE_PREFIX. So this code is there just for the
      * heck of it.
      */
      TRACE("User %i is trying something fishy...", buf.st_uid);
      unlink(tmp);
      return true;
    }
    TQFile f(tmp);
    if (!f.open(IO_ReadOnly)) return true;
    TQTextStream s(&f);
    TQStringList argv;
    while (!s.atEnd()) { TQString x; s >> x; argv += x; }
    f.close();
    unlink(tmp); // delete the tmp file
    mTrayLabelMgr->processCommand(argv);
    return true;
  }
  else
  {
    if (mTrayLabelMgr->x11EventFilter(event))
    {
      return true;
    }
    return TDEApplication::x11EventFilter(event);
  }
}

#include "tdedocker.moc"
