You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
562 lines
20 KiB
562 lines
20 KiB
/***************************************************************************
|
|
kio_mac.cpp
|
|
-------------------
|
|
copyright : (C) 2002 Jonathan Riddell
|
|
email : jr@jriddell.org
|
|
version : 1.0.1
|
|
release date : 19 July 2002
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* 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. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#define PARTITION "/dev/hda11"
|
|
|
|
#include <kinstance.h>
|
|
#include <kdebug.h>
|
|
#include <klocale.h>
|
|
#include <kconfig.h>
|
|
#include <tqstring.h>
|
|
#include <tqregexp.h>
|
|
|
|
#include <sys/stat.h>
|
|
#include <stdlib.h>
|
|
#include <iostream>
|
|
#include <time.h>
|
|
|
|
#include "kio_mac.moc"
|
|
|
|
using namespace KIO;
|
|
|
|
extern "C" {
|
|
int KDE_EXPORT kdemain(int, char **argv) {
|
|
KInstance instance("kio_mac");
|
|
MacProtocol slave(argv[2], argv[3]);
|
|
slave.dispatchLoop();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
MacProtocol::MacProtocol(const TQCString &pool, const TQCString &app)
|
|
: TQObject(), SlaveBase("mac", pool, app) {
|
|
/* logFile = new TQFile("/home/jr/logfile");
|
|
logFile->open(IO_ReadWrite | IO_Append);
|
|
logStream = new TQTextStream(logFile);
|
|
*logStream << "Start Macprotocol()" << endl;
|
|
*/
|
|
}
|
|
|
|
MacProtocol::~MacProtocol() {
|
|
/* *logStream << "destructor ~MacProtocol()" << endl;
|
|
logFile->close();
|
|
delete logFile;
|
|
logFile = 0;
|
|
delete logStream;
|
|
logStream = 0;
|
|
*/
|
|
delete myKProcess;
|
|
myKProcess = 0L;
|
|
}
|
|
|
|
//get() called when a file is to be read
|
|
void MacProtocol::get(const KURL& url) {
|
|
TQString path = prepareHP(url); //mount and change to correct directory - return the filename
|
|
TQString query = url.query();
|
|
TQString mode("-");
|
|
TQString mime;
|
|
processedBytes = 0;
|
|
|
|
//Find out the size and if it's a text file
|
|
UDSEntry entry = doStat(url);
|
|
UDSEntry::Iterator it;
|
|
for(it = entry.begin(); it != entry.end(); ++it) {
|
|
if ((*it).m_uds == KIO::UDS_MIME_TYPE) {
|
|
mime = (*it).m_str;
|
|
}
|
|
if ((*it).m_uds == KIO::UDS_SIZE) {
|
|
totalSize((*it).m_long);
|
|
}
|
|
}
|
|
|
|
//find out if a mode has been specified in the query e.g. ?mode=t
|
|
//or if it's a text file then set the mode to text
|
|
int modepos = query.tqfind("mode=");
|
|
int textpos = mime.tqfind("text");
|
|
if (modepos != -1) {
|
|
mode += query.mid(modepos + 5, 1);
|
|
if (mode != "-r" && mode != "-b" && mode != "-m" && mode != "-t" && mode != "-a") {
|
|
error(ERR_SLAVE_DEFINED, i18n("Unknown mode"));
|
|
}
|
|
} else if (textpos != -1) {
|
|
mode += "t";
|
|
} else {
|
|
mode += "r";
|
|
}
|
|
|
|
//now we can read the file
|
|
myKProcess = new KProcess();
|
|
|
|
*myKProcess << "hpcopy" << mode << path << "-";
|
|
|
|
//data is now sent directly from the slot
|
|
connect(myKProcess, TQT_SIGNAL(receivedStdout(KProcess *, char *, int)),
|
|
this, TQT_SLOT(slotSetDataStdOutput(KProcess *, char *, int)));
|
|
|
|
myKProcess->start(KProcess::Block, KProcess::All);
|
|
|
|
if (!myKProcess->normalExit() || !(myKProcess->exitStatus() == 0)) {
|
|
error(ERR_SLAVE_DEFINED,
|
|
i18n("There was an error with hpcopy - please ensure it is installed"));
|
|
return;
|
|
}
|
|
|
|
//clean up
|
|
delete myKProcess; myKProcess = 0;
|
|
//finish
|
|
data(TQByteArray());
|
|
finished();
|
|
}
|
|
|
|
//listDir() called when the user is looking at a directory
|
|
void MacProtocol::listDir(const KURL& url) {
|
|
TQString filename = prepareHP(url);
|
|
|
|
if (filename.isNull()) {
|
|
error(ERR_CANNOT_LAUNCH_PROCESS, i18n("No filename was found"));
|
|
} else {
|
|
myKProcess = new KProcess();
|
|
*myKProcess << "hpls" << "-la" << filename;
|
|
|
|
standardOutputStream = TQString::null;
|
|
connect(myKProcess, TQT_SIGNAL(receivedStdout(KProcess *, char *, int)),
|
|
this, TQT_SLOT(slotGetStdOutput(KProcess *, char *, int)));
|
|
|
|
myKProcess->start(KProcess::Block, KProcess::All);
|
|
|
|
if ((!myKProcess->normalExit()) || (!myKProcess->exitStatus() == 0)) {
|
|
error(ERR_SLAVE_DEFINED,
|
|
i18n("There was an error with hpls - please ensure it is installed"));
|
|
}
|
|
|
|
//clean up
|
|
delete myKProcess; myKProcess = 0;
|
|
disconnect(myKProcess, TQT_SIGNAL(receivedStdout(KProcess *, char *, int)),
|
|
this, TQT_SLOT(slotGetStdOutput(KProcess *, char *, int)));
|
|
|
|
UDSEntry entry;
|
|
if (!standardOutputStream.isEmpty()) {
|
|
TQTextStream in(&standardOutputStream, IO_ReadOnly);
|
|
TQString line = in.readLine(); //throw away top file which shows current directory
|
|
line = in.readLine();
|
|
|
|
while (line != NULL) {
|
|
//1.0.4 puts this funny line in sometimes, we don't want it
|
|
if (line.tqcontains("Thread ") == 0) {
|
|
entry = makeUDS(line);
|
|
listEntry(entry, false);
|
|
}
|
|
line = in.readLine();
|
|
}
|
|
}//if standardOutputStream != null
|
|
|
|
listEntry(entry, true);
|
|
finished();
|
|
|
|
}//if filename == null
|
|
}
|
|
|
|
//stat() called to see if it's a file or directory, called before listDir() or get()
|
|
void MacProtocol::stat(const KURL& url) {
|
|
statEntry(doStat(url));
|
|
finished();
|
|
}
|
|
|
|
//doStat(), does all the work that stat() needs
|
|
//it's been separated out so it can be called from get() which
|
|
//also need information
|
|
TQValueList<KIO::UDSAtom> MacProtocol::doStat(const KURL& url) {
|
|
TQString filename = prepareHP(url);
|
|
|
|
if (filename.isNull()) {
|
|
error(ERR_SLAVE_DEFINED, i18n("No filename was found in the URL"));
|
|
} else if (! filename.isEmpty()) {
|
|
myKProcess = new KShellProcess();
|
|
|
|
*myKProcess << "hpls" << "-ld" << filename;
|
|
|
|
standardOutputStream = TQString::null;
|
|
connect(myKProcess, TQT_SIGNAL(receivedStdout(KProcess *, char *, int)),
|
|
this, TQT_SLOT(slotGetStdOutput(KProcess *, char *, int)));
|
|
|
|
myKProcess->start(KProcess::Block, KProcess::All);
|
|
|
|
if ((!myKProcess->normalExit()) || (!myKProcess->exitStatus() == 0)) {
|
|
error(ERR_SLAVE_DEFINED,
|
|
i18n("hpls did not exit normally - please ensure you have installed the hfsplus tools"));
|
|
}
|
|
|
|
//clean up
|
|
delete myKProcess; myKProcess = 0;
|
|
disconnect(myKProcess, TQT_SIGNAL(receivedStdout(KProcess *, char *, int)),
|
|
this, TQT_SLOT(slotGetStdOutput(KProcess *, char *, int)));
|
|
|
|
if (standardOutputStream.isEmpty()) {
|
|
filename.tqreplace("\\ ", " "); //get rid of escapes
|
|
filename.tqreplace("\\&", "&"); //mm, slashes...
|
|
filename.tqreplace("\\!", "!");
|
|
filename.tqreplace("\\(", "(");
|
|
filename.tqreplace("\\)", ")");
|
|
error(ERR_DOES_NOT_EXIST, filename);
|
|
} else {
|
|
//remove trailing \n
|
|
TQString line = standardOutputStream.left(standardOutputStream.length()-1);
|
|
UDSEntry entry = makeUDS(line);
|
|
return entry;
|
|
}
|
|
} else { //filename is empty means we're looking at root dir
|
|
//we don't have a listing for the root directory so here's a dummy one
|
|
UDSEntry entry = makeUDS("d 0 item Jan 01 2000 /");
|
|
return entry;
|
|
}//if filename == null
|
|
|
|
return TQValueList<KIO::UDSAtom>();
|
|
}
|
|
|
|
//prepareHP() called from get() listDir() and stat()
|
|
//(re)mounts the partition and changes to the appropriate directory
|
|
TQString MacProtocol::prepareHP(const KURL& url) {
|
|
TQString path = url.path(-1);
|
|
if (path.left(1) == "/") {
|
|
path = path.mid(1); // strip leading slash
|
|
}
|
|
|
|
//find out if a device has been specified in the query e.g. ?dev=/dev/fd0
|
|
//or in the config file (query device entries are saved to config file)
|
|
TQString device;
|
|
KConfig* config = new KConfig("macrc");
|
|
|
|
TQString query = url.query();
|
|
int modepos = query.tqfind("dev=");
|
|
if (modepos == -1) {
|
|
//no device specified, read from config or go with #define PARTITION
|
|
device = config->readEntry("device",PARTITION);
|
|
} else {
|
|
//TODO this means dev=foo must be the last argument in the query
|
|
device = query.mid(modepos + 4);
|
|
config->writeEntry("device",device);
|
|
}
|
|
delete config; config = 0;
|
|
|
|
//first we run just hpmount and check the output to see if it's version 1.0.2 or 1.0.4
|
|
myKProcess = new KProcess();
|
|
*myKProcess << "hpmount";
|
|
standardOutputStream = TQString::null;
|
|
connect(myKProcess, TQT_SIGNAL(receivedStderr(KProcess *, char *, int)),
|
|
this, TQT_SLOT(slotGetStdOutput(KProcess *, char *, int)));
|
|
|
|
myKProcess->start(KProcess::Block, KProcess::All);
|
|
|
|
bool version102 = true;
|
|
|
|
if (standardOutputStream.tqcontains("options") != 0) {
|
|
version102 = false;
|
|
}
|
|
|
|
delete myKProcess; myKProcess = 0;
|
|
disconnect(myKProcess, TQT_SIGNAL(receivedStderr(KProcess *, char *, int)),
|
|
this, TQT_SLOT(slotGetStdOutput(KProcess *, char *, int)));
|
|
|
|
//now mount the drive
|
|
myKProcess = new KProcess();
|
|
if (version102) {
|
|
*myKProcess << "hpmount" << device;
|
|
} else {
|
|
*myKProcess << "hpmount" << "-r" << device;
|
|
}
|
|
|
|
myKProcess->start(KProcess::Block, KProcess::All);
|
|
|
|
if ((!myKProcess->normalExit()) || (!myKProcess->exitStatus() == 0)) {
|
|
//TODO this error interrupts the user when typing ?dev=foo on each letter of foo
|
|
error(ERR_SLAVE_DEFINED,
|
|
i18n("hpmount did not exit normally - please ensure that hfsplus utils are installed,\n"
|
|
"that you have permission to read the partition (ls -l /dev/hdaX)\n"
|
|
"and that you have specified the correct partition.\n"
|
|
"You can specify partitions by adding ?dev=/dev/hda2 to the URL."));
|
|
return NULL;
|
|
}
|
|
|
|
//clean up
|
|
delete myKProcess; myKProcess = 0;
|
|
|
|
//escape any funny characters
|
|
//TODO are there any more characters to escape?
|
|
path.tqreplace(" ", "\\ ");
|
|
path.tqreplace("&", "\\&");
|
|
path.tqreplace("!", "\\!");
|
|
path.tqreplace("(", "\\(");
|
|
path.tqreplace(")", "\\)");
|
|
|
|
//then change to the right directory
|
|
int s; TQString dir;
|
|
s = path.tqfind('/');
|
|
while (s != -1) {
|
|
dir = path.left(s);
|
|
path = path.mid(s+1);
|
|
|
|
myKProcess = new KProcess();
|
|
*myKProcess << "hpcd" << dir;
|
|
|
|
myKProcess->start(KProcess::Block, KProcess::All);
|
|
|
|
if ((!myKProcess->normalExit()) || (!myKProcess->exitStatus() == 0)) {
|
|
error(ERR_SLAVE_DEFINED,
|
|
i18n("hpcd did not exit normally - please ensure it is installed"));
|
|
return NULL;
|
|
}
|
|
|
|
//clean up
|
|
delete myKProcess; myKProcess = 0;
|
|
|
|
s = path.tqfind('/');
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
//makeUDS() takes a line of output from hpls -l and converts it into
|
|
// one of these UDSEntrys to return
|
|
//called from listDir() and stat()
|
|
TQValueList<KIO::UDSAtom> MacProtocol::makeUDS(const TQString& _line) {
|
|
TQString line(_line);
|
|
UDSEntry entry;
|
|
|
|
//is it a file or a directory
|
|
TQRegExp dirRE("^d. +([^ ]+) +([^ ]+) +([^ ]+) +([^ ]+) +([^ ]+) +(.*)");
|
|
TQRegExp fileRE("^([f|F]). +(....)/(....) +([^ ]+) +([^ ]+) +([^ ]+) +([^ ]+) +([^ ]+) +(.*)");
|
|
if (dirRE.exactMatch(line)) {
|
|
UDSAtom atom;
|
|
atom.m_uds = KIO::UDS_NAME;
|
|
atom.m_str = dirRE.cap(6);
|
|
entry.append(atom);
|
|
|
|
atom.m_uds = KIO::UDS_MODIFICATION_TIME;
|
|
atom.m_long = makeTime(dirRE.cap(4), dirRE.cap(3), dirRE.cap(5));
|
|
entry.append(atom);
|
|
|
|
atom.m_uds = KIO::UDS_FILE_TYPE;
|
|
atom.m_long = S_IFDIR;
|
|
entry.append(atom);
|
|
|
|
atom.m_uds = KIO::UDS_ACCESS;
|
|
atom.m_long = 0755;
|
|
entry.append(atom);
|
|
|
|
} else if (fileRE.exactMatch(line)) {
|
|
UDSAtom atom;
|
|
atom.m_uds = KIO::UDS_NAME;
|
|
atom.m_str = fileRE.cap(9);
|
|
entry.append(atom);
|
|
|
|
atom.m_uds = KIO::UDS_SIZE;
|
|
TQString theSize(fileRE.cap(4)); //TODO: this is data size, what about resource size?
|
|
atom.m_long = theSize.toLong();
|
|
entry.append(atom);
|
|
|
|
atom.m_uds = KIO::UDS_MODIFICATION_TIME;
|
|
atom.m_long = makeTime(fileRE.cap(7), fileRE.cap(6), fileRE.cap(8));
|
|
entry.append(atom);
|
|
|
|
atom.m_uds = KIO::UDS_ACCESS;
|
|
if (TQString(fileRE.cap(1)) == TQString("F")) { //if locked then read only
|
|
atom.m_long = 0444;
|
|
} else {
|
|
atom.m_long = 0644;
|
|
}
|
|
entry.append(atom);
|
|
|
|
atom.m_uds = KIO::UDS_MIME_TYPE;
|
|
TQString mimetype = getMimetype(fileRE.cap(2),fileRE.cap(3));
|
|
atom.m_str = mimetype.local8Bit();
|
|
entry.append(atom);
|
|
|
|
// Is it a file or a link/alias, just make aliases link to themselves
|
|
if (TQString(fileRE.cap(2)) == TQString("adrp") ||
|
|
TQString(fileRE.cap(2)) == TQString("fdrp")) {
|
|
atom.m_uds = KIO::UDS_FILE_TYPE;
|
|
atom.m_long = S_IFREG;
|
|
entry.append(atom);
|
|
|
|
atom.m_uds = KIO::UDS_LINK_DEST;
|
|
atom.m_str = fileRE.cap(9); //I have a file called "Mozilla alias" the name
|
|
// of which displays funny because of this.
|
|
// No idea why. Same for other kioslaves. A font thing?
|
|
entry.append(atom);
|
|
} else {
|
|
atom.m_uds = KIO::UDS_FILE_TYPE;
|
|
atom.m_long = S_IFREG;
|
|
entry.append(atom);
|
|
}
|
|
} else {
|
|
error(ERR_INTERNAL, i18n("hpls output was not matched"));
|
|
} //if match dirRE or fileRE
|
|
|
|
return entry;
|
|
}
|
|
|
|
//slotGetStdOutput() grabs output from the hp commands
|
|
// and adds it to the buffer
|
|
void MacProtocol::slotGetStdOutput(KProcess*, char *s, int len) {
|
|
standardOutputStream += TQString::fromLocal8Bit(s, len);
|
|
}
|
|
|
|
//slotSetDataStdOutput() is used during hpcopy to give
|
|
//standard output to KDE
|
|
void MacProtocol::slotSetDataStdOutput(KProcess*, char *s, int len) {
|
|
processedBytes += len;
|
|
processedSize(processedBytes);
|
|
TQByteArray array;
|
|
array.setRawData(s, len);
|
|
data(array);
|
|
array.resetRawData(s, len);
|
|
}
|
|
|
|
//makeTime() takes in the date output from hpls -l
|
|
//and returns as good a timestamp as we're going to get
|
|
int MacProtocol::makeTime(TQString mday, TQString mon, TQString third) {
|
|
int year; int month; int day;
|
|
int hour; int minute;
|
|
|
|
//find the month
|
|
if (mon == "Jan") { month = 1; }
|
|
else if (mon == "Feb") { month = 2; }
|
|
else if (mon == "Mar") { month = 3; }
|
|
else if (mon == "Apr") { month = 4; }
|
|
else if (mon == "May") { month = 5; }
|
|
else if (mon == "Jun") { month = 6; }
|
|
else if (mon == "Jul") { month = 7; }
|
|
else if (mon == "Aug") { month = 8; }
|
|
else if (mon == "Sep") { month = 9; }
|
|
else if (mon == "Oct") { month = 10; }
|
|
else if (mon == "Nov") { month = 11; }
|
|
else if (mon == "Dec") { month = 12; }
|
|
else {
|
|
error(ERR_INTERNAL, i18n("Month output from hpls -l not matched"));
|
|
month = 13;
|
|
}
|
|
|
|
//if the file is recent (last 12 months) hpls gives us the time,
|
|
// otherwise it only prints the year
|
|
TQRegExp hourMin("(..):(..)");
|
|
if (hourMin.exactMatch(third)) {
|
|
TQDate tqcurrentDate(TQDate::currentDate());
|
|
|
|
if (month > tqcurrentDate.month()) {
|
|
year = tqcurrentDate.year() - 1;
|
|
} else {
|
|
year = tqcurrentDate.year();
|
|
}
|
|
TQString h(hourMin.cap(1));
|
|
TQString m(hourMin.cap(2));
|
|
hour = h.toInt();
|
|
minute = m.toInt();
|
|
} else {
|
|
year = third.toInt();
|
|
hour = 0;
|
|
minute = 0;
|
|
}// if hour:min or year
|
|
|
|
day = mday.toInt();
|
|
|
|
//check it's valid
|
|
if ( (!TQDate::isValid(year, month, day)) || (!TQTime::isValid(hour, minute, 0) ) ) {
|
|
error(ERR_INTERNAL, i18n("Could not parse a valid date from hpls"));
|
|
}
|
|
|
|
//put it together and work it out
|
|
TQDate fileDate(year, month, day);
|
|
TQTime fileTime(hour, minute);
|
|
TQDateTime fileDateTime(fileDate, fileTime);
|
|
|
|
return fileDateTime.toTime_t();
|
|
}
|
|
|
|
TQString MacProtocol::getMimetype(TQString type, TQString app) {
|
|
if (type == TQString("TEXT") && app == TQString("ttxt")) {
|
|
return TQString("text/plain");
|
|
} else if (type == TQString("TEXT") && app == TQString("udog")) {
|
|
return TQString("text/html");
|
|
} else if (type == TQString("svgs")) {
|
|
return TQString("text/xml");
|
|
} else if (type == TQString("ZIP ")) {
|
|
return TQString("application/zip");
|
|
} else if (type == TQString("pZip")) {
|
|
return TQString("application/zip");
|
|
} else if (type == TQString("APPL")) {
|
|
return TQString("application/x-executable");
|
|
} else if (type == TQString("MooV")) {
|
|
return TQString("video/quicktime");
|
|
} else if (type == TQString("TEXT") && app == TQString("MSWD")) {
|
|
return TQString("application/vnd.ms-word");
|
|
} else if (type == TQString("PDF ")) {
|
|
return TQString("application/pdf");
|
|
} else if (app == TQString("CARO")) {
|
|
return TQString("application/pdf");
|
|
} else if (type == TQString("SIT5")) {
|
|
return TQString("application/x-stuffit");
|
|
} else if (type == TQString("SITD")) {
|
|
return TQString("application/x-stuffit");
|
|
} else if (type == TQString("SIT!")) {
|
|
return TQString("application/x-stuffit");
|
|
} else if (app == TQString("SIT!")) {
|
|
return TQString("application/x-stuffit");
|
|
} else if (type == TQString("RTFf")) {
|
|
return TQString("text/rtf");
|
|
} else if (type == TQString("GIFf")) {
|
|
return TQString("image/gif");
|
|
} else if (type == TQString("JPEG")) {
|
|
return TQString("image/jpeg");
|
|
} else if (type == TQString("PNGf")) {
|
|
return TQString("image/png");
|
|
} else if (type == TQString("XBMm")) {
|
|
return TQString("image/x-xbm");
|
|
} else if (type == TQString("EPSF")) {
|
|
return TQString("image/x-epsf");
|
|
} else if (type == TQString("TIFF")) {
|
|
return TQString("image/tiff");
|
|
} else if (type == TQString("PICT")) {
|
|
return TQString("image/pict");
|
|
} else if (type == TQString("TPIC")) {
|
|
return TQString("image/x-targa");
|
|
} else if (type == TQString("ULAW")) {
|
|
return TQString("audio/basic");
|
|
} else if (type == TQString("AIFF")) {
|
|
return TQString("audio/x-aiff");
|
|
} else if (type == TQString("WAVE")) {
|
|
return TQString("audio/x-wav");
|
|
} else if (type == TQString("FFIL") && app == TQString("DMOV")) {
|
|
return TQString("application/x-font");
|
|
} else if (type == TQString("XLS3")) {
|
|
return TQString("application/vnd.ms-excel");
|
|
} else if (type == TQString("XLS4")) {
|
|
return TQString("application/vnd.ms-excel");
|
|
} else if (type == TQString("XLS5")) {
|
|
return TQString("application/vnd.ms-excel");
|
|
} else if (app == TQString("MSWD")) {
|
|
return TQString("application/vnd.ms-word");
|
|
} else if (type == TQString("TEXT")) {
|
|
return TQString("text/plain");
|
|
} else if (app == TQString("ttxt")) {
|
|
return TQString("text/plain");
|
|
}
|
|
return TQString("application/octet-stream");
|
|
}
|
|
|
|
|