//////////////////////////////////////////////////////////////////////////
// sysinfo.cpp                                                          //
//                                                                      //
// Copyright (C)  2005  Lukas Tinkl <lukas.tinkl@suse.cz>               //
//                                                                      //
// 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.               //
//                                                                      //
// This program 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 program; if not, write to the Free Software          //
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA            //
// 02111-1307, USA.                                                     //
//////////////////////////////////////////////////////////////////////////

#include <config.h>

#ifdef HAVE_LONG_LONG
/* better to use long-long, because some 32bit-machines have more total
   memory (with swap) than just the 4GB which fits into a 32bit-long */
typedef unsigned long long t_memsize;
#else
typedef unsigned long t_memsize;
#endif

//#include <hd.h>

#include <qfile.h>
#include <qdir.h>

#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <sys/sysinfo.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <mntent.h>
#include <sys/vfs.h>
#include <string.h>
#include <sys/utsname.h>
#include <hal/libhal.h>

#include <kapplication.h>
#include <kcmdlineargs.h>
#include <kdebug.h>
#include <kinstance.h>
#include <kglobal.h>
#include <kstandarddirs.h>
#include <klocale.h>
#include <dcopref.h>
#include <kprocess.h>
#include <kmimetype.h>
#include <kiconloader.h>
#include <kdeversion.h>
#include <kuser.h>
#include <kglobalsettings.h>
#include <ktempfile.h>

#include "sysinfo.h"


using namespace KIO;
#define BR "<br>"

static QString formattedUnit( t_memsize value )
{
    if (value > (1024 * 1024))
        if (value > (1024 * 1024 * 1024))
            return i18n("%1 GB").arg(KGlobal::locale()->formatNumber(value / (1024 * 1024 * 1024.0), 2));
        else
            return i18n("%1 MB").arg(KGlobal::locale()->formatNumber(value / (1024 * 1024.0), 2));
    else
        return i18n("%1 KB").arg(KGlobal::locale()->formatNumber(value / 1024.0, 2));
}

kio_sysinfoProtocol::kio_sysinfoProtocol( const QCString & pool_socket, const QCString & app_socket )
    : SlaveBase( "kio_sysinfo", pool_socket, app_socket ), m_dcopClient( new DCOPClient() )
{
    if ( !m_dcopClient->isAttached() )
        m_dcopClient->attach();
}

kio_sysinfoProtocol::~kio_sysinfoProtocol()
{
    m_dcopClient->detach();
    delete m_dcopClient;
}

