summaryrefslogtreecommitdiffstats
path: root/kfile-plugins/torrent
diff options
context:
space:
mode:
Diffstat (limited to 'kfile-plugins/torrent')
-rw-r--r--kfile-plugins/torrent/Makefile.am22
-rw-r--r--kfile-plugins/torrent/README7
-rw-r--r--kfile-plugins/torrent/bbase.h91
-rw-r--r--kfile-plugins/torrent/bdict.cpp220
-rw-r--r--kfile-plugins/torrent/bdict.h203
-rw-r--r--kfile-plugins/torrent/bint.cpp104
-rw-r--r--kfile-plugins/torrent/bint.h111
-rw-r--r--kfile-plugins/torrent/blist.cpp178
-rw-r--r--kfile-plugins/torrent/blist.h200
-rw-r--r--kfile-plugins/torrent/bstring.cpp128
-rw-r--r--kfile-plugins/torrent/bstring.h138
-rw-r--r--kfile-plugins/torrent/bytetape.cpp158
-rw-r--r--kfile-plugins/torrent/bytetape.h194
-rw-r--r--kfile-plugins/torrent/kfile_torrent.cpp404
-rw-r--r--kfile-plugins/torrent/kfile_torrent.desktop58
-rw-r--r--kfile-plugins/torrent/kfile_torrent.h84
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: