diff options
-rw-r--r-- | CLA | 145 | ||||
-rw-r--r-- | LICENSE | 136 | ||||
-rw-r--r-- | docs/system/pyload_DataLayout.png | bin | 48354 -> 57771 bytes | |||
-rw-r--r-- | module/FileManager.py | 26 | ||||
-rw-r--r-- | module/PyFile.py | 40 | ||||
-rw-r--r-- | module/PyPackage.py | 42 | ||||
-rw-r--r-- | module/database/DatabaseBackend.py | 48 | ||||
-rw-r--r-- | module/database/FileDatabase.py | 184 | ||||
-rw-r--r-- | module/lib/hg_tool.py | 133 | ||||
-rw-r--r-- | module/remote/socketbackend/ttypes.py | 16 | ||||
-rw-r--r-- | module/remote/thriftbackend/pyload.thrift | 48 | ||||
-rw-r--r-- | module/remote/thriftbackend/thriftgen/pyload/Pyload.py | 2 | ||||
-rw-r--r-- | module/remote/thriftbackend/thriftgen/pyload/ttypes.py | 52 | ||||
-rw-r--r-- | module/web/pyload_app.py | 6 | ||||
-rw-r--r-- | module/web/utils.py | 2 | ||||
-rw-r--r-- | pavement.py | 4 | ||||
-rwxr-xr-x | pyLoadCore.py | 21 | ||||
-rw-r--r-- | tests/test_database.py | 59 |
18 files changed, 667 insertions, 297 deletions
@@ -0,0 +1,145 @@ +Thank you for your interest in contributing to pyLoad ("We" or "Us"). + +This contributor agreement ("Agreement") documents the rights granted by +contributors to Us. To make this document effective, please sign it and send it +to Us by email, following the instructions at http://pyload.org/contributing. +This is a legally binding document, so please read it carefully before agreeing +to it. The Agreement may cover more than one software project managed by Us. +1. Definitions + +"You" means the individual who Submits a Contribution to Us. + +"Contribution" means any work of authorship that is Submitted by You to Us in +which You own or assert ownership of the Copyright. If You do not own the +Copyright in the entire work of authorship, please follow the instructions in +http://pyload.org/contributing. + +"Copyright" means all rights protecting works of authorship owned or controlled +by You, including copyright, moral and neighboring rights, as appropriate, for +the full term of their existence including any extensions by You. + +"Material" means the work of authorship which is made available by Us to third +parties. When this Agreement covers more than one software project, the Material +means the work of authorship to which the Contribution was Submitted. After You +Submit the Contribution, it may be included in the Material. + +"Submit" means any form of electronic, verbal, or written communication sent to +Us or our representatives, including but not limited to electronic mailing +lists, source code control systems, and issue tracking systems that are managed +by, or on behalf of, Us for the purpose of discussing and improving the +Material, but excluding communication that is conspicuously marked or otherwise +designated in writing by You as "Not a Contribution." + +"Submission Date" means the date on which You Submit a Contribution to Us. + +"Effective Date" means the date You execute this Agreement or the date You first +Submit a Contribution to Us, whichever is earlier. +2. Grant of Rights +2.1 Copyright License + +(a) You retain ownership of the Copyright in Your Contribution and have the same +rights to use or license the Contribution which You would have had without +entering into the Agreement. + +(b) To the maximum extent permitted by the relevant law, You grant to Us a +perpetual, worldwide, non-exclusive, transferable, royalty-free, irrevocable +license under the Copyright covering the Contribution, with the right to +sublicense such rights through multiple tiers of sublicensees, to reproduce, +modify, display, perform and distribute the Contribution as part of the +Material; provided that this license is conditioned upon compliance with Section +2.3. +2.2 Patent License + +For patent claims including, without limitation, method, process, and apparatus +claims which You own, control or have the right to grant, now or in the future, +You grant to Us a perpetual, worldwide, non-exclusive, transferable, +royalty-free, irrevocable patent license, with the right to sublicense these +rights to multiple tiers of sublicensees, to make, have made, use, sell, offer +for sale, import and otherwise transfer the Contribution and the Contribution in +combination with the Material (and portions of such combination). This license +is granted only to the extent that the exercise of the licensed rights infringes +such patent claims; and provided that this license is conditioned upon +compliance with Section 2.3. +2.3 Outbound License + +Based on the grant of rights in Sections 2.1 and 2.2, if We include Your +Contribution in a Material, We may license the Contribution under any license, +including copyleft, permissive, commercial, or proprietary licenses. As a +condition on the exercise of this right, We agree to also license the +Contribution under the terms of the license or licenses which We are using for +the Material on the Submission Date. + +2.4 Moral Rights. If moral rights apply to the Contribution, to the maximum +extent permitted by law, You waive and agree not to assert such moral rights +against Us or our successors in interest, or any of our licensees, either direct +or indirect. + +2.5 Our Rights. You acknowledge that We are not obligated to use Your +Contribution as part of the Material and may decide to include any Contribution +We consider appropriate. + +2.6 Reservation of Rights. Any rights not expressly licensed under this section +are expressly reserved by You. +3. Agreement + +You confirm that: + +(a) You have the legal authority to enter into this Agreement. + +(b) You own the Copyright and patent claims covering the Contribution which are +required to grant the rights under Section 2. + +(c) The grant of rights under Section 2 does not violate any grant of rights +which You have made to third parties, including Your employer. If You are an +employee, You have had Your employer approve this Agreement or sign the Entity +version of this document. If You are less than eighteen years old, please have +Your parents or guardian sign the Agreement. + +(d) You have followed the instructions in http://pyload.org/contributing, if You +do not own the Copyright in the entire work of authorship Submitted. +4. Disclaimer + +EXCEPT FOR THE EXPRESS WARRANTIES IN SECTION 3, THE CONTRIBUTION IS PROVIDED "AS +IS". MORE PARTICULARLY, ALL EXPRESS OR IMPLIED WARRANTIES INCLUDING, WITHOUT +LIMITATION, ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY DISCLAIMED BY YOU TO US. TO THE +EXTENT THAT ANY SUCH WARRANTIES CANNOT BE DISCLAIMED, SUCH WARRANTY IS LIMITED +IN DURATION TO THE MINIMUM PERIOD PERMITTED BY LAW. +5. Consequential Damage Waiver + +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL YOU BE +LIABLE FOR ANY LOSS OF PROFITS, LOSS OF ANTICIPATED SAVINGS, LOSS OF DATA, +INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL AND EXEMPLARY DAMAGES ARISING OUT +OF THIS AGREEMENT REGARDLESS OF THE LEGAL OR EQUITABLE THEORY (CONTRACT, TORT OR +OTHERWISE) UPON WHICH THE CLAIM IS BASED. +6. Miscellaneous + +6.1 This Agreement will be governed by and construed in accordance with the laws +of excluding its conflicts of law provisions. Under certain circumstances, the +governing law in this section might be superseded by the United Nations +Convention on Contracts for the International Sale of Goods ("UN Convention") +and the parties intend to avoid the application of the UN Convention to this +Agreement and, thus, exclude the application of the UN Convention in its +entirety to this Agreement. + +6.2 This Agreement sets out the entire agreement between You and Us for Your +Contributions to Us and overrides all other agreements or understandings. + +6.3 If You or We assign the rights or obligations received through this +Agreement to a third party, as a condition of the assignment, that third party +must agree in writing to abide by all the rights and obligations in the +Agreement. + +6.4 The failure of either party to require performance by the other party of any +provision of this Agreement in one situation shall not affect the right of a +party to require such performance at any time in the future. A waiver of +performance under a provision in one situation shall not be considered a waiver +of the performance of the provision in the future or a waiver of the provision +in its entirety. + +6.5 If any provision of this Agreement is found void and unenforceable, such +provision will be replaced to the extent possible with a provision that comes +closest to the meaning of the original provision and which is enforceable. The +terms and conditions set forth in this Agreement shall apply notwithstanding any +failure of essential purpose of this Agreement or any limited remedy to the +maximum extent possible under law.
\ No newline at end of file @@ -1,5 +1,27 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 +pyLoad - download manager +Copyright(c) 2008-2012 pyLoad Team +All rights reserved. +licensing@pyload.org + +Open Source License +------------------- + +You are allowed to use this software under the terms of the GNU Affero +General Public License as published by the Free Software Foundation; +either version 3 of the License, or (at your option) any later version. +A copy of the GNU Affero General Public License can be found below. + +Alternative License +------------------- + +With an explicit permission of the authors you may use or distribute +this software under a different license according to the agreement. +Please contact licensing@pyload.org for further information. + +-- + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> Everyone is permitted to copy and distribute verbatim copies @@ -7,17 +29,15 @@ Preamble - The GNU General Public License is a free, copyleft license for -software and other kinds of works. + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to +our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. +software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you @@ -26,44 +46,34 @@ them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. The precise terms and conditions for copying, distribution and modification follow. @@ -72,7 +82,7 @@ modification follow. 0. Definitions. - "This License" refers to version 3 of the GNU General Public License. + "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. @@ -549,35 +559,45 @@ to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. - 13. Use with the GNU Affero General Public License. + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single +under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General +Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published +GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's +versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. diff --git a/docs/system/pyload_DataLayout.png b/docs/system/pyload_DataLayout.png Binary files differindex f620e62c6..98ab31a69 100644 --- a/docs/system/pyload_DataLayout.png +++ b/docs/system/pyload_DataLayout.png diff --git a/module/FileManager.py b/module/FileManager.py index cb3b2ae96..5a0d2b958 100644 --- a/module/FileManager.py +++ b/module/FileManager.py @@ -1,6 +1,20 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +############################################################################### +# Copyright(c) 2008-2012 pyLoad Team +# http://www.pyload.org +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Subjected to the terms and conditions in LICENSE +# +# @author: RaNaN +############################################################################### + from time import time from threading import RLock from module.utils import lock @@ -19,6 +33,8 @@ def invalidate(func): return new +# TODO: needs to be replaced later +OWNER = 0 class FileManager: """Handles all request made to obtain information, @@ -78,7 +94,7 @@ class FileManager: @invalidate def addLinks(self, data, package): """Add links, data = (plugin, url) tuple. Internal method should use API.""" - self.db.addLinks(data, package) + self.db.addLinks(data, package, OWNER) self.evm.dispatchEvent("packageUpdated", package) @@ -86,7 +102,7 @@ class FileManager: def addPackage(self, name, folder, root, password, site, comment, paused): """Adds a package to database""" pid = self.db.addPackage(name, folder, root, password, site, comment, - PackageStatus.Paused if paused else PackageStatus.Ok) + PackageStatus.Paused if paused else PackageStatus.Ok, OWNER) p = self.db.getPackageInfo(pid) self.evm.dispatchEvent("packageInserted", pid, p.root, p.packageorder) @@ -97,7 +113,7 @@ class FileManager: def getPackage(self, pid): """return package instance""" if pid == self.ROOT_PACKAGE: - return RootPackage(self) + return RootPackage(self, OWNER) elif pid in self.packages: pack = self.packages[pid] pack.timestamp = time() @@ -115,7 +131,7 @@ class FileManager: def getPackageInfo(self, pid): """returns dict with package information""" if pid == self.ROOT_PACKAGE: - pack = RootPackage(self).toInfoData() + pack = RootPackage(self, OWNER).toInfoData() elif pid in self.packages: pack = self.packages[pid].toInfoData() pack.stats = self.db.getStatsForPackage(pid) @@ -183,7 +199,7 @@ class FileManager: # root package is not in database, create an instance if pid == self.ROOT_PACKAGE: - view.root = RootPackage(self).toInfoData() + view.root = RootPackage(self, OWNER).toInfoData() packs[self.ROOT_PACKAGE] = view.root elif pid in packs: view.root = packs[pid] diff --git a/module/PyFile.py b/module/PyFile.py index 5e6a3fae3..4cd0488a0 100644 --- a/module/PyFile.py +++ b/module/PyFile.py @@ -1,20 +1,19 @@ #!/usr/bin/env python -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program 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 program; if not, see <http://www.gnu.org/licenses/>. - - @author: RaNaN -""" +# -*- coding: utf-8 -*- + +############################################################################### +# Copyright(c) 2008-2012 pyLoad Team +# http://www.pyload.org +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Subjected to the terms and conditions in LICENSE +# +# @author: RaNaN +############################################################################### from time import sleep, time from threading import RLock @@ -48,14 +47,14 @@ class PyFile(object): Represents a file object at runtime """ __slots__ = ("m", "fid", "_name", "_size", "filestatus", "media", "added", "fileorder", - "url", "pluginname", "hash", "status", "error", "packageid", + "url", "pluginname", "hash", "status", "error", "packageid", "ownerid", "lock", "plugin", "waitUntil", "active", "abort", "statusname", "reconnected", "progress", "maxprogress", "pluginclass") @staticmethod def fromInfoData(m, info): f = PyFile(m, info.fid, info.name, info.size, info.status, info.media, info.added, info.fileorder, - "", "", "", DownloadStatus.NA, "", info.package) + "", "", "", DownloadStatus.NA, "", info.package, info.owner) if info.download: f.url = info.download.url f.pluginname = info.download.plugin @@ -66,7 +65,7 @@ class PyFile(object): return f def __init__(self, manager, fid, name, size, filestatus, media, added, fileorder, - url, pluginname, hash, status, error, package): + url, pluginname, hash, status, error, package, owner): self.m = manager @@ -82,6 +81,7 @@ class PyFile(object): self.hash = hash self.status = status self.error = error + self.ownerid = owner self.packageid = package #should not be used, use package() instead # database information ends here @@ -183,7 +183,7 @@ class PyFile(object): def toInfoData(self): - return FileInfo(self.fid, self.getName(), self.packageid, self.getSize(), self.filestatus, + return FileInfo(self.fid, self.getName(), self.packageid, self.ownerid, self.getSize(), self.filestatus, self.media, self.added, self.fileorder, DownloadInfo( self.url, self.pluginname, self.hash, self.status, self.getStatusName(), self.error ) diff --git a/module/PyPackage.py b/module/PyPackage.py index d0739124f..1dc2754ef 100644 --- a/module/PyPackage.py +++ b/module/PyPackage.py @@ -1,20 +1,19 @@ #!/usr/bin/env python -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program 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 program; if not, see <http://www.gnu.org/licenses/>. - - @author: RaNaN -""" +# -*- coding: utf-8 -*- + +############################################################################### +# Copyright(c) 2008-2012 pyLoad Team +# http://www.pyload.org +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Subjected to the terms and conditions in LICENSE +# +# @author: RaNaN +############################################################################### from time import time @@ -29,16 +28,17 @@ class PyPackage: @staticmethod def fromInfoData(m, info): - return PyPackage(m, info.pid, info.name, info.folder, info.root, + return PyPackage(m, info.pid, info.name, info.folder, info.root, info.owner, info.site, info.comment, info.password, info.added, info.status, info.packageorder) - def __init__(self, manager, pid, name, folder, root, site, comment, password, added, status, packageorder): + def __init__(self, manager, pid, name, folder, root, owner, site, comment, password, added, status, packageorder): self.m = manager self.pid = pid self.name = name self.folder = folder self.root = root + self.ownerid = owner self.site = site self.comment = comment self.password = password @@ -56,7 +56,7 @@ class PyPackage: return self.timestamp + 30 * 60 > time() def toInfoData(self): - return PackageInfo(self.pid, self.name, self.folder, self.root, self.site, + return PackageInfo(self.pid, self.name, self.folder, self.root, self.ownerid, self.site, self.comment, self.password, self.added, self.status, self.packageorder ) @@ -92,8 +92,8 @@ class PyPackage: class RootPackage(PyPackage): - def __init__(self, m): - PyPackage.__init__(self, m, -1, "root", "", -2, "", "", "", 0, PackageStatus.Ok, 0) + def __init__(self, m, owner): + PyPackage.__init__(self, m, -1, "root", "", owner, -2, "", "", "", 0, PackageStatus.Ok, 0) def getPath(self, name=""): return join(self.m.core.config["general"]["download_folder"], name) diff --git a/module/database/DatabaseBackend.py b/module/database/DatabaseBackend.py index 516aa981f..ec39e3fd9 100644 --- a/module/database/DatabaseBackend.py +++ b/module/database/DatabaseBackend.py @@ -1,21 +1,21 @@ #!/usr/bin/env python -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program 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 program; if not, see <http://www.gnu.org/licenses/>. - - @author: RaNaN - @author: mkaay -""" +# -*- coding: utf-8 -*- + +############################################################################### +# Copyright(c) 2008-2012 pyLoad Team +# http://www.pyload.org +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Subjected to the terms and conditions in LICENSE +# +# @author: RaNaN +# @author: mkaay +############################################################################### + from threading import Thread, Event from shutil import move @@ -245,8 +245,10 @@ class DatabaseBackend(Thread): '"added" INTEGER DEFAULT 0 NOT NULL,' # set by trigger '"status" INTEGER DEFAULT 0 NOT NULL,' '"packageorder" INTEGER DEFAULT -1 NOT NULL,' #incremented by trigger - '"root" INTEGER DEFAULT -1 NOT NULL,' - 'CHECK (root != pid) ' + '"root" INTEGER DEFAULT -1 NOT NULL, ' + '"owner" INTEGER NOT NULL, ' + 'FOREIGN KEY(owner) REFERENCES users(uid), ' + 'CHECK (root != pid)' ')' ) @@ -267,7 +269,8 @@ class DatabaseBackend(Thread): 'END' ) - self.c.execute('CREATE INDEX IF NOT EXISTS "root_index" ON packages(root)') + self.c.execute('CREATE INDEX IF NOT EXISTS "package_index" ON packages(root, owner)') + self.c.execute('CREATE INDEX IF NOT EXISTS "package_owner" ON packages(owner)') self.c.execute( 'CREATE TABLE IF NOT EXISTS "files" (' @@ -284,11 +287,14 @@ class DatabaseBackend(Thread): '"dlstatus" INTEGER DEFAULT 0 NOT NULL, ' '"error" TEXT DEFAULT "" NOT NULL, ' '"package" INTEGER NOT NULL, ' + '"owner" INTEGER NOT NULL, ' + 'FOREIGN KEY(owner) REFERENCES users(uid), ' 'FOREIGN KEY(package) REFERENCES packages(id)' ')' ) - self.c.execute('CREATE INDEX IF NOT EXISTS "package_index" ON files(package)') + self.c.execute('CREATE INDEX IF NOT EXISTS "file_index" ON files(package, owner)') + self.c.execute('CREATE INDEX IF NOT EXISTS "file_owner" ON files(owner)') self.c.execute( 'CREATE TRIGGER IF NOT EXISTS "insert_file" AFTER INSERT ON "files"' diff --git a/module/database/FileDatabase.py b/module/database/FileDatabase.py index b783d15d9..80da775c7 100644 --- a/module/database/FileDatabase.py +++ b/module/database/FileDatabase.py @@ -1,88 +1,97 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -""" - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program 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 program; if not, see <http://www.gnu.org/licenses/>. - - @author: RaNaN -""" +############################################################################### +# Copyright(c) 2008-2012 pyLoad Team +# http://www.pyload.org +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Subjected to the terms and conditions in LICENSE +# +# @author: RaNaN +############################################################################### + from new_collections import OrderedDict -from module.Api import DownloadInfo, LinkStatus, FileInfo, PackageInfo, PackageStats +from module.Api import DownloadInfo, FileInfo, PackageInfo, PackageStats from module.database import DatabaseMethods, queue, async, inner -default = PackageStats(0, 0, 0, 0) +zero_stats = PackageStats(0, 0, 0, 0) class FileMethods(DatabaseMethods): @queue - def filecount(self): + def filecount(self, user=None): """returns number of files""" self.c.execute("SELECT COUNT(*) FROM files") return self.c.fetchone()[0] @queue - def queuecount(self): + def queuecount(self, user=None): """ number of files in queue not finished yet""" # status not in NA, finished, skipped self.c.execute("SELECT COUNT(*) FROM files WHERE dlstatus NOT IN (0,5,6)") return self.c.fetchone()[0] @queue - def processcount(self, fid): + def processcount(self, fid, user=None): """ number of files which have to be processed """ # status in online, queued, starting, waiting, downloading self.c.execute("SELECT COUNT(*) FROM files as WHERE dlstatus IN (2,3,8,9,10) AND fid != ?", (str(fid), )) return self.c.fetchone()[0] + # TODO: think about multiuser side effects on *count methods + @queue - def addLink(self, url, name, plugin, package): + def addLink(self, url, name, plugin, package, owner): # mark file status initially as missing, dlstatus - queued - self.c.execute('INSERT INTO files(url, name, plugin, status, dlstatus, package) VALUES(?,?,?,1,3,?)', - (url, name, plugin, package)) + self.c.execute('INSERT INTO files(url, name, plugin, status, dlstatus, package, owner) VALUES(?,?,?,1,3,?,?)', + (url, name, plugin, package, owner)) return self.c.lastrowid @async - def addLinks(self, links, package): + def addLinks(self, links, package, owner): """ links is a list of tuples (url, plugin)""" - links = [(x[0], x[0], x[1], package) for x in links] - self.c.executemany('INSERT INTO files(url, name, plugin, status, dlstatus, package) VALUES(?,?,?,1,3,?)', links) + links = [(x[0], x[0], x[1], package, owner) for x in links] + self.c.executemany('INSERT INTO files(url, name, plugin, status, dlstatus, package, owner) VALUES(?,?,?,1,3,?,?)', + links) @queue - def addFile(self, name, size, media, package): + def addFile(self, name, size, media, package, owner): # file status - ok, dl status NA - self.c.execute('INSERT INTO files(name, size, media, package) VALUES(?,?,?,?)', - (name, size, media, package)) + self.c.execute('INSERT INTO files(name, size, media, package, owner) VALUES(?,?,?,?,?)', + (name, size, media, package, owner)) return self.c.lastrowid @queue - def addPackage(self, name, folder, root, password, site, comment, status): - self.c.execute('INSERT INTO packages(name, folder, root, password, site, comment, status) VALUES(?,?,?,?,?,?,?)' - , - (name, folder, root, password, site, comment, status)) + def addPackage(self, name, folder, root, password, site, comment, status, owner): + self.c.execute( + 'INSERT INTO packages(name, folder, root, password, site, comment, status, owner) VALUES(?,?,?,?,?,?,?,?)' + , (name, folder, root, password, site, comment, status, owner)) return self.c.lastrowid @async - def deletePackage(self, pid): - # order updated by trigger - self.c.execute('DELETE FROM packages WHERE pid=?', (pid,)) + def deletePackage(self, pid, owner=None): + # order updated by trigge + if owner is None: + self.c.execute('DELETE FROM packages WHERE pid=?', (pid,)) + else: + self.c.execute('DELETE FROM packages WHERE pid=? AND owner=?', (pid, owner)) @async - def deleteFile(self, fid, order, package): + def deleteFile(self, fid, order, package, owner=None): """ To delete a file order and package of it is needed """ - self.c.execute('DELETE FROM files WHERE fid=?', (fid,)) - self.c.execute('UPDATE files SET fileorder=fileorder-1 WHERE fileorder > ? AND package=?', - (order, package)) + if owner is None: + self.c.execute('DELETE FROM files WHERE fid=?', (fid,)) + self.c.execute('UPDATE files SET fileorder=fileorder-1 WHERE fileorder > ? AND package=?', + (order, package)) + else: + self.c.execute('DELETE FROM files WHERE fid=? AND owner=?', (fid, owner)) + self.c.execute('UPDATE files SET fileorder=fileorder-1 WHERE fileorder > ? AND package=? AND owner=?', + (order, package, owner)) @async def saveCollector(self, owner, data): @@ -92,8 +101,10 @@ class FileMethods(DatabaseMethods): @queue def retrieveCollector(self, owner): """ retrieve the saved string """ - self.c.execute('SELECT data FROM collector owner=?', (owner,)) - return self.c.fetchone()[0] + self.c.execute('SELECT data FROM collector WHERE owner=?', (owner,)) + r = self.c.fetchone() + if not r: return None + return r[0] @async def deleteCollector(self, owner): @@ -101,66 +112,81 @@ class FileMethods(DatabaseMethods): self.c.execute('DELETE FROM collector WHERE owner=?', (owner,)) @queue - def getAllFiles(self, package=None, search=None, unfinished=False): + def getAllFiles(self, package=None, search=None, unfinished=False, owner=None): """ Return dict with file information :param package: optional package to filter out :param search: or search string for file name :param unfinished: filter by dlstatus not finished + :param owner: only specific owner """ - qry = ('SELECT fid, name, size, status, media, added, fileorder, ' - 'url, plugin, hash, dlstatus, error, package FROM files') + qry = ('SELECT fid, name, owner, size, status, media, added, fileorder, ' + 'url, plugin, hash, dlstatus, error, package FROM files WHERE ') + + arg = [] if unfinished: - qry += ' WHERE dlstatus NOT IN (0, 5, 6)' + qry += 'dlstatus NOT IN (0, 5, 6) AND ' + if owner is not None: + qry += 'owner=? AND ' + arg.append(owner) if package is not None: - qry += ' AND' if unfinished else ' WHERE' - self.c.execute(qry + ' package=? ORDER BY package, fileorder', (package,)) - elif search is not None: - qry += ' AND' if unfinished else ' WHERE' + arg.append(package) + qry += 'package=? AND ' + if search is not None: search = "%%%s%%" % search.strip("%") - self.c.execute(qry + ' name LIKE ? ORDER BY package, fileorder', (search,)) + arg.append(search) + qry += "name LIKE ? " - else: - self.c.execute(qry) + # make qry valid + if qry.endswith("WHERE "): qry = qry[:-6] + if qry.endswith("AND "): qry = qry[:-4] + + self.c.execute(qry + "ORDER BY package, fileorder", arg) data = OrderedDict() for r in self.c: - f = FileInfo(r[0], r[1], r[12], r[2], r[3], r[4], r[5], r[6]) - if r[10] > 0: # dl status != NA - f.download = DownloadInfo(r[7], r[8], r[9], r[10], self.manager.statusMsg[r[10]], r[11]) + f = FileInfo(r[0], r[1], r[13], r[2], r[3], r[4], r[5], r[6], r[7]) + if r[11] > 0: # dl status != NA + f.download = DownloadInfo(r[8], r[9], r[10], r[11], self.manager.statusMsg[r[11]], r[12]) data[r[0]] = f return data @queue - def getAllPackages(self, root=None): + def getAllPackages(self, root=None, owner=None): """ Return dict with package information :param root: optional root to filter """ - qry = ('SELECT pid, name, folder, root, site, comment, password, added, status, packageorder ' + qry = ('SELECT pid, name, folder, root, owner, site, comment, password, added, status, packageorder ' 'FROM packages%s ORDER BY root, packageorder') if root is None: - stats = self.getPackageStats() - self.c.execute(qry % "") + stats = self.getPackageStats(owner=owner) + if owner is None: + self.c.execute(qry % "") + else: + self.c.execute(qry % " WHERE owner=?", (owner,)) else: - stats = self.getPackageStats(root=root) - self.c.execute(qry % ' WHERE root=? OR pid=?', (root, root)) + stats = self.getPackageStats(root=root, owner=owner) + if owner is None: + self.c.execute(qry % ' WHERE root=? OR pid=?', (root, root)) + else: + self.c.execute(qry % ' WHERE (root=? OR pid=?) AND owner=?', (root, root, owner)) data = OrderedDict() for r in self.c: data[r[0]] = PackageInfo( - r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], stats.get(r[0], default) + r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], stats.get(r[0], zero_stats) ) return data @inner - def getPackageStats(self, pid=None, root=None): + def getPackageStats(self, pid=None, root=None, owner=None): qry = ("SELECT p.pid, SUM(f.size) AS sizetotal, COUNT(f.fid) AS linkstotal, sizedone, linksdone " "FROM packages p JOIN files f ON p.pid = f.package AND f.dlstatus > 0 %(sub)s LEFT OUTER JOIN " "(SELECT p.pid AS pid, SUM(f.size) AS sizedone, COUNT(f.fid) AS linksdone " @@ -173,6 +199,8 @@ class FileMethods(DatabaseMethods): self.c.execute(qry % {"sub": "AND (p.root=:root OR p.pid=:root)"}, locals()) elif pid is not None: self.c.execute(qry % {"sub": "AND p.pid=:pid"}, locals()) + elif owner is not None: + self.c.execute(qry % {"sub": "AND p.owner=:owner"}, locals()) else: self.c.execute(qry % {"sub": ""}) @@ -193,17 +221,17 @@ class FileMethods(DatabaseMethods): @queue def getFileInfo(self, fid, force=False): - """get data for specific file""" - self.c.execute('SELECT fid, name, size, status, media, added, fileorder, ' + """get data for specific file, when force is true download info will be appended""" + self.c.execute('SELECT fid, name, owner, size, status, media, added, fileorder, ' 'url, plugin, hash, dlstatus, error, package FROM files ' 'WHERE fid=?', (fid,)) r = self.c.fetchone() if not r: return None else: - f = FileInfo(r[0], r[1], r[12], r[2], r[3], r[4], r[5], r[6]) - if r[10] > 0 or force: - f.download = DownloadInfo(r[7], r[8], r[9], r[10], self.manager.statusMsg[r[10]], r[11]) + f = FileInfo(r[0], r[1], r[13], r[2], r[3], r[4], r[5], r[6], r[7]) + if r[11] > 0 or force: + f.download = DownloadInfo(r[8], r[9], r[10], r[11], self.manager.statusMsg[r[11]], r[12]) return f @@ -213,7 +241,7 @@ class FileMethods(DatabaseMethods): if stats: stats = self.getPackageStats(pid=pid) - self.c.execute('SELECT pid, name, folder, root, site, comment, password, added, status, packageorder ' + self.c.execute('SELECT pid, name, folder, root, owner, site, comment, password, added, status, packageorder ' 'FROM packages WHERE pid=?', (pid,)) r = self.c.fetchone() @@ -221,11 +249,11 @@ class FileMethods(DatabaseMethods): return None else: return PackageInfo( - r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], stats.get(r[0], default) if stats else None + r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10], stats.get(r[0], zero_stats) if stats else None ) @async - def updateLinkInfo(self, data): + def updateLinkInfo(self, data, owner): """ data is list of tuples (name, size, status,[ hash,] url)""" if data and len(data[0]) == 4: self.c.executemany('UPDATE files SET name=?, size=?, dlstatus=? WHERE url=? AND dlstatus IN (0,1,2,3,14)', @@ -246,6 +274,7 @@ class FileMethods(DatabaseMethods): self.c.execute('UPDATE packages SET name=?, folder=?, site=?, comment=?, password=?, status=? WHERE pid=?', (p.name, p.folder, p.site, p.comment, p.password, p.status, p.pid)) + # TODO: most modifying methods needs owner argument to avoid checking beforehand @async def orderPackage(self, pid, root, oldorder, order): if oldorder > order: # package moved upwards @@ -293,11 +322,6 @@ class FileMethods(DatabaseMethods): self.c.execute('SELECT max(packageorder) FROM packages WHERE root=?', (dpid,)) r = self.c.fetchone() max = (r[0] if r[0] else 0) + 1 - print max - - self.c.execute('SELECT pid, packageorder FROM packages WHERE root=?', (dpid,)) - for r in self.c: - print r self.c.execute('UPDATE packages SET packageorder=packageorder-1 WHERE packageorder > ? AND root=?', (order, root)) @@ -314,6 +338,8 @@ class FileMethods(DatabaseMethods): # status -> queued self.c.execute('UPDATE files SET status=3 WHERE package=?', (pid,)) + + # TODO: multi user approach @queue def getJob(self, occ): """return pyfile ids, which are suitable for download and dont use a occupied plugin""" @@ -338,7 +364,7 @@ class FileMethods(DatabaseMethods): return [r[0] for r in self.c] @queue - def restartFailed(self): + def restartFailed(self, owner): # status=queued, where status in failed, aborted, temp offline self.c.execute("UPDATE files SET dlstatus=3, error='' WHERE dlstatus IN (7, 11, 12)") diff --git a/module/lib/hg_tool.py b/module/lib/hg_tool.py new file mode 100644 index 000000000..cd97833df --- /dev/null +++ b/module/lib/hg_tool.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import re +from subprocess import Popen, PIPE +from time import time, gmtime, strftime + +aliases = {"zoidber": "zoidberg", "zoidberg10": "zoidberg", "webmaster": "dhmh", "mast3rranan": "ranan", + "ranan2": "ranan"} +exclude = ["locale/*", "module/lib/*"] +date_format = "%Y-%m-%d" +line_re = re.compile(r" (\d+) \**", re.I) + +def add_exclude_flags(args): + for dir in exclude: + args.extend(["-X", dir]) + +# remove small percentages +def wipe(data, perc=1): + s = (sum(data.values()) * perc) / 100 + for k, v in data.items(): + if v < s: del data[k] + + return data + +# remove aliases +def de_alias(data): + for k, v in aliases.iteritems(): + if k not in data: continue + alias = aliases[k] + + if alias in data: data[alias] += data[k] + else: data[alias] = data[k] + + del data[k] + + return data + + +def output(data): + s = float(sum(data.values())) + print "Total Lines: %d" % s + for k, v in data.iteritems(): + print "%15s: %.1f%% | %d" % (k, (v * 100) / s, v) + print + + +def file_list(): + args = ["hg", "status", "-A"] + add_exclude_flags(args) + p = Popen(args, stdout=PIPE) + out, err = p.communicate() + return [x.split()[1] for x in out.splitlines() if x.split()[0] in "CMA"] + + +def hg_annotate(path): + args = ["hg", "annotate", "-u", path] + p = Popen(args, stdout=PIPE) + out, err = p.communicate() + + data = {} + + for line in out.splitlines(): + author, non, line = line.partition(":") + + # probably binary file + if author == path: return {} + + author = author.strip().lower() + if not line.strip(): continue # don't count blank lines + + if author in data: data[author] += 1 + else: data[author] = 1 + + return de_alias(data) + + +def hg_churn(days=None): + args = ["hg", "churn"] + if days: + args.append("-d") + t = time() - 60 * 60 * 24 * days + args.append("%s to %s" % (strftime(date_format, gmtime(t)), strftime(date_format))) + + add_exclude_flags(args) + p = Popen(args, stdout=PIPE) + out, err = p.communicate() + + data = {} + + for line in out.splitlines(): + m = line_re.search(line) + author = line.split()[0] + lines = int(m.group(1)) + + if "@" in author: + author, n, email = author.partition("@") + + author = author.strip().lower() + + if author in data: data[author] += lines + else: data[author] = lines + + return de_alias(data) + + +def complete_annotate(): + files = file_list() + data = {} + for f in files: + tmp = hg_annotate(f) + for k, v in tmp.iteritems(): + if k in data: data[k] += v + else: data[k] = v + + return data + + +if __name__ == "__main__": + for d in (30, 90, 180): + c = wipe(hg_churn(d)) + print "Changes in %d days:" % d + output(c) + + c = wipe(hg_churn()) + print "Total changes:" + output(c) + + print "Current source code version:" + data = wipe(complete_annotate()) + output(data) + + diff --git a/module/remote/socketbackend/ttypes.py b/module/remote/socketbackend/ttypes.py index 36f2b01ef..127098ec3 100644 --- a/module/remote/socketbackend/ttypes.py +++ b/module/remote/socketbackend/ttypes.py @@ -159,12 +159,13 @@ class FileDoesNotExists(Exception): self.fid = fid class FileInfo(BaseObject): - __slots__ = ['fid', 'name', 'package', 'size', 'status', 'media', 'added', 'fileorder', 'download'] + __slots__ = ['fid', 'name', 'package', 'owner', 'size', 'status', 'media', 'added', 'fileorder', 'download'] - def __init__(self, fid=None, name=None, package=None, size=None, status=None, media=None, added=None, fileorder=None, download=None): + def __init__(self, fid=None, name=None, package=None, owner=None, size=None, status=None, media=None, added=None, fileorder=None, download=None): self.fid = fid self.name = name self.package = package + self.owner = owner self.size = size self.status = status self.media = media @@ -210,13 +211,14 @@ class PackageDoesNotExists(Exception): self.pid = pid class PackageInfo(BaseObject): - __slots__ = ['pid', 'name', 'folder', 'root', 'site', 'comment', 'password', 'added', 'status', 'packageorder', 'stats', 'fids', 'pids'] + __slots__ = ['pid', 'name', 'folder', 'root', 'owner', 'site', 'comment', 'password', 'added', 'status', 'packageorder', 'stats', 'fids', 'pids'] - def __init__(self, pid=None, name=None, folder=None, root=None, site=None, comment=None, password=None, added=None, status=None, packageorder=None, stats=None, fids=None, pids=None): + def __init__(self, pid=None, name=None, folder=None, root=None, owner=None, site=None, comment=None, password=None, added=None, status=None, packageorder=None, stats=None, fids=None, pids=None): self.pid = pid self.name = name self.folder = folder self.root = root + self.owner = owner self.site = site self.comment = comment self.password = password @@ -291,9 +293,9 @@ class ServiceException(Exception): self.msg = msg class UserData(BaseObject): - __slots__ = ['uid', 'name', 'email', 'role', 'permission', 'folder', 'traffic', 'limit', 'user', 'templateName'] + __slots__ = ['uid', 'name', 'email', 'role', 'permission', 'folder', 'traffic', 'dllimit', 'user', 'templateName'] - def __init__(self, uid=None, name=None, email=None, role=None, permission=None, folder=None, traffic=None, limit=None, user=None, templateName=None): + def __init__(self, uid=None, name=None, email=None, role=None, permission=None, folder=None, traffic=None, dllimit=None, user=None, templateName=None): self.uid = uid self.name = name self.email = email @@ -301,7 +303,7 @@ class UserData(BaseObject): self.permission = permission self.folder = folder self.traffic = traffic - self.limit = limit + self.dllimit = dllimit self.user = user self.templateName = templateName diff --git a/module/remote/thriftbackend/pyload.thrift b/module/remote/thriftbackend/pyload.thrift index 254f41068..181ca4204 100644 --- a/module/remote/thriftbackend/pyload.thrift +++ b/module/remote/thriftbackend/pyload.thrift @@ -84,13 +84,12 @@ enum Permission { All = 0, // requires no permission, but login Add = 1, // can add packages Delete = 2, // can delete packages - Status = 4, // see and change server status - List = 16, // see listed downloads - Modify = 32, // modify some attribute of downloads - Download = 64, // can download from webinterface - Accounts = 128, // can access accounts - Interaction = 256, // can interact with plugins - Addons = 512 // user can activate addons + Modify = 4, // modify some attribute of downloads + Status = 8, // see and change server status + Download = 16, // can download from webinterface + Accounts = 32, // can access accounts + Interaction = 64, // can interact with plugins + Addons = 128 // user can activate addons } enum Role { @@ -141,12 +140,13 @@ struct FileInfo { 1: FileID fid, 2: string name, 3: PackageID package, - 4: ByteCount size, - 5: FileStatus status, - 6: MediaType media, - 7: UTCDate added, - 8: i16 fileorder, - 9: optional DownloadInfo download, + 4: UserID owner, + 5: ByteCount size, + 6: FileStatus status, + 7: MediaType media, + 8: UTCDate added, + 9: i16 fileorder, + 10: optional DownloadInfo download, } struct PackageStats { @@ -161,15 +161,16 @@ struct PackageInfo { 2: string name, 3: string folder, 4: PackageID root, - 5: string site, - 6: string comment, - 7: string password, - 8: UTCDate added, - 9: PackageStatus status, - 10: i16 packageorder, - 11: PackageStats stats, - 12: list<FileID> fids, - 13: list<PackageID> pids, + 5: UserID owner, + 6: string site, + 7: string comment, + 8: string password, + 9: UTCDate added, + 10: PackageStatus status, + 11: i16 packageorder, + 12: PackageStats stats, + 13: list<FileID> fids, + 14: list<PackageID> pids, } // thrift does not allow recursive datatypes, so all data is accumulated and mapped with id @@ -266,10 +267,9 @@ struct AddonService { struct OnlineCheck { 1: ResultID rid, // -1 -> nothing more to get - 2: map<string, LinkStatus> data, //url to result + 2: map<string, LinkStatus> data, // url to result } - // exceptions exception PackageDoesNotExists { diff --git a/module/remote/thriftbackend/thriftgen/pyload/Pyload.py b/module/remote/thriftbackend/thriftgen/pyload/Pyload.py index c45663d25..dd446cfc3 100644 --- a/module/remote/thriftbackend/thriftgen/pyload/Pyload.py +++ b/module/remote/thriftbackend/thriftgen/pyload/Pyload.py @@ -9,7 +9,7 @@ from thrift.Thrift import TType, TMessageType, TException from ttypes import * from thrift.Thrift import TProcessor -from thrift.protocol.TBase import TBase, TExceptionBase, TApplicationException +from thrift.protocol.TBase import TBase, TExceptionBase class Iface(object): diff --git a/module/remote/thriftbackend/thriftgen/pyload/ttypes.py b/module/remote/thriftbackend/thriftgen/pyload/ttypes.py index c177a9dd2..d170f4688 100644 --- a/module/remote/thriftbackend/thriftgen/pyload/ttypes.py +++ b/module/remote/thriftbackend/thriftgen/pyload/ttypes.py @@ -411,6 +411,7 @@ class FileInfo(TBase): - fid - name - package + - owner - size - status - media @@ -423,6 +424,7 @@ class FileInfo(TBase): 'fid', 'name', 'package', + 'owner', 'size', 'status', 'media', @@ -436,18 +438,20 @@ class FileInfo(TBase): (1, TType.I32, 'fid', None, None, ), # 1 (2, TType.STRING, 'name', None, None, ), # 2 (3, TType.I32, 'package', None, None, ), # 3 - (4, TType.I64, 'size', None, None, ), # 4 - (5, TType.I32, 'status', None, None, ), # 5 - (6, TType.I32, 'media', None, None, ), # 6 - (7, TType.I64, 'added', None, None, ), # 7 - (8, TType.I16, 'fileorder', None, None, ), # 8 - (9, TType.STRUCT, 'download', (DownloadInfo, DownloadInfo.thrift_spec), None, ), # 9 + (4, TType.I32, 'owner', None, None, ), # 4 + (5, TType.I64, 'size', None, None, ), # 5 + (6, TType.I32, 'status', None, None, ), # 6 + (7, TType.I32, 'media', None, None, ), # 7 + (8, TType.I64, 'added', None, None, ), # 8 + (9, TType.I16, 'fileorder', None, None, ), # 9 + (10, TType.STRUCT, 'download', (DownloadInfo, DownloadInfo.thrift_spec), None, ), # 10 ) - def __init__(self, fid=None, name=None, package=None, size=None, status=None, media=None, added=None, fileorder=None, download=None,): + def __init__(self, fid=None, name=None, package=None, owner=None, size=None, status=None, media=None, added=None, fileorder=None, download=None,): self.fid = fid self.name = name self.package = package + self.owner = owner self.size = size self.status = status self.media = media @@ -494,6 +498,7 @@ class PackageInfo(TBase): - name - folder - root + - owner - site - comment - password @@ -510,6 +515,7 @@ class PackageInfo(TBase): 'name', 'folder', 'root', + 'owner', 'site', 'comment', 'password', @@ -527,22 +533,24 @@ class PackageInfo(TBase): (2, TType.STRING, 'name', None, None, ), # 2 (3, TType.STRING, 'folder', None, None, ), # 3 (4, TType.I32, 'root', None, None, ), # 4 - (5, TType.STRING, 'site', None, None, ), # 5 - (6, TType.STRING, 'comment', None, None, ), # 6 - (7, TType.STRING, 'password', None, None, ), # 7 - (8, TType.I64, 'added', None, None, ), # 8 - (9, TType.I32, 'status', None, None, ), # 9 - (10, TType.I16, 'packageorder', None, None, ), # 10 - (11, TType.STRUCT, 'stats', (PackageStats, PackageStats.thrift_spec), None, ), # 11 - (12, TType.LIST, 'fids', (TType.I32,None), None, ), # 12 - (13, TType.LIST, 'pids', (TType.I32,None), None, ), # 13 + (5, TType.I32, 'owner', None, None, ), # 5 + (6, TType.STRING, 'site', None, None, ), # 6 + (7, TType.STRING, 'comment', None, None, ), # 7 + (8, TType.STRING, 'password', None, None, ), # 8 + (9, TType.I64, 'added', None, None, ), # 9 + (10, TType.I32, 'status', None, None, ), # 10 + (11, TType.I16, 'packageorder', None, None, ), # 11 + (12, TType.STRUCT, 'stats', (PackageStats, PackageStats.thrift_spec), None, ), # 12 + (13, TType.LIST, 'fids', (TType.I32,None), None, ), # 13 + (14, TType.LIST, 'pids', (TType.I32,None), None, ), # 14 ) - def __init__(self, pid=None, name=None, folder=None, root=None, site=None, comment=None, password=None, added=None, status=None, packageorder=None, stats=None, fids=None, pids=None,): + def __init__(self, pid=None, name=None, folder=None, root=None, owner=None, site=None, comment=None, password=None, added=None, status=None, packageorder=None, stats=None, fids=None, pids=None,): self.pid = pid self.name = name self.folder = folder self.root = root + self.owner = owner self.site = site self.comment = comment self.password = password @@ -809,7 +817,7 @@ class UserData(TBase): - permission - folder - traffic - - limit + - dllimit - user - templateName """ @@ -822,7 +830,7 @@ class UserData(TBase): 'permission', 'folder', 'traffic', - 'limit', + 'dllimit', 'user', 'templateName', ] @@ -836,12 +844,12 @@ class UserData(TBase): (5, TType.I16, 'permission', None, None, ), # 5 (6, TType.STRING, 'folder', None, None, ), # 6 (7, TType.I64, 'traffic', None, None, ), # 7 - (8, TType.I16, 'limit', None, None, ), # 8 + (8, TType.I16, 'dllimit', None, None, ), # 8 (9, TType.I32, 'user', None, None, ), # 9 (10, TType.STRING, 'templateName', None, None, ), # 10 ) - def __init__(self, uid=None, name=None, email=None, role=None, permission=None, folder=None, traffic=None, limit=None, user=None, templateName=None,): + def __init__(self, uid=None, name=None, email=None, role=None, permission=None, folder=None, traffic=None, dllimit=None, user=None, templateName=None,): self.uid = uid self.name = name self.email = email @@ -849,7 +857,7 @@ class UserData(TBase): self.permission = permission self.folder = folder self.traffic = traffic - self.limit = limit + self.dllimit = dllimit self.user = user self.templateName = templateName diff --git a/module/web/pyload_app.py b/module/web/pyload_app.py index 4c448d2cd..2234b76c6 100644 --- a/module/web/pyload_app.py +++ b/module/web/pyload_app.py @@ -36,7 +36,7 @@ from utils import render_to_response, parse_permissions, parse_userdata, \ from filters import relpath, unquotepath -from module.Api import Output +from module.Api import Output, Permission from module.utils import format_size from module.utils.fs import save_join, fs_encode, fs_decode, listdir @@ -150,7 +150,7 @@ def logout(): @route("/") @route("/home") -@login_required("LIST") +@login_required("List") def home(): try: res = [toDict(x) for x in PYLOAD.getProgressInfo()] @@ -168,7 +168,7 @@ def home(): @route("/queue") -@login_required("LIST") +@login_required("List") def queue(): queue = PYLOAD.getQueue() diff --git a/module/web/utils.py b/module/web/utils.py index ac4bdd4f8..1641fdbba 100644 --- a/module/web/utils.py +++ b/module/web/utils.py @@ -50,7 +50,7 @@ def parse_permissions(session): def permlist(): - return [x for x in dir(Permission) if not x.startswith("_") and x != "ALL"] + return [x for x in dir(Permission) if not x.startswith("_") and x != "All"] def get_permission(perms, p): diff --git a/pavement.py b/pavement.py index da56fefbb..e9d90fb99 100644 --- a/pavement.py +++ b/pavement.py @@ -47,7 +47,7 @@ setup( keywords = ('pyload', 'download-manager', 'one-click-hoster', 'download'), url="http://pyload.org", download_url='http://pyload.org/download', - license='GPL v3', + license='AGPL v3', author="pyLoad Team", author_email="support@pyload.org", platforms = ('Any',), @@ -78,7 +78,7 @@ setup( "Environment :: Console", "Environment :: Web Environment", "Intended Audience :: End Users/Desktop", - "License :: OSI Approved :: GNU General Public License (GPL)", + "License :: OSI Approved :: GNU Affero General Public License v3", "Operating System :: OS Independent", "Programming Language :: Python :: 2" ] diff --git a/pyLoadCore.py b/pyLoadCore.py index 25b4c51c0..ac9fb0133 100755 --- a/pyLoadCore.py +++ b/pyLoadCore.py @@ -1,18 +1,15 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """ - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, - or (at your option) any later version. - - This program 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 program; if not, see <http://www.gnu.org/licenses/>. + Copyright(c) 2008-2012 pyLoad Team + http://www.pyload.org + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + Subjected to the terms and conditions in LICENSE @author: spoob @author: sebnapi diff --git a/tests/test_database.py b/tests/test_database.py index 9fe7796a4..e3e6b9963 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -18,6 +18,8 @@ class TestDatabase(BenchmarkTest): "get_package_data", "get_file_data", "find_files", "collector", "purge"] pids = None fids = None + owner = 123 + pstatus = 0 @classmethod def setUpClass(cls): @@ -49,22 +51,23 @@ class TestDatabase(BenchmarkTest): def test_insert(self, n=200): for i in range(n): - pid = self.db.addPackage("name", "folder", choice(self.pids), "password", "site", "comment", 0) + pid = self.db.addPackage("name", "folder", choice(self.pids), "password", "site", "comment", self.pstatus, + self.owner) self.pids.append(pid) def test_insert_links(self): for i in range(10000): - fid = self.db.addLink("url %s" % i, "name", "plugin", choice(self.pids)) + fid = self.db.addLink("url %s" % i, "name", "plugin", choice(self.pids), self.owner) self.fids.append(fid) def test_insert_many(self): for pid in self.pids: - self.db.addLinks([("url %s" % i, "plugin") for i in range(50)], pid) + self.db.addLinks([("url %s" % i, "plugin") for i in range(50)], pid, self.owner) def test_get_packages(self): packs = self.db.getAllPackages() n = len(packs) - assert n == len(self.pids) -1 + assert n == len(self.pids) - 1 print "Fetched %d packages" % n self.assert_pack(choice(packs.values())) @@ -121,30 +124,43 @@ class TestDatabase(BenchmarkTest): assert "1" in f.name def test_collector(self): - self.db.deleteCollector() - assert not self.db.getCollector() - - self.db.addCollector("plugin", "package", [("name", 0, 0, "url %d" % i) for i in range(10)]) - coll = self.db.getCollector() - assert len(coll) == 10 - assert coll[0].plugin == "plugin" - assert coll[0].packagename == "package" - assert coll[0].name == "name" - assert "url" in coll[0].url - - self.db.deleteCollector(url="url 1") - assert len(self.db.getCollector()) == 9 - self.db.deleteCollector(package="package") - assert not self.db.getCollector() + self.db.saveCollector(0, "data") + assert self.db.retrieveCollector(0) == "data" + self.db.deleteCollector(0) def test_purge(self): self.db.purgeLinks() + + def test_user_context(self): + self.db.purgeAll() + + p1 = self.db.addPackage("name", "folder", 0, "password", "site", "comment", self.pstatus, 0) + self.db.addLink("url", "name", "plugin", p1, 0) + p2 = self.db.addPackage("name", "folder", 0, "password", "site", "comment", self.pstatus, 1) + self.db.addLink("url", "name", "plugin", p2, 1) + + assert len(self.db.getAllPackages(owner=0)) == 1 == len(self.db.getAllFiles(owner=0)) + assert len(self.db.getAllPackages(root=0, owner=0)) == 1 == len(self.db.getAllFiles(package=p1, owner=0)) + assert len(self.db.getAllPackages(owner=1)) == 1 == len(self.db.getAllFiles(owner=1)) + assert len(self.db.getAllPackages(root=0, owner=1)) == 1 == len(self.db.getAllFiles(package=p2, owner=1)) + assert len(self.db.getAllPackages()) == 2 == len(self.db.getAllFiles()) + + self.db.deletePackage(p1, 1) + assert len(self.db.getAllPackages(owner=0)) == 1 == len(self.db.getAllFiles(owner=0)) + self.db.deletePackage(p1, 0) + assert len(self.db.getAllPackages(owner=1)) == 1 == len(self.db.getAllFiles(owner=1)) + self.db.deletePackage(p2) + + assert len(self.db.getAllPackages()) == 0 + + def assert_file(self, f): try: assert f is not None - self.assert_int(f, ("fid", "status", "size", "media", "fileorder", "added", "package")) + self.assert_int(f, ("fid", "status", "size", "media", "fileorder", "added", "package", "owner")) assert f.status in range(5) + assert f.owner == self.owner assert f.media in range(1024) assert f.package in self.pids assert f.added > 10 ** 6 # date is usually big integer @@ -155,8 +171,9 @@ class TestDatabase(BenchmarkTest): def assert_pack(self, p): try: assert p is not None - self.assert_int(p, ("pid", "root", "added", "status", "packageorder")) + self.assert_int(p, ("pid", "root", "added", "status", "packageorder", "owner")) assert p.pid in self.pids + assert p.owner == self.owner assert p.status in range(5) assert p.root in self.pids assert p.added > 10 ** 6 |