void kio_sysinfoProtocol::get( const KURL & /*url*/ )
{
    mimeType( "application/x-sysinfo" );

    // header
    QString location = locate( "data", "sysinfo/about/my-computer.html" );
    QFile f( location );
    f.open( IO_ReadOnly );
    QTextStream t( &f );
    QString content = t.read();
    content = content.arg( i18n( "My Computer" ) ); // <title>

    content = content.arg( "file:" + locate( "data", "sysinfo/about/suse10.1.css" ) );

    content = content.arg( i18n( "My Computer" ) ); // <h1>
    content = content.arg( i18n( "Folders, Harddisks, Removable Devices, System Information and more..." ) ); // catchphrase

    QString sysInfo = "<div id=\"column2\">"; // table with 2 cols
    QString dummy;

    // disk info
    infoMessage( i18n( "Looking for disk information..." ) );
    sysInfo += "<h2 id=\"hdds\">" + i18n( "Disk Information" ) + "</h2>";
    sysInfo += diskInfo();

    osInfo();
    sysInfo += "<h2 id=\"sysinfo\">" +i18n( "OS Information" ) + "</h2>";
    sysInfo += "<table>";
    sysInfo += "<tr><td>" + i18n( "OS:" ) + "</td><td>" + m_info[OS_SYSNAME] + " " + m_info[OS_RELEASE] + " " + m_info[OS_MACHINE] + "</td></tr>";
    sysInfo += "<tr><td>" + i18n( "Current user:" ) + "</td><td>" + m_info[OS_USER] + "@" + m_info[OS_HOSTNAME] + "</td></tr>";
    sysInfo += "<tr><td>" + i18n( "System:" ) +  "</td><td>" + m_info[OS_SYSTEM] + "</td></tr>";
    sysInfo += "<tr><td>" + i18n( "KDE:" ) + "</td><td>" + KDE::versionString() + "</td></tr>";
    sysInfo += "</table>";

    // OpenGL info
    if ( glInfo() )
    {
        sysInfo += "<h2 id=\"display\">" + i18n( "Display Info" ) + "</h2>";
        sysInfo += "<table>";
        sysInfo += "<tr><td>" + i18n( "Vendor:" ) + "</td><td>" + m_info[GFX_VENDOR] +  "</td></tr>";
        sysInfo += "<tr><td>" + i18n( "Model:" ) + "</td><td>" + m_info[GFX_MODEL] + "</td></tr>";
	if (!m_info[GFX_DRIVER].isNull())
	  sysInfo += "<tr><td>" + i18n( "Driver:" ) + "</td><td>" + m_info[GFX_DRIVER] + "</td></tr>";
        sysInfo += "</table>";
    }

    sysInfo += "</div><div id=\"column1\">"; // second column

    // OS info
    infoMessage( i18n( "Getting OS information...." ) );

    // common folders
    sysInfo += "<h2 id=\"dirs\">" + i18n( "Common Folders" ) + "</h2>";
    sysInfo += "<ul>";
    if ( KStandardDirs::exists( KGlobalSettings::documentPath() + "/" ) )
        sysInfo += QString( "<li><a href=\"file:%1\">" ).arg( KGlobalSettings::documentPath() ) + i18n( "My Documents" ) + "</a></li>";
    sysInfo += QString( "<li><a href=\"file:%1\">" ).arg( QDir::homeDirPath() ) + i18n( "My Home Folder" ) + "</a></li>";
    sysInfo += QString( "<li><a href=\"file:%1\">" ).arg( QDir::rootDirPath() ) + i18n( "Root Folder" ) + "</a></li>";
    sysInfo += "<li><a href=\"remote:/\">" + i18n( "Network Folders" ) + "</a></li>";
    sysInfo += "</ul>";

    // net info
    infoMessage( i18n( "Looking up network status..." ) );
    int state = netInfo();
    if (state > 1) { // assume no network manager / networkstatus
      sysInfo += "<h2 id=\"net\">" + i18n( "Network Status" ) + "</h2>";
      sysInfo += "<ul>";
      sysInfo += "<li>" + netStatus( state ) + "</li>";
      sysInfo += "</ul>";
    }

    // CPU info
    infoMessage( i18n( "Looking for CPU information..." ) );
    cpuInfo();
    if ( !m_info[CPU_MODEL].isNull() )
    {
        sysInfo += "<h2 id=\"cpu\">" + i18n( "CPU Information" ) + "</h2>";
        sysInfo += "<ul>";
        sysInfo += "<li><span class=\"label\">" + i18n( "Processor (CPU):" ) + "</span>" + m_info[CPU_MODEL] + "</li>";
        sysInfo += "<li><span class=\"label\">" + i18n( "Speed:" ) + "</span> " +
                   i18n( "%1 MHz" ).arg( KGlobal::locale()->formatNumber( m_info[CPU_SPEED].toFloat(), 2 ) ) + "</li>";
        sysInfo += "</ul>";
    }

    // memory info
    infoMessage( i18n( "Looking for memory information..." ) );
    memoryInfo();
    sysInfo += "<h2 id=\"memory\">" + i18n( "Memory Information" ) + "</h2>";
    sysInfo += "<table>";
    sysInfo += "<tr><td>" + i18n( "Total memory (RAM):" ) + "</td><td>" + m_info[MEM_TOTALRAM] + "</td></tr>";
    sysInfo += "<tr><td>" + i18n( "Free memory:" ) + "</td><td>" + m_info[MEM_FREERAM] + "</td></tr>";
    dummy = i18n( "Used Memory" );
    dummy = "<tr><td>" + i18n( "Total swap:" ) + "</td><td>" + m_info[MEM_TOTALSWAP] + "</td></tr>";
    dummy = "<tr><td>" + i18n( "Free swap:" ) + "</td><td>" + m_info[MEM_FREESWAP] + "</td></tr>";
    sysInfo += "</table>";

    sysInfo += "</div>";

    // Send the data
    content = content.arg( sysInfo ); // put the sysinfo text into the main box
    data( QCString( content.utf8() ) );
    data( QByteArray() ); // empty array means we're done sending the data
    finished();
}

