|
|
|
/*
|
|
|
|
* 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.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
#include <tqdatetime.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
#include <tqdir.h>
|
|
|
|
#include <tqstringlist.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"))
|
|
|
|
|
|
|
|
TQStringList filesList (BList *list);
|
|
|
|
Q_ULLONG filesLength (BList *list);
|
|
|
|
|
|
|
|
KTorrentPlugin::KTorrentPlugin (TQObject *parent, const char *name,
|
|
|
|
const TQStringList &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"), TQVariant::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"), TQVariant::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"), TQVariant::String);
|
|
|
|
if (!item)
|
|
|
|
{
|
|
|
|
kdError() << "Error adding Announce to group!\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
item = addItemInfo(group, "creation date", i18n("Date Created"), TQVariant::DateTime);
|
|
|
|
if (!item)
|
|
|
|
{
|
|
|
|
kdError() << "Error adding DateCreated to group!\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
item = addItemInfo(group, "NumFiles", i18n("Number of Files"), TQVariant::Int);
|
|
|
|
if (!item)
|
|
|
|
{
|
|
|
|
kdError() << "Error adding NumFiles to group!\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
item = addItemInfo(group, "piece length", i18n("File Piece Length"), TQVariant::Int);
|
|
|
|
if (!item)
|
|
|
|
{
|
|
|
|
kdError() << "Error adding PieceLength to group!\n";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setUnit (item, KFileMimeTypeInfo::Bytes);
|
|
|
|
|
|
|
|
item = addItemInfo(group, "comment", i18n("Comment"), TQVariant::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;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQFile 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.
|
|
|
|
TQByteArray 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", TQString(str->get_string()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_dict->contains("creation date"))
|
|
|
|
{
|
|
|
|
BInt *the_data = m_dict->findInt ("creation date");
|
|
|
|
TQDateTime my_date;
|
|
|
|
|
|
|
|
if (!the_data)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
unsigned int the_time = the_data->get_value();
|
|
|
|
|
|
|
|
/* Hopefully the_time is UTC, because that's what TQDateTime 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;
|
|
|
|
|
|
|
|
TQString 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", TQString());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns a TQStringList containing file names within the list. The list
|
|
|
|
* should be the one contained within the info dictionary of the torrent,
|
|
|
|
* keyed by 'files'
|
|
|
|
*/
|
|
|
|
TQStringList filesList (BList *list)
|
|
|
|
{
|
|
|
|
TQStringList 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;
|
|
|
|
|
|
|
|
TQString 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 TQString consisting of path and file name */
|
|
|
|
for (unsigned int j = 1; j < list_path->count(); ++j)
|
|
|
|
{
|
|
|
|
str.append (TQDir::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.
|
|
|
|
TQStringList list = info.groups();
|
|
|
|
TQStringList::Iterator it = list.begin();
|
|
|
|
|
|
|
|
for (; it != list.end(); ++it)
|
|
|
|
{
|
|
|
|
TQStringList list2 = info[*it].keys();
|
|
|
|
TQStringList::Iterator it2 = list2.begin();
|
|
|
|
|
|
|
|
for (; it2 != list2.end(); ++it2)
|
|
|
|
{
|
|
|
|
TQString 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;
|
|
|
|
|
|
|
|
TQString the_name = info[*it][key].value().toString();
|
|
|
|
|
|
|
|
// Remove trailing slashes
|
|
|
|
the_name.replace (TQRegExp("/*$"), "");
|
|
|
|
|
|
|
|
name_str->setValue (the_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TQFile 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:
|