/*************************************************************************** tdeio_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 <tdeconfig.h> #include <tqstring.h> #include <tqregexp.h> #include <sys/stat.h> #include <stdlib.h> #include <iostream> #include <time.h> #include "tdeio_mac.moc" using namespace TDEIO; extern "C" { int KDE_EXPORT kdemain(int, char **argv) { TDEInstance instance("tdeio_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 myTDEProcess; myTDEProcess = 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 == TDEIO::UDS_MIME_TYPE) { mime = (*it).m_str; } if ((*it).m_uds == TDEIO::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.find("mode="); int textpos = mime.find("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 myTDEProcess = new TDEProcess(); *myTDEProcess << "hpcopy" << mode << path << "-"; //data is now sent directly from the slot connect(myTDEProcess, TQT_SIGNAL(receivedStdout(TDEProcess *, char *, int)), this, TQT_SLOT(slotSetDataStdOutput(TDEProcess *, char *, int))); myTDEProcess->start(TDEProcess::Block, TDEProcess::All); if (!myTDEProcess->normalExit() || !(myTDEProcess->exitStatus() == 0)) { error(ERR_SLAVE_DEFINED, i18n("There was an error with hpcopy - please ensure it is installed")); return; } //clean up delete myTDEProcess; myTDEProcess = 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 { myTDEProcess = new TDEProcess(); *myTDEProcess << "hpls" << "-la" << filename; standardOutputStream = TQString::null; connect(myTDEProcess, TQT_SIGNAL(receivedStdout(TDEProcess *, char *, int)), this, TQT_SLOT(slotGetStdOutput(TDEProcess *, char *, int))); myTDEProcess->start(TDEProcess::Block, TDEProcess::All); if ((!myTDEProcess->normalExit()) || (!myTDEProcess->exitStatus() == 0)) { error(ERR_SLAVE_DEFINED, i18n("There was an error with hpls - please ensure it is installed")); } //clean up delete myTDEProcess; myTDEProcess = 0; disconnect(myTDEProcess, TQT_SIGNAL(receivedStdout(TDEProcess *, char *, int)), this, TQT_SLOT(slotGetStdOutput(TDEProcess *, 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.contains("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<TDEIO::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()) { myTDEProcess = new KShellProcess(); *myTDEProcess << "hpls" << "-ld" << filename; standardOutputStream = TQString::null; connect(myTDEProcess, TQT_SIGNAL(receivedStdout(TDEProcess *, char *, int)), this, TQT_SLOT(slotGetStdOutput(TDEProcess *, char *, int))); myTDEProcess->start(TDEProcess::Block, TDEProcess::All); if ((!myTDEProcess->normalExit()) || (!myTDEProcess->exitStatus() == 0)) { error(ERR_SLAVE_DEFINED, i18n("hpls did not exit normally - please ensure you have installed the hfsplus tools")); } //clean up delete myTDEProcess; myTDEProcess = 0; disconnect(myTDEProcess, TQT_SIGNAL(receivedStdout(TDEProcess *, char *, int)), this, TQT_SLOT(slotGetStdOutput(TDEProcess *, char *, int))); if (standardOutputStream.isEmpty()) { filename.replace("\\ ", " "); //get rid of escapes filename.replace("\\&", "&"); //mm, slashes... filename.replace("\\!", "!"); filename.replace("\\(", "("); filename.replace("\\)", ")"); 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<TDEIO::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; TDEConfig* config = new TDEConfig("macrc"); TQString query = url.query(); int modepos = query.find("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 myTDEProcess = new TDEProcess(); *myTDEProcess << "hpmount"; standardOutputStream = TQString::null; connect(myTDEProcess, TQT_SIGNAL(receivedStderr(TDEProcess *, char *, int)), this, TQT_SLOT(slotGetStdOutput(TDEProcess *, char *, int))); myTDEProcess->start(TDEProcess::Block, TDEProcess::All); bool version102 = true; if (standardOutputStream.contains("options") != 0) { version102 = false; } delete myTDEProcess; myTDEProcess = 0; disconnect(myTDEProcess, TQT_SIGNAL(receivedStderr(TDEProcess *, char *, int)), this, TQT_SLOT(slotGetStdOutput(TDEProcess *, char *, int))); //now mount the drive myTDEProcess = new TDEProcess(); if (version102) { *myTDEProcess << "hpmount" << device; } else { *myTDEProcess << "hpmount" << "-r" << device; } myTDEProcess->start(TDEProcess::Block, TDEProcess::All); if ((!myTDEProcess->normalExit()) || (!myTDEProcess->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 myTDEProcess; myTDEProcess = 0; //escape any funny characters //TODO are there any more characters to escape? path.replace(" ", "\\ "); path.replace("&", "\\&"); path.replace("!", "\\!"); path.replace("(", "\\("); path.replace(")", "\\)"); //then change to the right directory int s; TQString dir; s = path.find('/'); while (s != -1) { dir = path.left(s); path = path.mid(s+1); myTDEProcess = new TDEProcess(); *myTDEProcess << "hpcd" << dir; myTDEProcess->start(TDEProcess::Block, TDEProcess::All); if ((!myTDEProcess->normalExit()) || (!myTDEProcess->exitStatus() == 0)) { error(ERR_SLAVE_DEFINED, i18n("hpcd did not exit normally - please ensure it is installed")); return NULL; } //clean up delete myTDEProcess; myTDEProcess = 0; s = path.find('/'); } 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<TDEIO::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 = TDEIO::UDS_NAME; atom.m_str = dirRE.cap(6); entry.append(atom); atom.m_uds = TDEIO::UDS_MODIFICATION_TIME; atom.m_long = makeTime(dirRE.cap(4), dirRE.cap(3), dirRE.cap(5)); entry.append(atom); atom.m_uds = TDEIO::UDS_FILE_TYPE; atom.m_long = S_IFDIR; entry.append(atom); atom.m_uds = TDEIO::UDS_ACCESS; atom.m_long = 0755; entry.append(atom); } else if (fileRE.exactMatch(line)) { UDSAtom atom; atom.m_uds = TDEIO::UDS_NAME; atom.m_str = fileRE.cap(9); entry.append(atom); atom.m_uds = TDEIO::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 = TDEIO::UDS_MODIFICATION_TIME; atom.m_long = makeTime(fileRE.cap(7), fileRE.cap(6), fileRE.cap(8)); entry.append(atom); atom.m_uds = TDEIO::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 = TDEIO::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 = TDEIO::UDS_FILE_TYPE; atom.m_long = S_IFREG; entry.append(atom); atom.m_uds = TDEIO::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 tdeioslaves. A font thing? entry.append(atom); } else { atom.m_uds = TDEIO::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(TDEProcess*, char *s, int len) { standardOutputStream += TQString::fromLocal8Bit(s, len); } //slotSetDataStdOutput() is used during hpcopy to give //standard output to KDE void MacProtocol::slotSetDataStdOutput(TDEProcess*, 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 currentDate(TQDate::currentDate()); if (month > currentDate.month()) { year = currentDate.year() - 1; } else { year = currentDate.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"); }