void kio_sysinfoProtocol::mimetype( const KURL & /*url*/ )
{
    mimeType( "application/x-sysinfo" );
    finished();
}

static unsigned long int scan_one( const char* buff, const char *key )
{
    char *b = strstr( buff, key );
    if ( !b )
        return 0;
    unsigned long int val = 0;
    if ( sscanf( b + strlen( key ), ": %lu", &val ) != 1 )
        return 0;
    kdDebug(1242) << "scan_one " << key << " " << val << endl;
    return val;
}

static unsigned long int calculateFreeRam()
{
    FILE *fd = fopen( "/proc/meminfo", "rt" );
    if ( !fd )
        return 0;

    QTextIStream is( fd );
    QString MemInfoBuf = is.read();

    unsigned long int MemFree = scan_one( MemInfoBuf.latin1(), "MemFree" );
    unsigned long int Buffers = scan_one( MemInfoBuf.latin1(), "Buffers" );
    unsigned long int Cached  = scan_one( MemInfoBuf.latin1(), "Cached" );
    unsigned long int Slab    = scan_one( MemInfoBuf.latin1(), "Slab" );
    fclose( fd );

    MemFree += Cached + Buffers + Slab;
    if ( MemFree > 50 * 1024 )
        MemFree -= 50 * 1024;
    return MemFree;
}

void kio_sysinfoProtocol::memoryInfo()
{
    struct sysinfo info;
    int retval = sysinfo( &info );

    if ( retval !=-1 )
    {
        const int mem_unit = info.mem_unit;

        m_info[MEM_TOTALRAM] = formattedUnit( info.totalram * mem_unit );
        unsigned long int totalFree = calculateFreeRam() * 1024;
        kdDebug(1242) << "total " << totalFree << " free " << info.freeram << " unit " << mem_unit << endl;
        if ( totalFree > info.freeram * info.mem_unit || true )
            m_info[MEM_FREERAM] = i18n("%1 (+ %2 Caches)").arg(formattedUnit( info.freeram * mem_unit ))
                                  .arg( formattedUnit( totalFree - info.freeram * mem_unit ));
        else
            m_info[MEM_FREERAM] = formattedUnit( info.freeram * mem_unit );

        m_info[MEM_TOTALSWAP] = formattedUnit( info.totalswap * mem_unit );
        m_info[MEM_FREESWAP] = formattedUnit( info.freeswap * mem_unit );

        m_info[SYSTEM_UPTIME] = convertSeconds( info.uptime );
    }
}

void kio_sysinfoProtocol::cpuInfo()
{
    QString speed = readFromFile( "/proc/cpuinfo", "cpu MHz", ":" );

    if ( speed.isNull() )    // PPC?
        speed = readFromFile( "/proc/cpuinfo", "clock", ":" );

    if ( speed.endsWith( "MHz", false ) )
        speed = speed.left( speed.length() - 3 );

    m_info[CPU_SPEED] = speed;

    m_info[CPU_MODEL] = readFromFile( "/proc/cpuinfo", "model name", ":" );
    if ( m_info[CPU_MODEL].isNull() ) // PPC?
         m_info[CPU_MODEL] = readFromFile( "/proc/cpuinfo", "cpu", ":" );
}


