diff options
Diffstat (limited to 'kfile-plugins/torrent')
-rw-r--r-- | kfile-plugins/torrent/Makefile.am | 22 | ||||
-rw-r--r-- | kfile-plugins/torrent/README | 7 | ||||
-rw-r--r-- | kfile-plugins/torrent/bbase.h | 91 | ||||
-rw-r--r-- | kfile-plugins/torrent/bdict.cpp | 220 | ||||
-rw-r--r-- | kfile-plugins/torrent/bdict.h | 203 | ||||
-rw-r--r-- | kfile-plugins/torrent/bint.cpp | 104 | ||||
-rw-r--r-- | kfile-plugins/torrent/bint.h | 111 | ||||
-rw-r--r-- | kfile-plugins/torrent/blist.cpp | 178 | ||||
-rw-r--r-- | kfile-plugins/torrent/blist.h | 200 | ||||
-rw-r--r-- | kfile-plugins/torrent/bstring.cpp | 128 | ||||
-rw-r--r-- | kfile-plugins/torrent/bstring.h | 138 | ||||
-rw-r--r-- | kfile-plugins/torrent/bytetape.cpp | 158 | ||||
-rw-r--r-- | kfile-plugins/torrent/bytetape.h | 194 | ||||
-rw-r--r-- | kfile-plugins/torrent/kfile_torrent.cpp | 404 | ||||
-rw-r--r-- | kfile-plugins/torrent/kfile_torrent.desktop | 58 | ||||
-rw-r--r-- | kfile-plugins/torrent/kfile_torrent.h | 84 |
16 files changed, 2300 insertions, 0 deletions
diff --git a/kfile-plugins/torrent/Makefile.am b/kfile-plugins/torrent/Makefile.am new file mode 100644 index 00000000..9727be90 --- /dev/null +++ b/kfile-plugins/torrent/Makefile.am @@ -0,0 +1,22 @@ +## Makefile.am for folder file meta info plugin + +INCLUDES = $(all_includes) + +# these are the headers for your project +noinst_HEADERS = kfile_torrent.h bbase.h bdict.h bytetape.h \ + bstring.h bint.h blist.h + +kde_module_LTLIBRARIES = kfile_torrent.la + +kfile_torrent_la_SOURCES = bytetape.cpp bint.cpp bstring.cpp blist.cpp bdict.cpp kfile_torrent.cpp +kfile_torrent_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) +kfile_torrent_la_LIBADD = $(LIB_KIO) + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +services_DATA = kfile_torrent.desktop +servicesdir = $(kde_servicesdir) + +messages: + $(XGETTEXT) *.cpp -o $(podir)/kfile_torrent.pot diff --git a/kfile-plugins/torrent/README b/kfile-plugins/torrent/README new file mode 100644 index 00000000..c7147c9d --- /dev/null +++ b/kfile-plugins/torrent/README @@ -0,0 +1,7 @@ +This is a meta information plugin for BitTorrent files (*.torrent). + +It doesn't depend on BitTorrent or any non-standard library being installed. + +The .torrent files used by BitTorrent are coded in what is called a b-encoding +by the BitTorrent author. Please see the actual kfile plugin source code for +more information on the encoding. diff --git a/kfile-plugins/torrent/bbase.h b/kfile-plugins/torrent/bbase.h new file mode 100644 index 00000000..19455523 --- /dev/null +++ b/kfile-plugins/torrent/bbase.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _BBASE_H +#define _BBASE_H + +#include <ksharedptr.h> + +class QIODevice; + +/** + * Abstract base class for the b-encoded types. Re-implemented + * by BInt, BList, BDict, and BString. Derive from this class in + * order to make a new type for the b-encoding suite. Classes + * derived from this one should not throw exceptions. Instead, + * they should implement isValid(), and allow calling modules to + * check error status that way. + * + * @author Michael Pyne <mpyne@grammarian.homelinux.net> + * @see BInt, BList, BDict, BString, KSharedPtr + */ + +// Derive from KShared to enable use of KSharedPtr. +class BBase : public KShared +{ + public: + + /** + * Identifies the particular class that has been instantiated. All + * subclasses of this class should have an identifier here. + */ + enum classID { + bBase, /**< This class is BBase. No subclass should return this. */ + bString, /**< This class is a BString. */ + bInt, /**< This class is a BInt. */ + bList, /**< This class is a BList. */ + bDict /**< This class is a BDict. (Dictionary/Map) */ + }; + + /** + * Returns the type identification of the object. It will + * be a value in the classID enum. A subclass of this class + * must implement this function. + * + * @return type identifier of the class + * @see classID + */ + virtual classID type_id() const = 0; + + /** + * Destructor for the class. This function must be reimplemented + * in subclasses. + */ + virtual ~BBase () { ; } + + /** + * Returns the validity status of the object. Newly constructed + * objects are invalid unless the initialization sequence completed + * successfully. + * + * @return the validity status of the object + */ + virtual bool isValid() const = 0; + + /** + * Outputs the b-encoded representation of the object to the given + * QIODevice. + * @param device the QIODevice to write to + * @return true on a successful write, false otherwise + */ + virtual bool writeToDevice (QIODevice &device) = 0; +}; + +#endif /* _BBASE_H */ + +// vim: set et sw=4 ts=4: diff --git a/kfile-plugins/torrent/bdict.cpp b/kfile-plugins/torrent/bdict.cpp new file mode 100644 index 00000000..47b3bcad --- /dev/null +++ b/kfile-plugins/torrent/bdict.cpp @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <qstringlist.h> +#include <qiodevice.h> + +#include <kdebug.h> + +#include "bbase.h" +#include "bdict.h" +#include "bstring.h" +#include "bint.h" +#include "blist.h" + +BDict::BDict (QByteArray &dict, int start) + : m_map(), m_valid(false) +{ + ByteTape tape(dict, start); + + init (tape); +} + +BDict::BDict (ByteTape &tape) + : m_map(), m_valid (false) +{ + init (tape); +} + +void BDict::init (ByteTape &tape) +{ + if (*tape != 'd') + { + kdDebug(7034) << "This isn't a dictionary!" << endl; + return; // This isn't a dictionary + } + + tape++; + + // We need to loop and read in a string, then read in some data + while (*tape != 'e') + { + BBase *temp_item = 0; + + // Read in string + KSharedPtr<BString> str (new BString (tape)); + + // Ensure str will be automatically deleted + if (!str || !str->isValid()) + { + kdDebug(7034) << (str ? "Invalid string" : "Unable to read String!") << endl; + return; + } + + // Read in data + switch (*tape) + { + case 'l': + temp_item = new BList (tape); + break; + + case 'i': + temp_item = new BInt (tape); + break; + + case 'd': + temp_item = new BDict (tape); + break; + + default: + // Hopefully this is a string + temp_item = new BString (tape); + } + + if (!temp_item || !temp_item->isValid()) + { + kdDebug(7034) << (temp_item ? "Invalid item!" + : "Unable to create keyed data!") << endl; + return; + } + + m_map.insert(str->get_string(), temp_item); + } + + // Move past the 'e' + tape++; + + // Let the map delete the items + m_map.setAutoDelete (true); + + // Oh yeah, we're valid now, too. :-) + m_valid = true; +} + +BDict::~BDict () +{ + // QDict will take care of deleting each entry that + // it holds. +} + +BInt *BDict::findInt (const char *key) +{ + BBase *base = find(key); + + if (base && base->type_id() == bInt) + return dynamic_cast<BInt*>(base); + + return 0; +} + +BList *BDict::findList (const char *key) +{ + BBase *base = find(key); + + if (base && base->type_id() == bList) + return dynamic_cast<BList*>(base); + + return 0; +} + +BDict *BDict::findDict (const char *key) +{ + BBase *base = find(key); + + if (base && base->type_id() == bDict) + return dynamic_cast<BDict*>(base); + + return 0; +} + +BString *BDict::findStr (const char *key) +{ + BBase *base = find(key); + + if (base && base->type_id() == bString) + return dynamic_cast<BString*>(base); + + return 0; +} + +bool BDict::writeToDevice(QIODevice &device) +{ + if (!isValid()) + return false; + + const char *d_str = "d"; + const char *e_str = "e"; + Q_LONG written = 0, result = 0; + + written = device.writeBlock (d_str, 1); + while (written < 1) + { + if (written < 0 || result < 0) + return false; + + result = device.writeBlock (d_str, 1); + written += result; + } + + // Strings are supposed to be written in the dictionary such that + // the keys are in sorted order. QDictIterator doesn't support an + // ordering, so we have to get a list of all the keys, sort it, and + // then go by the list. + + BBaseHashIterator iter (m_map); + QStringList key_list; + + for ( ; iter.current(); ++iter) + key_list.append(iter.currentKey()); + + key_list.sort(); + + QStringList::Iterator key_iter; + for (key_iter = key_list.begin(); key_iter != key_list.end(); ++key_iter) + { + QCString utfString = (*key_iter).utf8(); + QString str = QString("%1:").arg(utfString.size() - 1); + + QCString lenString = str.utf8(); + + // Write out length of key + device.writeBlock(lenString.data(), lenString.size() - 1); + + // Write out actual key + device.writeBlock(utfString.data(), utfString.size() - 1); + + // Write out the key's data + BBase *base = m_map.find(*key_iter); + if (!base->writeToDevice (device)) + return false; + } + + written = device.writeBlock (e_str, 1); + while ((uint) written < 1) + { + if (written < 0 || result < 0) + return false; + + result = device.writeBlock (e_str, 1); + written += result; + } + + return true; +} + +// vim: set et sw=4 ts=4: diff --git a/kfile-plugins/torrent/bdict.h b/kfile-plugins/torrent/bdict.h new file mode 100644 index 00000000..34c471c6 --- /dev/null +++ b/kfile-plugins/torrent/bdict.h @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _BDICT_H +#define _BDICT_H + +#include <qdict.h> +#include <qcstring.h> // QByteArray +#include "bytetape.h" +#include "bbase.h" + +// Some useful typedefs +typedef QDict<BBase> BBaseHash; +typedef QDictIterator<BBase> BBaseHashIterator; + +// Forward declarations +class BInt; +class BList; +class BString; + +/** + * Class to handle the BitTorrent b-encoded dictionary. It is keyed + * using const char *strings, and stores pointers to a class descended + * from BBase, such as BInt, BString, BList, or even more BDicts. + * + * @author Michael Pyne <mpyne@grammarian.homelinux.net> + * @see BBase, BInt, BString, BList + */ +class BDict : public BBase +{ + public: + + /** + * Construct a dictionary based on the b-encoded data contained in + * @p dict. You need to pass a value for @p start if the b-encoded + * dictionary doesn't start at the beginning of @p dict. + * + * @param dict the buffer containing the b-encoded dictionary + * @param start the position of the data within the buffer + */ + BDict (QByteArray &dict, int start = 0); + + /** + * Construct a dictionary from a ByteTape. The data and current + * position of @p tape will be shared with all objects using it. + * @p tape should already be positioned to the b-encoded dictionary. + * After construction, @p tape will point to the byte after the + * dictionary if successful. If not successful, @p tape will have + * an undefined position. + * + * @param tape the ByteTape to read from + * @see ByteTape + */ + BDict (ByteTape &tape); + + /** + * Destroys the object and frees all memory allocated to it. + */ + virtual ~BDict(); + + /** + * Returns the type of this class. + * + * @return bDict. This value is only returned by this class. + */ + virtual classID type_id() const { return bDict; } + + /** + * Returns the number of keyed values contained within this + * dictionary. + * + * @return the number of items in this dictionary + */ + virtual int count() const { return m_map.count(); } + + /** + * This function should be called to determine whether the + * dictionary was successfully created, since no exceptions + * are thrown. + * + * @return true if this is a valid (possibly empty!) dictionary, + * false otherwise + */ + virtual bool isValid() const { return m_valid; } + + /** + * This function determines whether or not a value with a + * certain key exists in the dictionary. The key is case-sensitive. + * + * @param key the key to search for a value for + * @return true, if there is a value for the @p key in the + * dictionary, false otherwise + */ + virtual bool contains (const char *key) { return m_map.find(key) != 0; } + + /** + * Returns a pointer to the BBase descendant keyed by @p key. You + * can use the type_id() method to determine the type of the + * object returned. + * + * @param key the key to search the dictionary for + * @return a pointer to the matching object, or 0 if no object + * matches the key + * @see BBase + */ + virtual BBase *find (const char *key) { return m_map.find(key); } + + /** + * Convienience function to find and return a BInt keyed by @p key. + * + * @param key the key to find a value for + * @return 0 if the key doesn't match a value, or if the value isn't + * a BInt. Otherwise, a pointer to the matching BInt is + * returned. + * @see BInt + */ + BInt* findInt (const char *key); + + /** + * Convienience function to find and return a BList keyed by @p key. + * + * @param key the key to find a value for + * @return 0 if the key doesn't match a value, or if the value isn't + * a BList. Otherwise, a pointer to the matching BList is + * returned. + * @see BList + */ + BList* findList (const char *key); + + /** + * Convienience function to find and return a BDict keyed by @p key. + * + * @param key the key to find a value for + * @return 0 if the key doesn't match a value, or if the value isn't + * a BDict. Otherwise, a pointer to the matching BDict is + * returned. + * @see BDict + */ + BDict* findDict (const char *key); + + /** + * Convienience function to find and return a BString keyed by @p key. + * + * @param key the key to find a value for + * @return 0 if the key doesn't match a value, or if the value isn't + * a BString. Otherwise, a pointer to the matching BString is + * returned. + * @see BString + */ + BString* findStr (const char *key); + + /** + * Outputs the b-encoded representation of the object to the given + * QIODevice. + * @param device the QIODevice to write to + * @return true on a successful write, false otherwise + */ + virtual bool writeToDevice (QIODevice &device); + + /** + * Returns a QDictIterator<BBase> that you can use to iterate through + * the items in the dictionary. + * + * @return QDictIterator<BBase>, which can be used to iterate through + * the items in the dictionary. + */ + BBaseHashIterator iterator() const + { + return BBaseHashIterator(m_map); + } + + private: + + /** + * This function handles the actual initialization of the object upon + * construction, and set the m_valid flag if successful. + * + * @param tape the ByteTape to read from + */ + void init (ByteTape &tape); + + BBaseHash m_map; /// The QDict that actually store the data + bool m_valid; /// Store initialization status +}; + +#endif /* _BDICT_H */ + +// vim: set et sw=4 ts=4: diff --git a/kfile-plugins/torrent/bint.cpp b/kfile-plugins/torrent/bint.cpp new file mode 100644 index 00000000..8f273e86 --- /dev/null +++ b/kfile-plugins/torrent/bint.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <qstring.h> +#include <qcstring.h> +#include <qiodevice.h> + +#include "bytetape.h" +#include "bint.h" + +// A bencoded int is (approximately) as follows: +// i(\d)+e +BInt::BInt (QByteArray &dict, int start) + : m_value (0), m_valid(false) +{ + ByteTape tape (dict, start); + init (tape); +} + +BInt::BInt (ByteTape &tape) + : m_value(0), m_valid(false) +{ + init(tape); +} + +void BInt::init (ByteTape &tape) +{ + if (*tape != 'i') + return; + + tape ++; // Move to start of digits + + QByteArray &dict (tape.data()); + if (dict.find('e', tape.pos()) == -1) + return; + + // Copy the part from the start to the e. The values in-between + // should be digits, perhaps preceded by a negative sign. + int length = dict.find('e', tape.pos()) - tape.pos(); + char *ptr = dict.data(); // Get start of buffer + ptr += tape.pos(); // Advance to current position in tape + + // Allocate temporary data buffer + QByteArray buffer(length + 1); + + qmemmove (buffer.data(), ptr, length); + buffer[length] = 0; // Null-terminate + + QString numberString (buffer); + bool a_isValid; // We want to make sure the string is a valid number + + m_value = numberString.toLongLong(&a_isValid); + + tape += length; // Move to 'e' + tape ++; // Move to next char + + m_valid = a_isValid; // Now we're good, if it was a number +} + +BInt::~BInt() +{ + /* Nothing yet */ +} + +bool BInt::writeToDevice (QIODevice &device) +{ + if (!m_valid) + return false; + + /* Write out i234e, and such */ + QString str = QString("i%1e"). + arg (m_value); + + Q_LONG written = 0, result = 0; + written = device.writeBlock (str.latin1(), str.length()); + while ((uint) written < str.length()) + { + if (written < 0 || result < 0) + return false; + + result = device.writeBlock(str.latin1() + written, + str.length() - written); + written += result; + } + + return true; +} + +// vim: set et ts=4 sw=4: diff --git a/kfile-plugins/torrent/bint.h b/kfile-plugins/torrent/bint.h new file mode 100644 index 00000000..1d4a4614 --- /dev/null +++ b/kfile-plugins/torrent/bint.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _BINT_H +#define _BINT_H + +#include <qcstring.h> +#include "bbase.h" +#include "bytetape.h" + +/** + * Class to represent a b-encoded integer. + * + * @author Michael Pyne <mpyne@grammarian.homelinux.net> + * @see BBase + */ +class BInt : public BBase +{ + public: + + /** + * Constructs a BInt by reading a b-encoded integer from @p dict. + * You can start reading from a position other than the beginning + * by passing that position to the constructor. + * + * @param dict the buffer to read from + * @param start the position within the buffer of the beginning + * of the b-encoded integer. + */ + BInt (QByteArray &dict, int start = 0); + + /** + * Constructs a BInt by reading a b-encoded integer from @p tape. + * + * @param tape the ByteTape to read from. It should already be + * positioned at the beginning of the b-encoded integer data. + * After construction, @p tape will point to the byte after + * the b-encoded integer on success. If construction was + * not successful, @p tape will have an undefined position. + */ + BInt (ByteTape &tape); + + /** + * Destructor for this class. No special action is taken. + */ + virtual ~BInt (); + + /** + * Returns the integer value of the data used to construct this + * object. + * + * @return this object's integer value + */ + Q_LLONG get_value () const { return m_value; } + + /** + * Returns the type of this class. + * + * @return bInt. This value is only returned by this class. + */ + virtual classID type_id() const { return bInt; } + + /** + * This function should be called to determine whether the + * integer was successfully created, since no exceptions + * are thrown. + * + * @return true if this is a valid integer, false otherwise + */ + virtual bool isValid() const { return m_valid; } + + /** + * Outputs the b-encoded representation of the object to the given + * QIODevice. + * @param device the QIODevice to write to + * @return true on a successful write, false otherwise + */ + virtual bool writeToDevice (QIODevice &device); + + private: + + /** + * Initialization function for the class, called to handle the + * actual work of reading the b-encoded data from @p tape. + * + * @param tape the ByteTape to read from + */ + void init(ByteTape &tape); + + Q_LLONG m_value; + bool m_valid; +}; + +#endif /* _BINT_H */ + +// vim: set et ts=4 sw=4: diff --git a/kfile-plugins/torrent/blist.cpp b/kfile-plugins/torrent/blist.cpp new file mode 100644 index 00000000..1aaa8b7c --- /dev/null +++ b/kfile-plugins/torrent/blist.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <qiodevice.h> + +#include "bytetape.h" +#include "blist.h" +#include "bdict.h" +#include "bstring.h" +#include "bint.h" + +BList::BList (ByteTape &tape) + : m_valid(false), m_array() +{ + init (tape); +} + +BList::BList (QByteArray &dict, unsigned int start) + : m_valid(false), m_array() +{ + ByteTape tape (dict, start); + + init (tape); +} + +void BList::init (ByteTape &tape) +{ + BBase *temp; + + if (*tape != 'l') + return; + + tape++; + + /* Repeat circling over the string until the list is over */ + while (*tape != 'e') + { + switch (*tape) + { + case 'd': + temp = new BDict (tape); + break; + + case 'l': /* This element is a list */ + temp = new BList (tape); + break; + + case 'i': /* This element is an int */ + temp = new BInt (tape); + break; + + default: /* Maybe a string? */ + temp = new BString (tape); + } + + if (!temp || !temp->isValid()) + return; // Invalid list element + + m_array.append (temp); + } + + m_valid = true; + + // Only way out is to detect 'e', so we need to increment tape past that. + tape++; +} + +BList::~BList() +{ + BBaseVectorIterator iter; + + for (iter = begin(); iter != end(); ++iter) + delete *iter; +} + +BBase* BList::index (unsigned int i) +{ + if (i >= count()) + return 0; + else + return m_array[i]; +} + +BList * BList::indexList (unsigned int i) +{ + BBase *base = index(i); + + if (base && base->type_id() == bList) + return dynamic_cast<BList*>(base); + + return 0; +} + +BInt * BList::indexInt (unsigned int i) +{ + BBase *base = index(i); + + if (base && base->type_id() == bInt) + return dynamic_cast<BInt*>(base); + + return 0; +} + +BDict * BList::indexDict (unsigned int i) +{ + BBase *base = index(i); + + if (base && base->type_id() == bDict) + return dynamic_cast<BDict*>(base); + + return 0; +} + +BString * BList::indexStr (unsigned int i) +{ + BBase *base = index(i); + + if (base && base->type_id() == bString) + return dynamic_cast<BString*>(base); + + return 0; +} + +bool BList::writeToDevice(QIODevice &device) +{ + if (!m_valid) + return false; + + const char *l_str = "l"; + const char *e_str = "e"; + Q_LONG written = 0, result = 0; + + written = device.writeBlock (l_str, 1); + while (written < 1) + { + if (written < 0 || result < 0) + return false; + + result = device.writeBlock (l_str, 1); + written += result; + } + + BBaseVectorIterator iter; + for (iter = begin(); iter != end(); ++iter) + { + if (!((*iter)->writeToDevice (device))) + return false; + } + + written = device.writeBlock (e_str, 1); + while (written < 1) + { + if (written < 0 || result < 0) + return false; + + result = device.writeBlock (e_str, 1); + written += result; + } + + return true; +} + +// vim: set et sw=4 ts=4: diff --git a/kfile-plugins/torrent/blist.h b/kfile-plugins/torrent/blist.h new file mode 100644 index 00000000..88a52bc4 --- /dev/null +++ b/kfile-plugins/torrent/blist.h @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _BLIST_H +#define _BLIST_H + +#include <qvaluelist.h> +#include <qcstring.h> +#include "bbase.h" +#include "bytetape.h" + +typedef QValueList<BBase *> BBaseVector; +typedef QValueList<BBase *>::iterator BBaseVectorIterator; + +// Predeclare the following classes +class BDict; +class BString; +class BInt; + +/** + * Class to construct a list of BBase objects from a b-encoded + * list. + * + * @author Michael Pyne <mpyne@grammarian.homelinux.net> + * @see BBase + */ +class BList : public BBase +{ + public: + + /** + * Construct a BList from @p dict. + * + * @param dict the buffer to read from + * @param start the position in the buffer to start + * reading from + */ + BList (QByteArray &dict, unsigned int start = 0); + + /** + * Construct a BList from @p tape. Any changes made to + * @p tape, such as its current position and data, will be + * shared with the object that called this constructor. @p tape + * should already be positioned at the position to read from. + * If construction was successful, @p tape will point to the + * byte after the list data. If construction was unsuccessful, + * the position of the tape is undefined. + * + * @param tape the ByteTape to read from. + */ + BList (ByteTape &tape); + + /** + * Destroys the list, and deletes all of the items that had + * been contained within. + */ + virtual ~BList (); + + /** + * Returns the type of this class. + * + * @return bList. This value is only returned by this class. + */ + virtual classID type_id() const { return bList; } + + /** + * Returns the number of items contained within the list. + * + * @return number of items in the list + */ + virtual unsigned int count() const { return m_array.count(); } + + /** + * This function should be called to determine whether the + * list was successfully created, since no exceptions + * are thrown. + * + * @return true if this is a valid list, false otherwise + */ + virtual bool isValid() const { return m_valid; } + + /** + * This function returns a pointer to the appropriate + * item in the list. + * + * @param i index of the item to return + * @return pointer to the appropriate BBase, or 0 if + * the index is out-of-bounds + */ + inline BBase * index (unsigned int i); + + /** + * Convience function to return a pointer to the appropriate + * item in the list, already casted. + * + * @param i index of the item to return + * @return pointer to the appropriate BBase, downcasted to + * BList. If the element is <b>not</b> a BList, 0 + * will be returned instead, even if it was a valid + * BBase. + */ + BList * indexList (unsigned int i); + + /** + * Convience function to return a pointer to the appropriate + * item in the list, already casted. + * + * @param i index of the item to return + * @return pointer to the appropriate BBase, downcasted to + * BInt. If the element is <b>not</b> a BInt, 0 + * will be returned instead, even if it was a valid + * BBase. + */ + BInt * indexInt (unsigned int i); + + /** + * Convience function to return a pointer to the appropriate + * item in the list, already casted. + * + * @param i index of the item to return + * @return pointer to the appropriate BBase, downcasted to + * BDict. If the element is <b>not</b> a BDict, 0 + * will be returned instead, even if it was a valid + * BBase. + */ + BDict * indexDict (unsigned int i); + + /** + * Convience function to return a pointer to the appropriate + * item in the list, already casted. + * + * @param i index of the item to return + * @return pointer to the appropriate BBase, downcasted to + * BString. If the element is <b>not</b> a BString, 0 + * will be returned instead, even if it was a valid + * BBase. + */ + BString * indexStr (unsigned int i); + + /** + * Returns an iterator to the first element in the list. + * There is no particular sorting associated with the list + * at this time. + * + * @return iterator pointing to the beginning of the list + * @see QValueList + */ + BBaseVectorIterator begin(void) { return m_array.begin(); } + + /** + * Returns an iterator pointing one element past the end of + * the list. Although this element belongs to the list, + * you should never dereference this iterator. Instead, treat + * it as a boundary condition to avoid. + * + * @return iterator pointing one element past the end of the list + * @see QValueList + */ + BBaseVectorIterator end(void) { return m_array.end(); } + + /** + * Outputs the b-encoded representation of the object to the given + * QIODevice. + * @param device the QIODevice to write to + * @return true on a successful write, false otherwise + */ + virtual bool writeToDevice (QIODevice &device); + + private: + + /** + * This function handles the actual initialization of the object upon + * construction, and set the m_valid flag if successful. + * + * @param tape the ByteTape to read from + */ + void init(ByteTape &tape); + + bool m_valid; + BBaseVector m_array; +}; + +#endif /* _BLIST_H */ + +// vim: set et sw=4 ts=4: diff --git a/kfile-plugins/torrent/bstring.cpp b/kfile-plugins/torrent/bstring.cpp new file mode 100644 index 00000000..b888af72 --- /dev/null +++ b/kfile-plugins/torrent/bstring.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <qcstring.h> +#include <qiodevice.h> + +#include <kdebug.h> + +#include "bstring.h" +#include "bytetape.h" + +BString::BString (QByteArray &dict, int start) : + m_data(), m_valid(false) +{ + ByteTape tape (dict, start); + init (tape); +} + +BString::BString (ByteTape &tape) + : m_data(), m_valid(false) +{ + init (tape); +} + +// The reason we don't store stuff in a QString is because BitTorrent +// b-encoded strings may contain zeroes within the string, which makes +// a BString more of a buffer than a true string. +void BString::init (ByteTape &tape) +{ + QByteArray &dict(tape.data()); + + if (dict.find(':', tape.pos()) == -1) + { + kdDebug(7034) << "Can't find : for string!" << endl; + return; + } + + // Copy the part from start to :, as it will be a number + // That number is the number of characters to read + int length = dict.find(':', tape.pos()) - tape.pos(); + char *ptr = dict.data(); + + ptr += tape.pos(); + + QByteArray buffer (length + 1); + qmemmove (buffer.data(), ptr, length); + buffer[length] = 0; + + QString numberString (buffer); + bool a_isValid; + ulong len = numberString.toULong (&a_isValid); + + if (!a_isValid) + { + kdDebug(7034) << "Invalid string length!" << endl; + return; + } + + // Now that we have the length, we need to advance the tape + // past the colon + tape += length; // Move to colon + if (*tape != ':') + { + // Sanity check + kdError(7034) << "SANITY CHECK FAILED. *tape != ':'!" << endl; + return; + } + + tape++; // Move past colon + + // Time to copy the data + char *textBuffer = tape.at(tape.pos()); + if (!m_data.resize(len + 1)) + return; + + qmemmove (m_data.data(), textBuffer, len); + m_data[len] = 0; // Null terminate for convienience + + tape += len; + m_valid = true; +} + +BString::~BString () +{ +} + +bool BString::writeToDevice(QIODevice &device) +{ + if (!m_valid) + return false; + + QString str = QString("%1:"). + arg(get_len()); + + QCString utfString = str.utf8(); + + /* Don't write null terminator */ + device.writeBlock (utfString.data(), utfString.size() - 1); + + // Output the actual data + device.writeBlock (m_data.data(), m_data.size() - 1); + + // Done + return true; +} + +bool BString::setValue (const QString &str) +{ + m_data = str.utf8(); + return true; +} + +// vim: set et ts=4 sw=4: diff --git a/kfile-plugins/torrent/bstring.h b/kfile-plugins/torrent/bstring.h new file mode 100644 index 00000000..eb98fef8 --- /dev/null +++ b/kfile-plugins/torrent/bstring.h @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _BSTRING_H +#define _BSTRING_H + +#include <qstring.h> +#include <qcstring.h> + +#include "bytetape.h" +#include "bbase.h" + +/** + * A class to handle the b-encoded strings used by BitTorrent. + * It implements BBase, and although the class is referred to + * as a string, it can hold arbitrary data, since b-encoded strings + * are stored with a length, instead of using a terminator character. + * + * @author Michael Pyne <mpyne@grammarian.homelinux.net> + * @see BBase + */ +class BString : public BBase +{ + public: + + /** + * Construct a BString from @p dict. + * + * @param dict the buffer to read from + * @param start the position in the buffer to start + * reading from + */ + BString (QByteArray &dict, int start = 0); + + /** + * Construct a BString from @p tape. Any changes made to + * @p tape, such as its current position and data, will be + * shared with the object that called this constructor. @p tape + * should already be positioned at the position to read from. + * If construction was successful, @p tape will point to the + * byte after the string data. If construction was unsuccessful, + * the position of the tape is undefined. + * + * @param tape the ByteTape to read from. + */ + BString (ByteTape &tape); + + /** + * Destroys the BString, and deallocates all memory that had + * been used. + */ + virtual ~BString (); + + /** + * Returns a QString representation of the data in the + * BString. It is the responsibility of the caller to ensure + * that the data is convertible to a QString. More specifically, + * the data should not contain any embedded NULLs. + * + * @return QString containing the data from this BString. + */ + QString get_string() const { return QString::fromUtf8(m_data.data()); } + + /** + * Returns the amount of data held by the string. It would be + * perhaps more appropriate to call this size(), since this is + * a buffer, not a true text string. + * + * @return the size of the string, not including the NULL + * terminator. + */ + const int get_len() const { return m_data.size() - 1; } + + /** + * Returns the type of this class. + * + * @return bString. This value is only returned by this class. + */ + virtual classID type_id() const { return bString; } + + /** + * This function should be called to determine whether the + * string was successfully created, since no exceptions + * are thrown. + * + * @return true if this is a valid string, false otherwise + */ + virtual bool isValid() const { return m_valid; } + + /** + * Outputs the b-encoded representation of the object to the given + * QIODevice. + * @param device the QIODevice to write to + * @return true on a successful write, false otherwise + */ + virtual bool writeToDevice (QIODevice &device); + + /** + * Changes the value of the string to the given QString. + * + * @param str the QString containing the new value + * @return true if the value was successfully changed, + * false otherwise. + */ + bool setValue (const QString &str); + + private: + + /** + * This function handles the actual initialization of the object upon + * construction, and set the m_valid flag if successful. + * + * @param tape the ByteTape to read from + */ + void init (ByteTape &tape); + + QByteArray m_data; + bool m_valid; +}; + +#endif /* _BSTRING_H */ + +// vim: set et ts=4 sw=4: diff --git a/kfile-plugins/torrent/bytetape.cpp b/kfile-plugins/torrent/bytetape.cpp new file mode 100644 index 00000000..f2289293 --- /dev/null +++ b/kfile-plugins/torrent/bytetape.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <kdebug.h> + +#include "bytetape.h" + +ByteTape::ByteTape (QByteArray &array, int pos) + : m_array(array), m_shared(new ByteTapeShared) +{ + m_shared->pos = pos; +} + +ByteTape::ByteTape (const ByteTape &tape) + : m_array(tape.m_array), m_shared(tape.m_shared) +{ +} + +ByteTape& ByteTape::operator += (const unsigned int i) +{ + m_shared->pos += i; + if (m_array.size() <= m_shared->pos) + { + kdDebug(7034) << "Whoops. Advanced too far." << endl; + m_shared->pos = m_array.size() - 1; + } + + return *this; +} + +ByteTape& ByteTape::operator -= (const unsigned int i) +{ + if (i > m_shared->pos) + { + kdDebug(7034) << "Whoops, tried to back up too far." << endl; + m_shared->pos = 0; + } + else + m_shared->pos -= i; + + return *this; +} + +char ByteTape::operator [] (const unsigned int i) +{ + if (i < m_array.size()) + return m_array[i]; + else + { + kdWarning() << "Can't dereference tape at " << i + << ", size is " << m_array.size() << endl; + return 0; + } +} + +char &ByteTape::operator * () +{ + return m_array[m_shared->pos]; +} + +// Postfix increment +ByteTape ByteTape::operator ++ (int) +{ + // Can't use copy ctor, as we'll be copying shared data, which defeats + // the semantics of the postfix operator. + ByteTape temp(m_array, m_shared->pos); + m_shared->pos ++; + + if (m_shared->pos >= m_array.size()) + { + m_shared->pos = m_array.size() - 1; + kdDebug(7034) << "Tape already at end!" << endl; + kdDebug(7034) << "Tape size is " << m_array.size() << endl; + } + + return temp; +} + +// Prefix increment +ByteTape & ByteTape::operator ++() +{ + m_shared->pos ++; + if (m_shared->pos >= m_array.size()) + { + m_shared->pos = m_array.size() - 1; + kdDebug(7034) << "Tape already at end!" << endl; + kdDebug(7034) << "Tape size is " << m_array.size() << endl; + } + + return *this; +} + +// Postfix decrement +ByteTape ByteTape::operator -- (int) +{ + // Can't use copy ctor, as we'll be copying shared data, which defeats + // the semantics of the postfix operator. + ByteTape temp(m_array, m_shared->pos); + + if (m_shared->pos != 0) + m_shared->pos --; + else + kdDebug(7034) << "Tape already at beginning!" << endl; + + return temp; +} + +// Prefix decrement +ByteTape & ByteTape::operator -- () +{ + if (m_shared->pos != 0) + m_shared->pos --; + else + kdDebug(7034) << "Tape already at beginning!" << endl; + + return *this; +} + +bool ByteTape::setPos (unsigned int pos) +{ + if (pos >= m_array.size()) + { + kdDebug(7034) << "Can't set tape to " << pos << endl; + return false; + } + + m_shared->pos = pos; + return true; +} + +char* ByteTape::at (const unsigned int i) +{ + if (i >= m_array.size()) + { + kdDebug(7034) << "Access to tape at " << i << " out-of-range." << endl; + return 0; + } + + return m_array.data() + i; +} + +// vim: set et ts=4 sw=4: diff --git a/kfile-plugins/torrent/bytetape.h b/kfile-plugins/torrent/bytetape.h new file mode 100644 index 00000000..1d8c8db2 --- /dev/null +++ b/kfile-plugins/torrent/bytetape.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _BYTETAPE_H +#define _BYTETAPE_H + +#include <ksharedptr.h> + +#include <qcstring.h> + +class ByteTapeShared : public KShared +{ + public: + + unsigned int pos; +}; + +/** + * Class to simulate a seekable byte stream. Very similar to QByteArray, + * but the difference is that this class "knows" what a current position + * is. Also, the copy constructor will share the byte stream of the + * ByteTape being copied. This means that any copies made of an object of + * this class will share BOTH the data and the current position. This is + * by design. + * + * @author Michael Pyne <mpyne@grammarian.homelinux.net> + * @see QByteArray + */ +class ByteTape +{ + public: + + /** + * Constructs a new ByteTape object from @p array. The + * current position will be set to @p pos. + * + * @param array a data buffer to use for reading and writing. + * The tape will be fixed to the size of this buffer. + * @param pos the initial position of the tape head. It must be + * a position within the buffer contained in @p array. + */ + ByteTape (QByteArray &array, int pos = 0); + + /** + * Creates a ByteTape as a copy of @p tape. The newly created + * object will share both data and the current position with @p tape. + * This is done using reference counts. + * + * @param tape the ByteTape to copy + */ + ByteTape (const ByteTape &tape); + + /** + * Increments the current position by @p i. It is the responsibility + * of the function caller to ensure that the new position is in bounds. + * The position will be capped to remain in bounds regardless. + * + * @param i the amount to increment the current position by + * @return t reference to the object incremented + */ + ByteTape & operator += (const unsigned int i); + + /** + * Decrements the current position by @p i. It is the responsibility + * of the function caller to ensure that the new position is in bounds. + * Unlike a real Turing machine, attempting to decrement past the + * start will be capped to the beginning instead of crashing the + * computer. + * + * @param i the amount to decrement the current position by + * @return a reference to the object decremented + */ + ByteTape & operator -= (const unsigned int i); + + /** + * Increments the current position by 1. This is a postfix + * operator (i++). The current position will be capped to the end + * of the buffer. + * + * @return the object before the increment operation + */ + ByteTape operator ++ (int); + + /** + * Increments the current position by 1. This is a prefix + * operator (++i). The current position will be capped to the end + * of the buffer. + * + * @return a reference to the object incremented + */ + ByteTape & operator ++ (); + + /** + * Decrements the current position by 1. This is a postfix + * operator (i--). The current position will be capped to the start + * of the buffer. + * + * @return the object before the decrement operation + */ + ByteTape operator -- (int); + + /** + * Decrements the current position by 1. This is a prefix + * operator (--i). The current position will be capped to the start + * of the buffer. + * + * @return a reference to the object decremented + */ + ByteTape & operator -- (); + + /** + * Returns the byte within the array indexed by @p i. It is + * the responsibility of the caller to ensure that @p i is in bounds. + * Although out-of-range errors will be detected, no exceptions will + * be thrown. Since a reference is not returned, you won't be able + * to assign to the result. Example: tape[i] = 'a' will not work. + * + * The reason a reference isn't returned is because no exceptions are + * thrown by this class, and we can't return a reference to an out-of- + * bounds character. + * + * @param i the index of the byte to return + * @return the byte at the given index. 0 may be returned on error, + * but does not necessarily indicate an error. + */ + char operator [] (const unsigned int i); + + /** + * Returns the byte at the tape's current position. You can assign + * to the reference returned. + * + * @return a reference to the byte at the tape head's current position + */ + char &operator * (); + + /** + * Returns the position in memory of data at the given index, @p i. + * Unlike operator [], this function returns a pointer, so it can be + * used to access memory. + * + * @param i index of the byte to lookup. + * @return 0 if @p i is out of range, else the address of memory + * at that index + */ + char *at(const unsigned int i); + + /** + * Returns the current position of the tape head. + * + * @return the tape head's current position + */ + unsigned int pos() const { return m_shared->pos; } + + /** + * Sets the current position of the tape head to @p pos. If the + * position given is out-of-bounds, it will be capped to be within + * the array. + * + * @param pos the new position of the tape head + * @return whether the set operation was successful + */ + bool setPos(unsigned int pos); + + /** + * Returns a reference to the QByteArray used to hold all the data. + * + * @return the QByteArray used to hold the data + * @see QByteArray + */ + QByteArray &data() { return m_array; } + + private: + QByteArray &m_array; + KSharedPtr<ByteTapeShared> m_shared; +}; + +#endif /* _BYTETAPE_H */ + +// vim: set et ts=4 sw=4: diff --git a/kfile-plugins/torrent/kfile_torrent.cpp b/kfile-plugins/torrent/kfile_torrent.cpp new file mode 100644 index 00000000..08007e11 --- /dev/null +++ b/kfile-plugins/torrent/kfile_torrent.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2003, 2004 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <qdatetime.h> +#include <qfile.h> +#include <qregexp.h> +#include <qdir.h> +#include <qstringlist.h> + +#include <kmessagebox.h> +#include <kgenericfactory.h> + +#include "kfile_torrent.h" +#include "bdict.h" +#include "blist.h" +#include "bint.h" +#include "bstring.h" + +typedef KGenericFactory<KTorrentPlugin> TorrentFactory; +K_EXPORT_COMPONENT_FACTORY(kfile_torrent, TorrentFactory("kfile_torrent")) + +QStringList filesList (BList *list); +Q_ULLONG filesLength (BList *list); + +KTorrentPlugin::KTorrentPlugin (QObject *parent, const char *name, + const QStringList &args) + : KFilePlugin (parent, name, args), m_failed(true), m_dict(0) +{ + KFileMimeTypeInfo *info = addMimeTypeInfo ("application/x-bittorrent"); + if (!info) + { + kdError() << "Error creating application/x-bittorrent mime type info!\n"; + return; + } + + KFileMimeTypeInfo::GroupInfo* group = + addGroupInfo (info, "TorrentInfo", i18n("Torrent Information")); + if (!group) + { + kdError() << "Error creating TorrentInfo group!\n"; + return; + } + setAttributes (group, KFileMimeTypeInfo::Modifiable); + + KFileMimeTypeInfo::ItemInfo *item = 0; + + item = addItemInfo(group, "name", i18n("Name"), QVariant::String); + if (!item) + { + kdError() << "Error adding Name to group!\n"; + return; + } + setHint (item, KFileMimeTypeInfo::Name); + setAttributes (item, KFileMimeTypeInfo::Modifiable); + + item = addItemInfo(group, "length", i18n("Torrent Length"), QVariant::ULongLong); + if (!item) + { + kdError() << "Error adding Length to group!\n"; + return; + } + setHint (item, KFileMimeTypeInfo::Length); + setUnit (item, KFileMimeTypeInfo::Bytes); + + item = addItemInfo(group, "announce", i18n("Tracker URL"), QVariant::String); + if (!item) + { + kdError() << "Error adding Announce to group!\n"; + return; + } + + item = addItemInfo(group, "creation date", i18n("Date Created"), QVariant::DateTime); + if (!item) + { + kdError() << "Error adding DateCreated to group!\n"; + return; + } + + item = addItemInfo(group, "NumFiles", i18n("Number of Files"), QVariant::Int); + if (!item) + { + kdError() << "Error adding NumFiles to group!\n"; + return; + } + + item = addItemInfo(group, "piece length", i18n("File Piece Length"), QVariant::Int); + if (!item) + { + kdError() << "Error adding PieceLength to group!\n"; + return; + } + setUnit (item, KFileMimeTypeInfo::Bytes); + + item = addItemInfo(group, "comment", i18n("Comment"), QVariant::String); + if (!item) + { + kdError() << "Error adding Comment to group!\n"; + return; + } + setAttributes (item, KFileMimeTypeInfo::MultiLine | KFileMimeTypeInfo::Modifiable); + + m_failed = false; +} + +bool KTorrentPlugin::readInfo (KFileMetaInfo &info, unsigned int) +{ + /* Since we don't throw during the ctor, check here whether we actually + * are valid are not. If not, die. + */ + if (m_failed) + { + kdError() << "Construction of KTorrentPlugin failed for " << info.path() << endl; + kdError() << "Aborting meta-info read.\n"; + return false; + } + + QFile file (info.path()); + if (!file.open(IO_ReadOnly)) + { + kdError() << "Unable to open given file!\n"; + return false; + } + + // We need to read in the entire file to parse the dictionary structure. + QByteArray buf = file.readAll(); + file.close(); + + if (!buf) + { + kdError() << "Empty file: " << info.path() << endl; + return false; + } + + m_dict = new BDict(buf); + + if (!m_dict) + { + kdError() << "Error creating dictionary from open file: " << info.path() << endl; + return false; + } + + if (!m_dict->isValid()) + { + kdDebug(7034) << "Invalid torrent file: " << info.path() << endl; + return false; + } + + KFileMetaInfoGroup group = appendGroup(info, "TorrentInfo"); + + // The remainder of this function will consist of a lot of redundancy checks. + // If a torrent has a key, but it is of the wrong type, then it isn't a valid + // torrent, and so we should just die. + + if (m_dict->contains("announce")) + { + BString *str = m_dict->findStr ("announce"); + if (!str) + return false; + appendItem (group, "announce", QString(str->get_string())); + } + + if (m_dict->contains("creation date")) + { + BInt *the_data = m_dict->findInt ("creation date"); + QDateTime my_date; + + if (!the_data) + return false; + + unsigned int the_time = the_data->get_value(); + + /* Hopefully the_time is UTC, because that's what QDateTime does. */ + my_date.setTime_t (the_time); + appendItem (group, "creation date", my_date); + } + + // A valid torrent must have the info dict, no reason to check twice for + // it. + BDict *info_dict = m_dict->findDict("info"); + int num_files = 1; + Q_ULLONG length = 0; + + if (!info_dict) + return false; + + if (!info_dict->contains("length")) + { + /* Has more than one file. The list of files is contained in a + * list called, appropriately enough, 'files' + */ + BList *info_list = info_dict->findList("files"); + if (!info_list) + return false; + + num_files = info_list->count(); + length = filesLength (info_list); + } + else + { + /* Only one file, let's put its length */ + BInt *blength = info_dict->findInt("length"); + if (!blength) + return false; + + length = blength->get_value(); + } + + appendItem (group, "NumFiles", num_files); + appendItem (group, "length", length); + + if (info_dict->contains("name")) + { + BString *str = info_dict->findStr("name"); + if (!str) + return false; + + QString real_str (str->get_string()); + + if (num_files > 1 && !real_str.endsWith("/")) + real_str.append('/'); + + appendItem (group, "name", real_str); + } + + // piece length is required as well + BInt *piece_length = info_dict->findInt("piece length"); + if (!piece_length) + return false; + + appendItem (group, "piece length", piece_length->get_value()); + + if (m_dict->contains("comment")) + { + BString *comment = m_dict->findStr("comment"); + if (!comment) + return false; + + appendItem (group, "comment", comment->get_string()); + } + else + appendItem (group, "comment", QString()); + + return true; +} + +/* Returns a QStringList containing file names within the list. The list + * should be the one contained within the info dictionary of the torrent, + * keyed by 'files' + */ +QStringList filesList (BList *list) +{ + QStringList str_list, failList; + + for (unsigned int i = 0; i < list->count(); ++i) + { + /* Each item in this list is a dictionary, composed as follows: + * length -> BInt (size of file) + * path -> BList (list of strings) + * The list of strings is used to construct directory paths. The + * last element of the list is the file name. + */ + + BDict *list_dict = list->indexDict(i); + if (!list_dict) + return failList; + + BList *list_path = list_dict->findList("path"); + if (!list_path) + return failList; + + QString str; + BString *temp_str; + + if (list_path->count() > 0) + { + temp_str = list_path->indexStr (0); + if (!temp_str) + return failList; + + str.append (temp_str->get_string()); + } + + /* Construct QString consisting of path and file name */ + for (unsigned int j = 1; j < list_path->count(); ++j) + { + str.append (QDir::separator()); + temp_str = list_path->indexStr (j); + if (!temp_str) + return failList; + + str.append (temp_str->get_string()); + } + + str_list += str; + } + + return str_list; +} + +/* This function determines the total length of a torrent stream. + * The list provided should be the same one provided for filesList. + */ +Q_ULLONG filesLength (BList *list) +{ + Q_ULLONG length = 0; + + for (unsigned int i = 0; i < list->count(); ++i) + { + /* Each item in this list is a dictionary, composed as follows: + * length -> BInt (size of file) + * path -> BList (list of strings) + */ + + BDict *list_dict = list->indexDict(i); + if (!list_dict) + return 0; + + BInt *bfile_len = list_dict->findInt("length"); + if (!bfile_len) + return 0; + + length += bfile_len->get_value(); + } + + return length; +} + +bool KTorrentPlugin::writeInfo(const KFileMetaInfo &info) const +{ + if (m_failed || !m_dict) + return false; + + // The m_dict is ready, all we have to do is open a file, and + // let 'er go. + QStringList list = info.groups(); + QStringList::Iterator it = list.begin(); + + for (; it != list.end(); ++it) + { + QStringList list2 = info[*it].keys(); + QStringList::Iterator it2 = list2.begin(); + + for (; it2 != list2.end(); ++it2) + { + QString key = *it2; + + if (info[*it][key].isModified()) + { + // Re-enter the entry in the dictionary. + if (key == "comment") + { + BString *b_str = m_dict->findStr("comment"); + if (!b_str) + return false; + + b_str->setValue (info[*it][key].value().toString()); + } + else if (key == "name") + { + BDict *info_dict = m_dict->findDict ("info"); + if (!info_dict) + return false; + + BString *name_str = info_dict->findStr ("name"); + if (!name_str) + return false; + + QString the_name = info[*it][key].value().toString(); + + // Remove trailing slashes + the_name.replace (QRegExp("/*$"), ""); + + name_str->setValue (the_name); + } + } + } + } + + QFile output (info.path()); + + if (!output.open(IO_WriteOnly | IO_Truncate)) + return false; + + return m_dict->writeToDevice(output); +} + +#include "kfile_torrent.moc" + +// vim: set ts=4 sw=4 et: diff --git a/kfile-plugins/torrent/kfile_torrent.desktop b/kfile-plugins/torrent/kfile_torrent.desktop new file mode 100644 index 00000000..854985d4 --- /dev/null +++ b/kfile-plugins/torrent/kfile_torrent.desktop @@ -0,0 +1,58 @@ +[Desktop Entry] +Type=Service +Name=Bit Torrent Info +Name[be]=Звесткі Bit Torrent +Name[bn]=বিট টোরেন্ট তথ্য +Name[br]=Titouriñ diwar-benn Bit Torrent +Name[bs]=Bit Torrent informacije +Name[ca]=Informació de Bit Torrent +Name[cs]=Bit Torrent informace +Name[da]=Info om bittorrent +Name[de]=Bit Torrent Informationen +Name[el]=Πληροφορίες Bit Torrent +Name[es]=Información Bit Torrent +Name[et]=Bit Torrenti info +Name[eu]=Bit-torrent informazioa +Name[fa]=اطلاعات جریان بیتی +Name[fi]=Bit Torrent +Name[fr]=Infos Bit Torrent +Name[gl]=Información de Bit Torrent +Name[he]=מידע ביטטורנט +Name[hu]=Bit Torrent-jellemzők +Name[is]=Bit Torrent upplýsingar +Name[it]=Informazioni Bittorrent +Name[ja]=Bit Torrent 情報 +Name[ka]=Bit Torrent ინფორმაცია +Name[kk]=Bit Torrent мәліметі +Name[km]=ព័ត៌មាន Bit Torrent +Name[lt]=Bit Torrent Informacija +Name[mk]=Информации за Bit Torrent +Name[nds]=BitTorrent-Info +Name[ne]=बिट टोरेन्ट सूचना +Name[nl]=Bittorent-informatie +Name[nn]=Bit Torrent-info +Name[pa]=Bit Torrent ਜਾਣਕਾਰੀ +Name[pl]=Informacja o Bit Torrent +Name[pt]=Informações Bit Torrent +Name[pt_BR]=Informações do Bit Torrent +Name[ro]=Informaţii Bit Torrent +Name[ru]=Сведения о BitTorrent +Name[sk]=Bit Torrent informácie +Name[sl]=Podatki o BitTorrentu +Name[sr]=Bit Torrent информације +Name[sr@Latn]=Bit Torrent informacije +Name[sv]=Information om Bit Torrent +Name[ta]=பிட் டோர்ரெண்ட் தகவல் +Name[tr]=Bit Torrent Bilgisi +Name[uk]=Інформація про Bit Torrent +Name[zh_CN]=BitTorrent 信息 +Name[zh_HK]=Bit Torrent 資訊 +Name[zh_TW]=Bit Torrent 資訊 +ServiceTypes=KFilePlugin +X-KDE-Library=kfile_torrent +# change MimeType here! (example: inode/directory) +MimeType=application/x-bittorrent +# change PreferredGroups here! (example: FolderInfo) +PreferredGroups= +# change PreferredItems here! (example: Items;Size) +PreferredItems= diff --git a/kfile-plugins/torrent/kfile_torrent.h b/kfile-plugins/torrent/kfile_torrent.h new file mode 100644 index 00000000..3e6d3ec4 --- /dev/null +++ b/kfile-plugins/torrent/kfile_torrent.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net> + * + * This software 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 library; see the file COPYING. + * If not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _KFILE_TORRENT_H +#define _KFILE_TORRENT_H + +#include <kfilemetainfo.h> + +#include "bdict.h" + +class QStringList; + +/** + * Class to provide meta info for BitTorrent files within Konqueror. + * Handles the mime type application/x-bittorrent, files typically + * have the extension .torrent + * + * @author Michael Pyne <mpyne@grammarian.homelinux.net> + * @todo Handle editing the meta info as well + * @bug The comment meta info should be Multi Line, but that only + * works when the comment can also be edited. + */ +class KTorrentPlugin : public KFilePlugin +{ + Q_OBJECT + + public: + /** + * Constructs the class, and prepares for reading info on a torrent. + * + * @param parent the parent of this object + * @param name the name of this object (not user-readable) + * @param args unused by this class + */ + KTorrentPlugin (QObject *parent, const char *name, const QStringList &args); + + /** + * Destructor that closes the dictionary holding the torrent information. + */ + ~KTorrentPlugin () { delete m_dict; } + + /** + * Reads information on a torrent file given by @p info. + * + * @param info information on the file to decode + * @return true if the meta info was successfully detected and added, + * false otherwise. + */ + virtual bool readInfo (KFileMetaInfo& info, unsigned int); + + /** + * Writes information on a torrent file given by @p info. + * BitTorrent are practically nothing but meta information. + * Therefore, the entire file might be changed. + * + * @param info information on the file to encode + * @return true if the meta info was successfully updated, + * false otherwise + */ + virtual bool writeInfo (const KFileMetaInfo& info) const; + + private: + bool m_failed; + BDict *m_dict; +}; + +#endif /* _KFILE_TORRENT_H */ + +// vim: set et ts=4 sw=4: |