//-*-c++-*-
/***************************************************************************
 *   Copyright (C) 2003 by Fred Schaettgen                                 *
 *   kbluetoothd@schaettgen.de                                             *
 *                                                                         *
 *   This program 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.                                   *
 ***************************************************************************/

#include <pwd.h>
#include <kapplication.h>
#include <kaboutdata.h>
#include <kcmdlineargs.h>
#include <kpassdlg.h>
#include <kdialogbase.h>
#include <klocale.h>
#include <kuser.h>
#include <kdebug.h>
#include <dcopclient.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qdir.h>
#include <qregexp.h>
#include <stdlib.h>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include "../../libkbluetooth/configinfo.h"

static const char description[] =
    I18N_NOOP("A KDE KPart Application");

static const char* version = KDEBluetoothConfig::version;

static KCmdLineOptions options[] =
{
    { "+dir", I18N_NOOP("Direction (in|out)"), 0},
    { "+addr", I18N_NOOP("Remote Bluetooth address"), 0},
    { "+[name]", I18N_NOOP("Name of the remote device"), 0},
    KCmdLineLastOption
};

KCmdLineArgs *cmdLineArgs;

QStringList getUsers()
{
    char buf[80];
    char *username = NULL;
    QStringList result;

    FILE *fs = popen("who", "r");
    while (fgets(buf, 80, fs) != NULL) {
	username = strdup(QString(buf).section(QRegExp("\\s+"), 0, 1).latin1());

	if (!result.contains(username))
		result.append(username);

    	free(username);
    }
	
    if (ferror(fs))
	    perror("fread");

    pclose(fs);

    return result;
}

QString showPinDialog(QString user,
    bool bIn, QString addr, QString name)
{
    QString result;
    QByteArray argData, replyData;
    QCString replyType;
    QDataStream arg(argData, IO_WriteOnly);

    KUser u(user.section(' ',0,0) );
    int uid = u.uid();
    int gid = u.gid();
    QString home = u.homeDir();
    QString homeEnv = QString("HOME=%1").arg(home);

    if (uid < 0 || gid < 0 || home.isEmpty())
        return QString::null;

    int fds[2];

    if (pipe(fds)) {
        perror("pipe");
        return QString::null;
    }

    pid_t pid = fork();
    if (pid < 0) {
        perror("fork()");
        return QString::null;
    }

    if (pid > 0) {
        close(fds[1]);

        int status = 0;
        while ( waitpid( pid, &status, 0 ) < 0 && errno == EINTR )
            ;

        if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
            QCString cstr(4096);
            int r = ::read(fds[0], cstr.data(), 4095);
            cstr.truncate(r >= 0 ? r : 0);
            return QString::fromUtf8(cstr);
        }
        return QString::null;
    }
    close(fds[0]);
    // become to user for ICEauthority access
    if (setgid(gid)) {
        kdDebug() << "setgid failed for gid: " << gid << endl;
        perror("setgid");
        _exit(254);
    }

    if (setuid(uid)) {
        kdDebug() << "setuid failed for uid: " << uid << endl;
        perror("setuid");
        _exit(254);
    }

    char *tmpHome = strdup(QFile::encodeName(homeEnv).data());
    putenv(tmpHome);

    DCOPClient *client = new DCOPClient();
    if (client->attach() == false) {
        kdDebug() << "DCOPClient::attach failed" << endl;
        goto error;
    }
    arg << bIn << addr << name;

    if (client->call( "kbluetoothd", "PinServer",
                "pinDialog(bool, QString, QString)", argData, replyType, replyData))
    {
        QDataStream reply(replyData, IO_ReadOnly);

        if ( replyType != "QString")
        {
            kdWarning() << "unexpected result " << replyType.data() << endl;
	    goto error;
        }
        reply >> result;
	goto end;
    }
    else {
	goto error;
    }

error:
    result = QString::null;

end:
    QCString cstr;
    cstr = result.utf8();
    int r = ::write(fds[1], cstr.data(), cstr.length());
    _exit(0);
}

QString tryAllSessions(bool bIn, QString addr, QString name)
{
    QStringList userList = getUsers();
    if(userList.isEmpty()) userList+="root";
    for (QStringList::iterator hIt = userList.begin(); hIt != userList.end(); ++hIt) {
        QString user = *hIt;
	QString ret = showPinDialog(user, bIn, addr, name);
	if (ret.length() > 0) 
		return ret;
    }
    return "";
}


int main(int argc, char **argv)
{
    KAboutData about("kbluepin", I18N_NOOP("kbluepin"), version, description,
                     KAboutData::License_GPL, "(C) 2003 Fred Schaettgen", 0, 0,
                     "kbluepin@schaettgen.de");
    about.addAuthor( "Fred Schaettgen,,,", 0, "fred@arrajadoa" );
    KCmdLineArgs::init(argc, argv, &about);
    KCmdLineArgs::addCmdLineOptions( options );

    // no session.. just start up normally
    cmdLineArgs = KCmdLineArgs::parsedArgs();

    QString dir = "out";
    QString addr = "<unknown>";
    QString name = "<unknown>";
    if (cmdLineArgs->count() > 0) dir = cmdLineArgs->arg(0);
    if (cmdLineArgs->count() > 1) addr = cmdLineArgs->arg(1);
    if (cmdLineArgs->count() > 2) name = cmdLineArgs->arg(2);

    bool bIncoming = (dir != QString("out"));
    kdDebug() << "Dir: " << dir << endl;
    kdDebug() << "Addr: " << addr << endl;
    kdDebug() << "Name: " << name << endl;

    QString ret = tryAllSessions(bIncoming, addr, name);
    if (ret.length() > 0) {
        // This *must* go to stdout, since this
        // will be parsed by Bluez's hcid, which is
        // calling kbluepin.
        std::cout << "PIN:" << ret.utf8() << std::endl;
        return 0;
    }
    else {
        // This *must* go to stdout, since this
        // will be parsed by Bluez's hcid, which is
        // calling kbluepin.
        std::cout << "ERR" << std::endl;
        return 1;
    }
}