QString kio_sysinfoProtocol::diskInfo()
{
    QString result = "<table><tr><th>" + i18n( "Device" ) + "</th><th>" + i18n( "Filesystem" ) + "</th><th>" +
                     i18n( "Total space" ) + "</th><th>" + i18n( "Available space" ) + "</th></tr>";

    if ( fillMediaDevices() )
    {
        for ( QValueList<DiskInfo>::ConstIterator it = m_devices.constBegin(); it != m_devices.constEnd(); ++it )
        {
            QString tooltip = i18n("Press the right mouse button for more options like Mount or Eject");

            DiskInfo di = ( *it );
            if ( di.mounted )
            {
                result += QString( "<tr><td>%1 <a href=\"media:/%2\" title=\"%7\">%3</a></td><td>%4</td><td>%5</td><td>%6</td></tr>" ).
                          arg( icon( di.iconName ) ).arg( di.name ).arg( di.label ).arg( di.fsType ).
                          arg( formattedUnit( di.total ) ).arg( formattedUnit( di.avail ) ).arg( tooltip );
            }
            else
            {
                result += QString( "<tr><td>%1 <a href=\"media:/%2\" title=\"%6\">%3</a></td><td>%4</td><td>%5</td><td></td></tr>" ).
                          arg( icon( di.iconName ) ).arg( di.name ).arg( di.label ).arg( di.fsType ).
                          arg( di.total ? formattedUnit( di.total) : QString::null).arg( tooltip );
            }
        }


    }
    else // fall back to fstab parsing
    {
        FILE * fp;
        if ( !( fp = setmntent( "/etc/fstab", "r" ) ) )
        {
            kdDebug(1242) << k_funcinfo << "Failed to open /etc/fstab" << endl;
            return QString::null;
        }

#define FS_NAME        mnt_ent->mnt_fsname     // device-name
#define FS_FILE        mnt_ent->mnt_dir        // mount-point
#define FS_TYPE        mnt_ent->mnt_type       // fs-type

        struct statfs sfs;
        struct mntent *mnt_ent;
        unsigned long long total, avail;

        while ( ( mnt_ent = getmntent( fp ) ) != NULL )
        {
            total = avail = 0;
            if ( statfs( FS_FILE, &sfs ) == 0 )
            {
                total = ( unsigned long long )sfs.f_blocks * sfs.f_bsize;
                if ( total > 0 && strcmp( FS_TYPE, "subdomainfs" ) != 0 ) // discard empty or otherwise strange devices
                {
                    avail = ( unsigned long long )( getuid() ? sfs.f_bavail : sfs.f_bfree ) * sfs.f_bsize;
                    int lastSlash = QString( FS_NAME ).findRev( '/' );
                    QString deviceName = lastSlash != -1 ? QString( FS_NAME ).mid( lastSlash + 1 ) : QString( FS_NAME );
                    //kdDebug(1242) << k_funcinfo << "Device name: " << deviceName << endl;
                    QString model = deviceName.left( deviceName.length() - 1 );
                    model = readFromFile( "/proc/ide/" + model + "/model" );
                    QString devIcon = icon( iconForDevice( deviceName ), KIcon::SizeSmall );
                    result += QString( "<tr><td>%1 <a href=\"file:%2\" title=\"%7\">%3</a></td><td>%4</td><td>%5</td><td>%6</td></tr>" )
                              .arg( devIcon ).arg( FS_FILE ).arg( FS_FILE ).arg( FS_TYPE ).arg( formattedUnit( total ) )
                              .arg( formattedUnit( avail ) ).arg( model );
                }
            }
        }
        endmntent( fp );
    }

    result += "</table>";
    return result;
}


int kio_sysinfoProtocol::netInfo() const
{
    // query kded.networkstatus.status(QString host)
    DCOPRef nsd( "kded", "networkstatus" );
    nsd.setDCOPClient( m_dcopClient );
    DCOPReply reply = nsd.call( "status", QString( "www.novell.com" ) );

    if ( reply.isValid() )
        return reply;

    kdDebug(1242) << k_funcinfo << "Reply is invalid" << endl;

    return 0;
}

#define HAVE_GLXCHOOSEVISUAL
#ifdef HAVE_GLXCHOOSEVISUAL
#include <GL/glx.h>
#endif

//-------------------------------------
bool hasDirectRendering ( QString &renderer ) {
    renderer = QString::null;

    Display *dpy = QApplication::desktop()->x11Display();

#ifdef HAVE_GLXCHOOSEVISUAL
    int attribSingle[] = {
        GLX_RGBA,
        GLX_RED_SIZE,   1,
        GLX_GREEN_SIZE, 1,
        GLX_BLUE_SIZE,  1,
        None
    };
    int attribDouble[] = {
      GLX_RGBA,
      GLX_RED_SIZE, 1,
      GLX_GREEN_SIZE, 1,
      GLX_BLUE_SIZE, 1,
      GLX_DOUBLEBUFFER,
      None
    };

    XVisualInfo* visinfo = glXChooseVisual (
        dpy, QApplication::desktop()->primaryScreen(), attribSingle
    );
    if (visinfo)
    {
        GLXContext ctx = glXCreateContext ( dpy, visinfo, NULL, True );
        if (glXIsDirect(dpy, ctx))
        {
            glXDestroyContext (dpy,ctx);
            return true;
        }

        XSetWindowAttributes attr;
        unsigned long mask;
        Window root;
        XVisualInfo *visinfo;
        int width = 100, height = 100;
        int scrnum = QApplication::desktop()->primaryScreen();

        root = RootWindow(dpy, scrnum);

        visinfo = glXChooseVisual(dpy, scrnum, attribSingle);
        if (!visinfo)
        {
            visinfo = glXChooseVisual(dpy, scrnum, attribDouble);
            if (!visinfo)
            {
                fprintf(stderr, "Error: couldn't find RGB GLX visual\n");
                return false;
            }
        }

        attr.background_pixel = 0;
        attr.border_pixel = 0;
        attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
        attr.event_mask = StructureNotifyMask | ExposureMask;
        mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;

        Window win = XCreateWindow(dpy, root, 0, 0, width, height,
                                   0, visinfo->depth, InputOutput,
                                   visinfo->visual, mask, &attr);

        if ( glXMakeCurrent(dpy, win, ctx))
            renderer = (const char *) glGetString(GL_RENDERER);
        XDestroyWindow(dpy, win);

        glXDestroyContext (dpy,ctx);
        return false;
    }
    else
    {
        return false;
    }
#else
#error no GL?
    return false;
#endif
}


bool kio_sysinfoProtocol::glInfo()
{
/*    static hd_data_t hd_data;
    static bool inited_hd = false;
    if ( !inited_hd )
    {
        memset(&hd_data, 0, sizeof(hd_data));
        inited_hd = true;
    }

    if (!hd_list(&hd_data, hw_display, 1, NULL))
        return false;

    hd_t* hd = hd_get_device_by_idx(&hd_data, hd_display_adapter(&hd_data));

    if (!hd)
        return false;

    driver_info_t *di = hd->driver_info;

    QString renderer;
    bool dri = hasDirectRendering( renderer );
    QString driver;

    for(di = di; di; di = di->next)
    {
        QString loadline;
	if (di->any.type == di_x11)
		driver = di->x11.server;
	else if (di->any.type == di_module && di->module.names)
		driver = di->module.names->str;
	loadline = QString("(II) LoadModule: \"%1\"").arg( driver );

        QFile file( "/var/log/Xorg.0.log" );
        if ( !file.exists() || !file.open( IO_ReadOnly ) )
        {
            di = 0;
            break;
        }

        QTextStream stream( &file );
        QString line;
        bool found_line = false;

        while ( !stream.atEnd() )
        {
            line = stream.readLine();
            if (line == loadline)
            {
                found_line = true;
                break;
            }
        }

        kdDebug(1242) << "found_line " << found_line << endl;
        if (found_line)
            break;
        else
	    driver = QString::null;
    }

    m_info[GFX_VENDOR] = hd->vendor.name;
    m_info[GFX_MODEL] = hd->device.name;
    if (!driver.isNull())
    {
        if (dri)
            m_info[GFX_DRIVER] = i18n("%1 (3D Support)").arg(driver);
        else
        {
            if ( renderer.contains( "Mesa GLX Indirect" ) )
                m_info[GFX_DRIVER] = i18n("%1 (No 3D Support)").arg(driver);
            else
                m_info[GFX_DRIVER] = driver;
        }
    }
    else
        m_info[GFX_DRIVER] = i18n( "unknown" );

    system( "eselect opengl show > /tmp/eselect_output" );
    system( "glxinfo | grep 'OpenGL renderer string' > /tmp/renderer" );
    m_info[GFX_MODEL] = readFromFile("/tmp/renderer" );
    m_info[GFX_DRIVER] = readFromFile("/tmp/eselect_output");*/
    return false;
}

QString kio_sysinfoProtocol::netStatus( int code ) const
{
    if ( code == 1 || code == 2 )
        return i18n( "Network is <strong>unreachable</strong>" );
    else if ( code == 3 || code == 4 || code == 6 )
        return i18n( "You are <strong>offline</strong>" );
    else if ( code == 5 )
        return i18n( "Network is <strong>shutting down</strong>" );
    else if ( code == 7 )
        return i18n( "<strong>Establishing</strong> connection to the network" );
    else if ( code == 8 )
        return i18n( "You are <strong>online</strong>" );

    return i18n( "Unknown network status" );
}

QString kio_sysinfoProtocol::readFromFile( const QString & filename, const QString & info, const char * sep ) const
{
    QFile file( filename );

    if ( !file.exists() || !file.open( IO_ReadOnly ) )
        return QString::null;

    QTextStream stream( &file );
    QString line;

    while ( !stream.atEnd() )
    {
        line = stream.readLine();
        if ( !line.isEmpty() )
        {
            if ( !sep )
                return line;
            if ( line.startsWith( info ) )
            {
                return line.section( sep, 1, 1 );
            }
        }
    }

    return QString::null;
}

QString kio_sysinfoProtocol::icon( const QString & name, int size ) const
{
    QString path = KGlobal::iconLoader()->iconPath( name, -size );
    return QString( "<img src=\"file:%1\" width=\"%2\" height=\"%3\" valign=\"bottom\"/>" ).arg( path ).arg( size ).arg( size );
}

QString kio_sysinfoProtocol::iconForDevice( const QString & name ) const
{
    DCOPRef nsd( "kded", "mediamanager" );
    nsd.setDCOPClient( m_dcopClient );
    QStringList result = nsd.call( "properties", name );

    if ( result.isEmpty() )
        return QString::null;

    KMimeType::Ptr mime = KMimeType::mimeType( result[10] );
    return mime->icon( QString::null, false );
}

void kio_sysinfoProtocol::osInfo()
{
    struct utsname uts;
    uname( &uts );
    m_info[ OS_SYSNAME ] = uts.sysname;
    m_info[ OS_RELEASE ] = uts.release;
    m_info[ OS_VERSION ] = uts.version;
    m_info[ OS_MACHINE ] = uts.machine;
    m_info[ OS_HOSTNAME ] = uts.nodename;

    m_info[ OS_USER ] = KUser().loginName();

    m_info[ OS_SYSTEM ] = readFromFile( "/etc/sabayon-release" );
}

static const KCmdLineOptions options[] =
{
        { "+protocol", "Protocol name", 0 },
        { "+pool", "Socket name", 0 },
        { "+app", "Socket name",  0 },
        KCmdLineLastOption
};

extern "C"
{
    int kdemain(int argc, char **argv)
    {
      // we need KApp to check the display capabilities
      putenv(strdup("SESSION_MANAGER="));
      KCmdLineArgs::init(argc, argv, "kio_sysinfo", 0, 0, 0, 0);
      KCmdLineArgs::addCmdLineOptions( options );
      KApplication app( false, true );

        kdDebug(1242) << "*** Starting kio_sysinfo " << endl;

        if (argc != 4) {
            kdDebug(1242) << "Usage: kio_sysinfo  protocol domain-socket1 domain-socket2" << endl;
            exit(-1);
        }

	KCmdLineArgs *args = KCmdLineArgs::parsedArgs();

        kio_sysinfoProtocol slave( args->arg(1), args->arg(2));
        slave.dispatchLoop();

        kdDebug(1242) << "*** kio_sysinfo Done" << endl;
        return 0;
    }
}

bool kio_sysinfoProtocol::fillMediaDevices()
{
    DCOPRef nsd( "kded", "mediamanager" );
    nsd.setDCOPClient( m_dcopClient );
    QStringList devices = nsd.call( "fullList" );
    if ( devices.isEmpty() )
        return false;

    kdDebug(1242) << devices << endl;

    m_devices.clear();

    LibHalContext  *m_halContext = libhal_ctx_new();
    if (!m_halContext)
      {
	kdDebug(1242) << "Failed to initialize HAL!" << endl;
        return false;
      }

    DBusError error;
    dbus_error_init(&error);
    DBusConnection *dbus_connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);

    if (dbus_error_is_set(&error)) {
      dbus_error_free(&error);
      libhal_ctx_free(m_halContext);
      m_halContext = 0;
    }

    if (m_halContext) {
      libhal_ctx_set_dbus_connection(m_halContext, dbus_connection);
      dbus_error_init(&error);
      if (!libhal_ctx_init(m_halContext, &error))
	{
	  printf("error %s %s\n", error.name, error.message);
	  if (dbus_error_is_set(&error))
	    dbus_error_free(&error);
	  libhal_ctx_free(m_halContext);
	  m_halContext = 0;
	}
    }

    for ( QStringList::ConstIterator it = devices.constBegin(); it != devices.constEnd(); ++it )
    {
        DiskInfo di;

        di.id = ( *it );
        di.name = *++it;
        di.label = *++it;
        di.userLabel = ( *++it );
        di.mountable = ( *++it == "true" ); // bool
        di.deviceNode = ( *++it );
        di.mountPoint = ( *++it );
        di.fsType = ( *++it );
        di.mounted = ( *++it == "true" ); // bool
        di.baseURL = ( *++it );
        di.mimeType = ( *++it );
        di.iconName = ( *++it );

        if ( di.iconName.isEmpty() ) // no user icon, query the MIME type
        {
            KMimeType::Ptr mime = KMimeType::mimeType( di.mimeType );
            di.iconName = mime->icon( QString::null, false );
        }

        di.total = di.avail = 0;

        // calc the free/total space
        struct statfs sfs;
        if ( di.mounted && statfs( QFile::encodeName( di.mountPoint ), &sfs ) == 0 )
        {
            di.total = ( unsigned long long )sfs.f_blocks * sfs.f_bsize;
            di.avail = ( unsigned long long )( getuid() ? sfs.f_bavail : sfs.f_bfree ) * sfs.f_bsize;
        } else if (m_halContext && di.id.startsWith("/org/freedesktop/Hal/" ) )
	{
	  dbus_error_init(&error);
	  di.total = libhal_device_get_property_uint64(m_halContext, di.id.latin1(), "volume.size", &error);
	  if (dbus_error_is_set(&error))
	    di.total = 0;
	}

        // guess the model
        QString ideName = di.name;
        ideName.truncate( 3 );
        di.model = readFromFile( "/proc/ide/" + ideName + "/model" );

        ++it; // skip separator

        m_devices.append( di );
    }

    libhal_ctx_free(m_halContext);

    return true;
}
