summaryrefslogtreecommitdiffstats
path: root/module
diff options
context:
space:
mode:
Diffstat (limited to 'module')
-rw-r--r--module/Api.py1033
-rw-r--r--module/CaptchaManager.py158
-rw-r--r--module/ConfigParser.py392
-rw-r--r--module/HookManager.py315
-rw-r--r--module/InitHomeDir.py83
-rw-r--r--module/PluginThread.py677
-rw-r--r--module/PullEvents.py120
-rw-r--r--module/PyFile.py285
-rw-r--r--module/PyPackage.py80
-rw-r--r--module/Scheduler.py141
-rw-r--r--module/ThreadManager.py318
-rw-r--r--module/__init__.py0
-rw-r--r--module/cli/AddPackage.py66
-rw-r--r--module/cli/Handler.py48
-rw-r--r--module/cli/ManageFiles.py204
-rw-r--r--module/cli/__init__.py2
-rw-r--r--module/cli/printer.py26
-rw-r--r--module/common/APIExerciser.py157
-rw-r--r--module/common/ImportDebugger.py19
-rw-r--r--module/common/JsEngine.py162
-rw-r--r--module/common/__init__.py2
-rw-r--r--module/common/json_layer.py13
-rw-r--r--module/common/packagetools.py155
-rw-r--r--module/common/pylgettext.py61
-rw-r--r--module/config/default.conf65
-rw-r--r--module/config/gui_default.xml13
-rw-r--r--module/database/DatabaseBackend.py352
-rw-r--r--module/database/FileDatabase.py944
-rw-r--r--module/database/StorageDatabase.py49
-rw-r--r--module/database/UserDatabase.py108
-rw-r--r--module/database/__init__.py6
-rw-r--r--module/debug.py95
-rw-r--r--module/forwarder.py73
-rw-r--r--module/gui/AccountEdit.py104
-rw-r--r--module/gui/Accounts.py213
-rw-r--r--module/gui/CNLServer.py226
-rw-r--r--module/gui/CaptchaDock.py94
-rw-r--r--module/gui/Collector.py407
-rw-r--r--module/gui/ConnectionManager.py302
-rw-r--r--module/gui/CoreConfigParser.py165
-rw-r--r--module/gui/LinkDock.py56
-rw-r--r--module/gui/MainWindow.py697
-rw-r--r--module/gui/Overview.py197
-rw-r--r--module/gui/PackageDock.py90
-rw-r--r--module/gui/Queue.py390
-rw-r--r--module/gui/SettingsWidget.py202
-rw-r--r--module/gui/XMLParser.py71
-rw-r--r--module/gui/__init__.py1
-rw-r--r--module/gui/connector.py165
-rw-r--r--module/lib/BeautifulSoup.py2012
-rw-r--r--module/lib/Getch.py76
-rw-r--r--module/lib/MultipartPostHandler.py139
-rw-r--r--module/lib/SafeEval.py47
-rw-r--r--module/lib/Unzip.py50
-rw-r--r--module/lib/__init__.py0
-rw-r--r--module/lib/beaker/__init__.py1
-rw-r--r--module/lib/beaker/cache.py459
-rw-r--r--module/lib/beaker/container.py633
-rw-r--r--module/lib/beaker/converters.py26
-rw-r--r--module/lib/beaker/crypto/__init__.py40
-rw-r--r--module/lib/beaker/crypto/jcecrypto.py30
-rw-r--r--module/lib/beaker/crypto/pbkdf2.py342
-rw-r--r--module/lib/beaker/crypto/pycrypto.py31
-rw-r--r--module/lib/beaker/crypto/util.py30
-rw-r--r--module/lib/beaker/exceptions.py24
-rw-r--r--module/lib/beaker/ext/__init__.py0
-rw-r--r--module/lib/beaker/ext/database.py165
-rw-r--r--module/lib/beaker/ext/google.py120
-rw-r--r--module/lib/beaker/ext/memcached.py82
-rw-r--r--module/lib/beaker/ext/sqla.py133
-rw-r--r--module/lib/beaker/middleware.py165
-rw-r--r--module/lib/beaker/session.py618
-rw-r--r--module/lib/beaker/synchronization.py381
-rw-r--r--module/lib/beaker/util.py302
-rw-r--r--module/lib/bottle.py2922
-rw-r--r--module/lib/feedparser.py3885
-rw-r--r--module/lib/jinja2/__init__.py73
-rw-r--r--module/lib/jinja2/_markupsafe/__init__.py225
-rw-r--r--module/lib/jinja2/_markupsafe/_bundle.py49
-rw-r--r--module/lib/jinja2/_markupsafe/_constants.py267
-rw-r--r--module/lib/jinja2/_markupsafe/_native.py45
-rw-r--r--module/lib/jinja2/_markupsafe/tests.py80
-rw-r--r--module/lib/jinja2/_stringdefs.py130
-rw-r--r--module/lib/jinja2/bccache.py280
-rw-r--r--module/lib/jinja2/compiler.py1640
-rw-r--r--module/lib/jinja2/constants.py32
-rw-r--r--module/lib/jinja2/debug.py308
-rw-r--r--module/lib/jinja2/defaults.py40
-rw-r--r--module/lib/jinja2/environment.py1118
-rw-r--r--module/lib/jinja2/exceptions.py143
-rw-r--r--module/lib/jinja2/ext.py610
-rw-r--r--module/lib/jinja2/filters.py719
-rw-r--r--module/lib/jinja2/lexer.py681
-rw-r--r--module/lib/jinja2/loaders.py449
-rw-r--r--module/lib/jinja2/meta.py102
-rw-r--r--module/lib/jinja2/nodes.py901
-rw-r--r--module/lib/jinja2/optimizer.py68
-rw-r--r--module/lib/jinja2/parser.py896
-rw-r--r--module/lib/jinja2/runtime.py544
-rw-r--r--module/lib/jinja2/sandbox.py271
-rw-r--r--module/lib/jinja2/tests.py146
-rw-r--r--module/lib/jinja2/utils.py601
-rw-r--r--module/lib/jinja2/visitor.py87
-rw-r--r--module/lib/rename_process.py14
-rw-r--r--module/lib/simplejson/__init__.py466
-rw-r--r--module/lib/simplejson/decoder.py421
-rw-r--r--module/lib/simplejson/encoder.py534
-rw-r--r--module/lib/simplejson/ordered_dict.py119
-rw-r--r--module/lib/simplejson/scanner.py77
-rw-r--r--module/lib/simplejson/tool.py39
-rw-r--r--module/lib/thrift/TSCons.py33
-rw-r--r--module/lib/thrift/TSerialization.py34
-rw-r--r--module/lib/thrift/Thrift.py154
-rw-r--r--module/lib/thrift/__init__.py20
-rw-r--r--module/lib/thrift/protocol/TBase.py72
-rw-r--r--module/lib/thrift/protocol/TBinaryProtocol.py259
-rw-r--r--module/lib/thrift/protocol/TCompactProtocol.py395
-rw-r--r--module/lib/thrift/protocol/TProtocol.py404
-rw-r--r--module/lib/thrift/protocol/__init__.py20
-rw-r--r--module/lib/thrift/server/THttpServer.py82
-rw-r--r--module/lib/thrift/server/TNonblockingServer.py310
-rw-r--r--module/lib/thrift/server/TProcessPoolServer.py125
-rw-r--r--module/lib/thrift/server/TServer.py274
-rw-r--r--module/lib/thrift/server/__init__.py20
-rw-r--r--module/lib/thrift/transport/THttpClient.py126
-rw-r--r--module/lib/thrift/transport/TSocket.py163
-rw-r--r--module/lib/thrift/transport/TTransport.py331
-rw-r--r--module/lib/thrift/transport/TTwisted.py219
-rw-r--r--module/lib/thrift/transport/TZlibTransport.py261
-rw-r--r--module/lib/thrift/transport/__init__.py20
-rw-r--r--module/lib/wsgiserver/LICENSE.txt25
-rw-r--r--module/lib/wsgiserver/__init__.py1794
-rw-r--r--module/network/Browser.py146
-rw-r--r--module/network/Bucket.py61
-rw-r--r--module/network/CookieJar.py50
-rw-r--r--module/network/HTTPChunk.py293
-rw-r--r--module/network/HTTPDownload.py340
-rw-r--r--module/network/HTTPRequest.py306
-rw-r--r--module/network/RequestFactory.py126
-rw-r--r--module/network/XDCCRequest.py162
-rw-r--r--module/network/__init__.py1
-rw-r--r--module/plugins/Account.py292
-rw-r--r--module/plugins/AccountManager.py185
-rw-r--r--module/plugins/Container.py75
-rw-r--r--module/plugins/Crypter.py72
-rw-r--r--module/plugins/Hook.py161
-rw-r--r--module/plugins/Hoster.py33
-rw-r--r--module/plugins/Plugin.py617
-rw-r--r--module/plugins/PluginManager.py380
-rw-r--r--module/plugins/__init__.py0
-rw-r--r--module/plugins/accounts/AlldebridCom.py59
-rw-r--r--module/plugins/accounts/BayfilesCom.py37
-rw-r--r--module/plugins/accounts/BillionuploadsCom.py16
-rw-r--r--module/plugins/accounts/BitshareCom.py32
-rw-r--r--module/plugins/accounts/CatShareNet.py56
-rw-r--r--module/plugins/accounts/CramitIn.py16
-rw-r--r--module/plugins/accounts/CzshareCom.py44
-rw-r--r--module/plugins/accounts/DebridItaliaCom.py44
-rw-r--r--module/plugins/accounts/DepositfilesCom.py35
-rw-r--r--module/plugins/accounts/EasybytezCom.py19
-rw-r--r--module/plugins/accounts/EuroshareEu.py41
-rw-r--r--module/plugins/accounts/FastixRu.py38
-rw-r--r--module/plugins/accounts/FastshareCz.py52
-rw-r--r--module/plugins/accounts/File4safeCom.py18
-rw-r--r--module/plugins/accounts/FileParadoxIn.py16
-rw-r--r--module/plugins/accounts/FilecloudIo.py59
-rw-r--r--module/plugins/accounts/FilefactoryCom.py49
-rw-r--r--module/plugins/accounts/FilejungleCom.py49
-rw-r--r--module/plugins/accounts/FileomCom.py16
-rw-r--r--module/plugins/accounts/FilerNet.py50
-rw-r--r--module/plugins/accounts/FilerioCom.py16
-rw-r--r--module/plugins/accounts/FilesMailRu.py28
-rw-r--r--module/plugins/accounts/FileserveCom.py44
-rw-r--r--module/plugins/accounts/FourSharedCom.py33
-rw-r--r--module/plugins/accounts/FreakshareCom.py43
-rw-r--r--module/plugins/accounts/FreeWayMe.py55
-rw-r--r--module/plugins/accounts/FshareVn.py63
-rw-r--r--module/plugins/accounts/Ftp.py17
-rw-r--r--module/plugins/accounts/HellshareCz.py76
-rw-r--r--module/plugins/accounts/Http.py17
-rw-r--r--module/plugins/accounts/HugefilesNet.py16
-rw-r--r--module/plugins/accounts/HundredEightyUploadCom.py16
-rw-r--r--module/plugins/accounts/JunocloudMe.py16
-rw-r--r--module/plugins/accounts/Keep2shareCc.py69
-rw-r--r--module/plugins/accounts/LetitbitNet.py34
-rw-r--r--module/plugins/accounts/LinestorageCom.py16
-rw-r--r--module/plugins/accounts/LinksnappyCom.py50
-rw-r--r--module/plugins/accounts/LomafileCom.py16
-rw-r--r--module/plugins/accounts/MegaDebridEu.py39
-rw-r--r--module/plugins/accounts/MegaRapidCz.py59
-rw-r--r--module/plugins/accounts/MegasharesCom.py48
-rw-r--r--module/plugins/accounts/MovReelCom.py19
-rw-r--r--module/plugins/accounts/MultishareCz.py44
-rw-r--r--module/plugins/accounts/MyfastfileCom.py35
-rwxr-xr-xmodule/plugins/accounts/NetloadIn.py40
-rw-r--r--module/plugins/accounts/NosuploadCom.py16
-rw-r--r--module/plugins/accounts/NovafileCom.py16
-rw-r--r--module/plugins/accounts/NowVideoAt.py56
-rw-r--r--module/plugins/accounts/OboomCom.py62
-rw-r--r--module/plugins/accounts/OneFichierCom.py55
-rw-r--r--module/plugins/accounts/OverLoadMe.py36
-rw-r--r--module/plugins/accounts/PremiumTo.py33
-rw-r--r--module/plugins/accounts/PremiumizeMe.py49
-rw-r--r--module/plugins/accounts/QuickshareCz.py43
-rw-r--r--module/plugins/accounts/RPNetBiz.py51
-rw-r--r--module/plugins/accounts/RapidfileshareNet.py18
-rw-r--r--module/plugins/accounts/RapidgatorNet.py58
-rw-r--r--module/plugins/accounts/RarefileNet.py16
-rw-r--r--module/plugins/accounts/RealdebridCom.py36
-rw-r--r--module/plugins/accounts/RehostTo.py41
-rw-r--r--module/plugins/accounts/RyushareCom.py25
-rw-r--r--module/plugins/accounts/SecureUploadEu.py16
-rw-r--r--module/plugins/accounts/SendmywayCom.py16
-rw-r--r--module/plugins/accounts/ShareonlineBiz.py45
-rw-r--r--module/plugins/accounts/SimplyPremiumCom.py46
-rw-r--r--module/plugins/accounts/SimplydebridCom.py34
-rw-r--r--module/plugins/accounts/StahnuTo.py34
-rw-r--r--module/plugins/accounts/StreamcloudEu.py16
-rw-r--r--module/plugins/accounts/TurbobitNet.py42
-rw-r--r--module/plugins/accounts/TusfilesNet.py23
-rw-r--r--module/plugins/accounts/UlozTo.py52
-rw-r--r--module/plugins/accounts/UnrestrictLi.py44
-rw-r--r--module/plugins/accounts/UploadcCom.py16
-rw-r--r--module/plugins/accounts/UploadedTo.py60
-rw-r--r--module/plugins/accounts/UploadheroCom.py41
-rw-r--r--module/plugins/accounts/UploadingCom.py63
-rw-r--r--module/plugins/accounts/UptoboxCom.py17
-rw-r--r--module/plugins/accounts/VidPlayNet.py16
-rw-r--r--module/plugins/accounts/XFileSharingPro.py30
-rw-r--r--module/plugins/accounts/YibaishiwuCom.py40
-rw-r--r--module/plugins/accounts/ZeveraCom.py56
-rw-r--r--module/plugins/accounts/__init__.py0
-rw-r--r--module/plugins/captcha/GigasizeCom.py24
-rw-r--r--module/plugins/captcha/LinksaveIn.py158
-rw-r--r--module/plugins/captcha/NetloadIn.py29
-rw-r--r--module/plugins/captcha/ShareonlineBiz.py39
-rw-r--r--module/plugins/captcha/__init__.py0
-rw-r--r--module/plugins/captcha/captcha.py317
-rw-r--r--module/plugins/container/CCF.py42
-rw-r--r--module/plugins/container/DLC_25.pycbin8346 -> 0 bytes
-rw-r--r--module/plugins/container/DLC_26.pycbin8319 -> 0 bytes
-rw-r--r--module/plugins/container/DLC_27.pycbin8243 -> 0 bytes
-rw-r--r--module/plugins/container/LinkList.py71
-rw-r--r--module/plugins/container/RSDF.py56
-rw-r--r--module/plugins/container/__init__.py0
-rw-r--r--module/plugins/crypter/BitshareComFolder.py21
-rw-r--r--module/plugins/crypter/C1neonCom.py19
-rw-r--r--module/plugins/crypter/ChipDe.py29
-rw-r--r--module/plugins/crypter/CrockoComFolder.py20
-rw-r--r--module/plugins/crypter/CryptItCom.py19
-rw-r--r--module/plugins/crypter/CzshareComFolder.py32
-rw-r--r--module/plugins/crypter/DDLMusicOrg.py51
-rw-r--r--module/plugins/crypter/DailymotionBatch.py106
-rw-r--r--module/plugins/crypter/DataHuFolder.py40
-rw-r--r--module/plugins/crypter/DdlstorageComFolder.py20
-rw-r--r--module/plugins/crypter/DepositfilesComFolder.py20
-rw-r--r--module/plugins/crypter/Dereferer.py26
-rw-r--r--module/plugins/crypter/DevhostStFolder.py58
-rw-r--r--module/plugins/crypter/DlProtectCom.py65
-rw-r--r--module/plugins/crypter/DontKnowMe.py29
-rw-r--r--module/plugins/crypter/DuckCryptInfo.py59
-rw-r--r--module/plugins/crypter/DuploadOrgFolder.py19
-rw-r--r--module/plugins/crypter/EasybytezComFolder.py22
-rw-r--r--module/plugins/crypter/EmbeduploadCom.py60
-rw-r--r--module/plugins/crypter/FilebeerInfoFolder.py19
-rw-r--r--module/plugins/crypter/FilecloudIoFolder.py21
-rw-r--r--module/plugins/crypter/FilecryptCc.py148
-rw-r--r--module/plugins/crypter/FilefactoryComFolder.py28
-rw-r--r--module/plugins/crypter/FilerNetFolder.py26
-rw-r--r--module/plugins/crypter/FileserveComFolder.py38
-rw-r--r--module/plugins/crypter/FilesonicComFolder.py18
-rw-r--r--module/plugins/crypter/FilestubeCom.py21
-rw-r--r--module/plugins/crypter/FiletramCom.py22
-rw-r--r--module/plugins/crypter/FiredriveComFolder.py19
-rw-r--r--module/plugins/crypter/FourChanOrg.py27
-rw-r--r--module/plugins/crypter/FreakhareComFolder.py38
-rw-r--r--module/plugins/crypter/FreetexthostCom.py27
-rw-r--r--module/plugins/crypter/FshareVnFolder.py20
-rw-r--r--module/plugins/crypter/GooGl.py32
-rw-r--r--module/plugins/crypter/HoerbuchIn.py62
-rw-r--r--module/plugins/crypter/HotfileComFolder.py19
-rw-r--r--module/plugins/crypter/ILoadTo.py19
-rw-r--r--module/plugins/crypter/ImgurComAlbum.py27
-rw-r--r--module/plugins/crypter/JunocloudMeFolder.py20
-rw-r--r--module/plugins/crypter/LetitbitNetFolder.py33
-rw-r--r--module/plugins/crypter/LinkCryptWs.py327
-rw-r--r--module/plugins/crypter/LinkSaveIn.py246
-rw-r--r--module/plugins/crypter/LinkdecrypterCom.py92
-rw-r--r--module/plugins/crypter/LixIn.py62
-rw-r--r--module/plugins/crypter/LofCc.py19
-rw-r--r--module/plugins/crypter/MBLinkInfo.py20
-rw-r--r--module/plugins/crypter/MediafireComFolder.py58
-rw-r--r--module/plugins/crypter/MegaRapidCzFolder.py20
-rw-r--r--module/plugins/crypter/MegauploadComFolder.py18
-rw-r--r--module/plugins/crypter/Movie2kTo.py19
-rw-r--r--module/plugins/crypter/MultiUpOrg.py38
-rw-r--r--module/plugins/crypter/MultiloadCz.py42
-rw-r--r--module/plugins/crypter/MultiuploadCom.py19
-rw-r--r--module/plugins/crypter/NCryptIn.py315
-rw-r--r--module/plugins/crypter/NetfolderIn.py70
-rw-r--r--module/plugins/crypter/NosvideoCom.py21
-rw-r--r--module/plugins/crypter/OneKhDe.py41
-rwxr-xr-xmodule/plugins/crypter/OronComFolder.py19
-rw-r--r--module/plugins/crypter/PastebinCom.py21
-rw-r--r--module/plugins/crypter/QuickshareCzFolder.py31
-rw-r--r--module/plugins/crypter/RSLayerCom.py19
-rw-r--r--module/plugins/crypter/RapidfileshareNetFolder.py20
-rw-r--r--module/plugins/crypter/RelinkUs.py284
-rw-r--r--module/plugins/crypter/SafelinkingNet.py81
-rw-r--r--module/plugins/crypter/SecuredIn.py19
-rw-r--r--module/plugins/crypter/SexuriaCom.py94
-rw-r--r--module/plugins/crypter/ShareLinksBiz.py286
-rw-r--r--module/plugins/crypter/SharingmatrixComFolder.py18
-rw-r--r--module/plugins/crypter/SpeedLoadOrgFolder.py19
-rw-r--r--module/plugins/crypter/StealthTo.py19
-rw-r--r--module/plugins/crypter/TnyCz.py27
-rw-r--r--module/plugins/crypter/TrailerzoneInfo.py19
-rw-r--r--module/plugins/crypter/TurbobitNetFolder.py44
-rw-r--r--module/plugins/crypter/TusfilesNetFolder.py45
-rw-r--r--module/plugins/crypter/UlozToFolder.py46
-rw-r--r--module/plugins/crypter/UploadableChFolder.py24
-rw-r--r--module/plugins/crypter/UploadedToFolder.py34
-rw-r--r--module/plugins/crypter/WiiReloadedOrg.py19
-rw-r--r--module/plugins/crypter/WuploadComFolder.py18
-rw-r--r--module/plugins/crypter/XFileSharingProFolder.py47
-rw-r--r--module/plugins/crypter/XupPl.py25
-rw-r--r--module/plugins/crypter/YoutubeBatch.py148
-rw-r--r--module/plugins/crypter/__init__.py0
-rw-r--r--module/plugins/hooks/AlldebridCom.py27
-rw-r--r--module/plugins/hooks/BypassCaptcha.py139
-rwxr-xr-xmodule/plugins/hooks/Captcha9kw.py256
-rw-r--r--module/plugins/hooks/CaptchaBrotherhood.py171
-rw-r--r--module/plugins/hooks/Checksum.py185
-rw-r--r--module/plugins/hooks/ClickAndLoad.py77
-rw-r--r--module/plugins/hooks/DeathByCaptcha.py218
-rw-r--r--module/plugins/hooks/DebridItaliaCom.py27
-rw-r--r--module/plugins/hooks/DeleteFinished.py79
-rw-r--r--module/plugins/hooks/DownloadScheduler.py78
-rw-r--r--module/plugins/hooks/EasybytezCom.py39
-rw-r--r--module/plugins/hooks/ExpertDecoders.py98
-rw-r--r--module/plugins/hooks/ExternalScripts.py141
-rw-r--r--module/plugins/hooks/ExtractArchive.py362
-rw-r--r--module/plugins/hooks/FastixRu.py28
-rw-r--r--module/plugins/hooks/FreeWayMe.py25
-rw-r--r--module/plugins/hooks/HotFolder.py66
-rw-r--r--module/plugins/hooks/IRCInterface.py431
-rw-r--r--module/plugins/hooks/ImageTyperz.py157
-rw-r--r--module/plugins/hooks/LinkdecrypterCom.py60
-rw-r--r--module/plugins/hooks/LinksnappyCom.py27
-rw-r--r--module/plugins/hooks/MegaDebridEu.py30
-rw-r--r--module/plugins/hooks/MergeFiles.py79
-rw-r--r--module/plugins/hooks/MultiHome.py82
-rw-r--r--module/plugins/hooks/MultishareCz.py27
-rw-r--r--module/plugins/hooks/MyfastfileCom.py28
-rw-r--r--module/plugins/hooks/OverLoadMe.py29
-rw-r--r--module/plugins/hooks/PremiumTo.py37
-rw-r--r--module/plugins/hooks/PremiumizeMe.py54
-rw-r--r--module/plugins/hooks/RPNetBiz.py52
-rw-r--r--module/plugins/hooks/RealdebridCom.py27
-rw-r--r--module/plugins/hooks/RehostTo.py41
-rw-r--r--module/plugins/hooks/RestartFailed.py44
-rw-r--r--module/plugins/hooks/SimplyPremiumCom.py30
-rw-r--r--module/plugins/hooks/SimplydebridCom.py22
-rw-r--r--module/plugins/hooks/UnSkipOnFail.py87
-rw-r--r--module/plugins/hooks/UnrestrictLi.py30
-rw-r--r--module/plugins/hooks/UpdateManager.py310
-rw-r--r--module/plugins/hooks/WindowsPhoneToastNotify.py61
-rw-r--r--module/plugins/hooks/XFileSharingPro.py98
-rw-r--r--module/plugins/hooks/XMPPInterface.py252
-rw-r--r--module/plugins/hooks/ZeveraCom.py22
-rw-r--r--module/plugins/hooks/__init__.py0
-rw-r--r--module/plugins/hoster/AlldebridCom.py87
-rw-r--r--module/plugins/hoster/BasePlugin.py106
-rw-r--r--module/plugins/hoster/BayfilesCom.py87
-rw-r--r--module/plugins/hoster/BezvadataCz.py94
-rw-r--r--module/plugins/hoster/BillionuploadsCom.py24
-rw-r--r--module/plugins/hoster/BitshareCom.py155
-rw-r--r--module/plugins/hoster/BoltsharingCom.py18
-rw-r--r--module/plugins/hoster/CatShareNet.py67
-rw-r--r--module/plugins/hoster/CloudzerNet.py20
-rw-r--r--module/plugins/hoster/CramitIn.py24
-rw-r--r--module/plugins/hoster/CrockoCom.py70
-rw-r--r--module/plugins/hoster/CyberlockerCh.py18
-rw-r--r--module/plugins/hoster/CzshareCom.py152
-rw-r--r--module/plugins/hoster/DailymotionCom.py125
-rw-r--r--module/plugins/hoster/DataHu.py42
-rw-r--r--module/plugins/hoster/DataportCz.py55
-rw-r--r--module/plugins/hoster/DateiTo.py82
-rw-r--r--module/plugins/hoster/DdlstorageCom.py19
-rw-r--r--module/plugins/hoster/DebridItaliaCom.py53
-rw-r--r--module/plugins/hoster/DepositfilesCom.py123
-rw-r--r--module/plugins/hoster/DevhostSt.py48
-rw-r--r--module/plugins/hoster/DlFreeFr.py214
-rw-r--r--module/plugins/hoster/DropboxCom.py42
-rw-r--r--module/plugins/hoster/DuploadOrg.py18
-rw-r--r--module/plugins/hoster/EasybytezCom.py26
-rw-r--r--module/plugins/hoster/EdiskCz.py56
-rw-r--r--module/plugins/hoster/EgoFilesCom.py18
-rw-r--r--module/plugins/hoster/EnteruploadCom.py18
-rw-r--r--module/plugins/hoster/EpicShareNet.py18
-rw-r--r--module/plugins/hoster/EuroshareEu.py67
-rw-r--r--module/plugins/hoster/ExtabitCom.py79
-rw-r--r--module/plugins/hoster/FastixRu.py76
-rw-r--r--module/plugins/hoster/FastshareCz.py78
-rw-r--r--module/plugins/hoster/FileApeCom.py18
-rw-r--r--module/plugins/hoster/FileParadoxIn.py25
-rw-r--r--module/plugins/hoster/FileSharkPl.py140
-rw-r--r--module/plugins/hoster/FileStoreTo.py37
-rw-r--r--module/plugins/hoster/FilebeerInfo.py18
-rw-r--r--module/plugins/hoster/FilecloudIo.py125
-rw-r--r--module/plugins/hoster/FilefactoryCom.py90
-rw-r--r--module/plugins/hoster/FilejungleCom.py29
-rw-r--r--module/plugins/hoster/FileomCom.py35
-rw-r--r--module/plugins/hoster/FilepostCom.py127
-rw-r--r--module/plugins/hoster/FilepupNet.py51
-rw-r--r--module/plugins/hoster/FilerNet.py81
-rw-r--r--module/plugins/hoster/FilerioCom.py25
-rw-r--r--module/plugins/hoster/FilesMailRu.py106
-rw-r--r--module/plugins/hoster/FileserveCom.py217
-rw-r--r--module/plugins/hoster/FileshareInUa.py18
-rw-r--r--module/plugins/hoster/FilesonicCom.py19
-rw-r--r--module/plugins/hoster/FilezyNet.py18
-rw-r--r--module/plugins/hoster/FiredriveCom.py18
-rw-r--r--module/plugins/hoster/FlyFilesNet.py45
-rw-r--r--module/plugins/hoster/FourSharedCom.py61
-rw-r--r--module/plugins/hoster/FreakshareCom.py176
-rw-r--r--module/plugins/hoster/FreeWayMe.py36
-rw-r--r--module/plugins/hoster/FreevideoCz.py18
-rw-r--r--module/plugins/hoster/FshareVn.py125
-rw-r--r--module/plugins/hoster/Ftp.py79
-rw-r--r--module/plugins/hoster/GamefrontCom.py90
-rw-r--r--module/plugins/hoster/GigapetaCom.py67
-rw-r--r--module/plugins/hoster/GooIm.py39
-rw-r--r--module/plugins/hoster/HellshareCz.py48
-rw-r--r--module/plugins/hoster/HellspyCz.py18
-rw-r--r--module/plugins/hoster/HotfileCom.py21
-rw-r--r--module/plugins/hoster/HugefilesNet.py27
-rw-r--r--module/plugins/hoster/HundredEightyUploadCom.py27
-rw-r--r--module/plugins/hoster/IFileWs.py18
-rw-r--r--module/plugins/hoster/IcyFilesCom.py18
-rw-r--r--module/plugins/hoster/IfileIt.py64
-rw-r--r--module/plugins/hoster/IfolderRu.py76
-rw-r--r--module/plugins/hoster/JumbofilesCom.py38
-rw-r--r--module/plugins/hoster/JunocloudMe.py28
-rw-r--r--module/plugins/hoster/Keep2shareCc.py135
-rw-r--r--module/plugins/hoster/KickloadCom.py18
-rw-r--r--module/plugins/hoster/KingfilesNet.py82
-rw-r--r--module/plugins/hoster/LemUploadsCom.py18
-rw-r--r--module/plugins/hoster/LetitbitNet.py142
-rw-r--r--module/plugins/hoster/LinksnappyCom.py76
-rw-r--r--module/plugins/hoster/LoadTo.py72
-rw-r--r--module/plugins/hoster/LomafileCom.py30
-rw-r--r--module/plugins/hoster/LuckyShareNet.py73
-rw-r--r--module/plugins/hoster/MediafireCom.py122
-rw-r--r--module/plugins/hoster/MegaCoNz.py171
-rw-r--r--module/plugins/hoster/MegaDebridEu.py94
-rw-r--r--module/plugins/hoster/MegaFilesSe.py18
-rw-r--r--module/plugins/hoster/MegaRapidCz.py71
-rw-r--r--module/plugins/hoster/MegacrypterCom.py56
-rw-r--r--module/plugins/hoster/MegareleaseOrg.py19
-rw-r--r--module/plugins/hoster/MegasharesCom.py113
-rw-r--r--module/plugins/hoster/MegauploadCom.py18
-rw-r--r--module/plugins/hoster/MegavideoCom.py19
-rw-r--r--module/plugins/hoster/MovReelCom.py26
-rw-r--r--module/plugins/hoster/MultishareCz.py80
-rw-r--r--module/plugins/hoster/MyfastfileCom.py46
-rw-r--r--module/plugins/hoster/MyvideoDe.py49
-rw-r--r--module/plugins/hoster/NahrajCz.py18
-rw-r--r--module/plugins/hoster/NarodRu.py60
-rw-r--r--module/plugins/hoster/NetloadIn.py294
-rw-r--r--module/plugins/hoster/NosuploadCom.py43
-rw-r--r--module/plugins/hoster/NovafileCom.py31
-rw-r--r--module/plugins/hoster/OboomCom.py145
-rw-r--r--module/plugins/hoster/OneFichierCom.py71
-rw-r--r--module/plugins/hoster/OronCom.py19
-rw-r--r--module/plugins/hoster/OverLoadMe.py84
-rw-r--r--module/plugins/hoster/PandaplaNet.py18
-rw-r--r--module/plugins/hoster/PornhostCom.py80
-rw-r--r--module/plugins/hoster/PornhubCom.py89
-rw-r--r--module/plugins/hoster/PotloadCom.py18
-rw-r--r--module/plugins/hoster/PremiumTo.py81
-rw-r--r--module/plugins/hoster/PremiumizeMe.py56
-rw-r--r--module/plugins/hoster/PromptfileCom.py45
-rw-r--r--module/plugins/hoster/PrzeklejPl.py18
-rw-r--r--module/plugins/hoster/QuickshareCz.py94
-rw-r--r--module/plugins/hoster/RPNetBiz.py85
-rw-r--r--module/plugins/hoster/RapidfileshareNet.py31
-rw-r--r--module/plugins/hoster/RapidgatorNet.py201
-rw-r--r--module/plugins/hoster/RarefileNet.py28
-rw-r--r--module/plugins/hoster/RealdebridCom.py94
-rw-r--r--module/plugins/hoster/RedtubeCom.py62
-rw-r--r--module/plugins/hoster/RehostTo.py44
-rw-r--r--module/plugins/hoster/RemixshareCom.py61
-rw-r--r--module/plugins/hoster/RgHostNet.py26
-rw-r--r--module/plugins/hoster/RyushareCom.py81
-rw-r--r--module/plugins/hoster/SecureUploadEu.py23
-rw-r--r--module/plugins/hoster/SendmywayCom.py24
-rw-r--r--module/plugins/hoster/SendspaceCom.py60
-rw-r--r--module/plugins/hoster/Share4webCom.py22
-rw-r--r--module/plugins/hoster/Share76Com.py18
-rw-r--r--module/plugins/hoster/ShareFilesCo.py18
-rw-r--r--module/plugins/hoster/SharebeesCom.py18
-rw-r--r--module/plugins/hoster/ShareonlineBiz.py191
-rw-r--r--module/plugins/hoster/ShareplaceCom.py89
-rw-r--r--module/plugins/hoster/SharingmatrixCom.py19
-rw-r--r--module/plugins/hoster/ShragleCom.py19
-rw-r--r--module/plugins/hoster/SimplyPremiumCom.py82
-rw-r--r--module/plugins/hoster/SimplydebridCom.py64
-rw-r--r--module/plugins/hoster/SockshareCom.py20
-rw-r--r--module/plugins/hoster/SoundcloudCom.py57
-rw-r--r--module/plugins/hoster/SpeedLoadOrg.py18
-rw-r--r--module/plugins/hoster/SpeedfileCz.py18
-rw-r--r--module/plugins/hoster/SpeedyshareCom.py51
-rw-r--r--module/plugins/hoster/StorageTo.py18
-rw-r--r--module/plugins/hoster/StreamCz.py71
-rw-r--r--module/plugins/hoster/StreamcloudEu.py31
-rw-r--r--module/plugins/hoster/TurbobitNet.py173
-rw-r--r--module/plugins/hoster/TurbouploadCom.py18
-rw-r--r--module/plugins/hoster/TusfilesNet.py35
-rw-r--r--module/plugins/hoster/TwoSharedCom.py41
-rw-r--r--module/plugins/hoster/UlozTo.py164
-rw-r--r--module/plugins/hoster/UloziskoSk.py72
-rw-r--r--module/plugins/hoster/UnibytesCom.py74
-rw-r--r--module/plugins/hoster/UnrestrictLi.py91
-rw-r--r--module/plugins/hoster/UpleaCom.py60
-rw-r--r--module/plugins/hoster/UploadStationCom.py19
-rw-r--r--module/plugins/hoster/UploadboxCom.py18
-rw-r--r--module/plugins/hoster/UploadedTo.py245
-rw-r--r--module/plugins/hoster/UploadhereCom.py18
-rw-r--r--module/plugins/hoster/UploadheroCom.py81
-rw-r--r--module/plugins/hoster/UploadingCom.py104
-rw-r--r--module/plugins/hoster/UploadkingCom.py18
-rw-r--r--module/plugins/hoster/UpstoreNet.py73
-rw-r--r--module/plugins/hoster/UptoboxCom.py34
-rw-r--r--module/plugins/hoster/VeehdCom.py81
-rw-r--r--module/plugins/hoster/VeohCom.py53
-rw-r--r--module/plugins/hoster/VidPlayNet.py26
-rw-r--r--module/plugins/hoster/VimeoCom.py75
-rw-r--r--module/plugins/hoster/Vipleech4uCom.py18
-rw-r--r--module/plugins/hoster/WarserverCz.py18
-rw-r--r--module/plugins/hoster/WebshareCz.py62
-rw-r--r--module/plugins/hoster/WrzucTo.py52
-rw-r--r--module/plugins/hoster/WuploadCom.py19
-rw-r--r--module/plugins/hoster/X7To.py18
-rw-r--r--module/plugins/hoster/XFileSharingPro.py57
-rw-r--r--module/plugins/hoster/XHamsterCom.py129
-rw-r--r--module/plugins/hoster/XVideosCom.py28
-rw-r--r--module/plugins/hoster/Xdcc.py207
-rw-r--r--module/plugins/hoster/YibaishiwuCom.py55
-rw-r--r--module/plugins/hoster/YoupornCom.py60
-rw-r--r--module/plugins/hoster/YourfilesTo.py87
-rw-r--r--module/plugins/hoster/YoutubeCom.py185
-rw-r--r--module/plugins/hoster/ZDF.py59
-rw-r--r--module/plugins/hoster/ZShareNet.py19
-rw-r--r--module/plugins/hoster/ZeveraCom.py42
-rw-r--r--module/plugins/hoster/ZippyshareCom.py65
-rw-r--r--module/plugins/hoster/__init__.py0
-rw-r--r--module/plugins/internal/AbstractExtractor.py109
-rw-r--r--module/plugins/internal/CaptchaService.py213
-rw-r--r--module/plugins/internal/DeadCrypter.py32
-rw-r--r--module/plugins/internal/DeadHoster.py32
-rw-r--r--module/plugins/internal/MultiHoster.py206
-rw-r--r--module/plugins/internal/SimpleCrypter.py165
-rw-r--r--module/plugins/internal/SimpleHoster.py569
-rw-r--r--module/plugins/internal/UnRar.py221
-rw-r--r--module/plugins/internal/UnZip.py41
-rw-r--r--module/plugins/internal/XFSAccount.py160
-rw-r--r--module/plugins/internal/XFSCrypter.py29
-rw-r--r--module/plugins/internal/XFSHoster.py345
-rw-r--r--module/plugins/internal/__init__.py0
-rw-r--r--module/remote/ClickAndLoadBackend.py170
-rw-r--r--module/remote/RemoteManager.py91
-rw-r--r--module/remote/SocketBackend.py25
-rw-r--r--module/remote/ThriftBackend.py56
-rw-r--r--module/remote/__init__.py2
-rw-r--r--module/remote/socketbackend/__init__.py2
-rw-r--r--module/remote/socketbackend/create_ttypes.py91
-rw-r--r--module/remote/socketbackend/ttypes.py383
-rw-r--r--module/remote/thriftbackend/Processor.py77
-rw-r--r--module/remote/thriftbackend/Protocol.py30
-rw-r--r--module/remote/thriftbackend/Socket.py129
-rw-r--r--module/remote/thriftbackend/ThriftClient.py109
-rw-r--r--module/remote/thriftbackend/ThriftTest.py92
-rw-r--r--module/remote/thriftbackend/Transport.py37
-rw-r--r--module/remote/thriftbackend/__init__.py0
-rw-r--r--module/remote/thriftbackend/pyload.thrift337
-rw-r--r--module/remote/thriftbackend/thriftgen/__init__.py0
-rwxr-xr-xmodule/remote/thriftbackend/thriftgen/pyload/Pyload-remote571
-rw-r--r--module/remote/thriftbackend/thriftgen/pyload/Pyload.py5534
-rw-r--r--module/remote/thriftbackend/thriftgen/pyload/__init__.py1
-rw-r--r--module/remote/thriftbackend/thriftgen/pyload/constants.py11
-rw-r--r--module/remote/thriftbackend/thriftgen/pyload/ttypes.py835
-rw-r--r--module/setup.py514
-rw-r--r--module/unescape.py3
-rw-r--r--module/utils.py201
-rw-r--r--module/web/ServerThread.py109
-rw-r--r--module/web/__init__.py0
-rw-r--r--module/web/api_app.py102
-rw-r--r--module/web/cnl_app.py164
-rw-r--r--module/web/filters.py62
-rw-r--r--module/web/json_app.py312
-rw-r--r--module/web/media/default/css/MooDialog.css92
-rw-r--r--module/web/media/default/css/default.css908
-rw-r--r--module/web/media/default/css/log.css72
-rw-r--r--module/web/media/default/css/pathchooser.css68
-rw-r--r--module/web/media/default/css/window.css73
-rw-r--r--module/web/media/default/img/add_folder.pngbin571 -> 0 bytes
-rw-r--r--module/web/media/default/img/ajax-loader.gifbin404 -> 0 bytes
-rw-r--r--module/web/media/default/img/arrow_refresh.pngbin685 -> 0 bytes
-rw-r--r--module/web/media/default/img/arrow_right.pngbin349 -> 0 bytes
-rw-r--r--module/web/media/default/img/big_button.gifbin1905 -> 0 bytes
-rw-r--r--module/web/media/default/img/big_button_over.gifbin728 -> 0 bytes
-rw-r--r--module/web/media/default/img/body.pngbin402 -> 0 bytes
-rw-r--r--module/web/media/default/img/button.pngbin452 -> 0 bytes
-rw-r--r--module/web/media/default/img/closebtn.gifbin254 -> 0 bytes
-rw-r--r--module/web/media/default/img/cog.pngbin512 -> 0 bytes
-rw-r--r--module/web/media/default/img/control_add.pngbin446 -> 0 bytes
-rw-r--r--module/web/media/default/img/control_add_blue.pngbin845 -> 0 bytes
-rw-r--r--module/web/media/default/img/control_cancel.pngbin3349 -> 0 bytes
-rw-r--r--module/web/media/default/img/control_cancel_blue.pngbin787 -> 0 bytes
-rw-r--r--module/web/media/default/img/control_pause.pngbin598 -> 0 bytes
-rw-r--r--module/web/media/default/img/control_pause_blue.pngbin721 -> 0 bytes
-rw-r--r--module/web/media/default/img/control_play.pngbin592 -> 0 bytes
-rw-r--r--module/web/media/default/img/control_play_blue.pngbin717 -> 0 bytes
-rw-r--r--module/web/media/default/img/control_stop.pngbin403 -> 0 bytes
-rw-r--r--module/web/media/default/img/control_stop_blue.pngbin695 -> 0 bytes
-rw-r--r--module/web/media/default/img/delete.pngbin715 -> 0 bytes
-rw-r--r--module/web/media/default/img/drag_corner.gifbin76 -> 0 bytes
-rw-r--r--module/web/media/default/img/error.pngbin701 -> 0 bytes
-rw-r--r--module/web/media/default/img/folder.pngbin537 -> 0 bytes
-rw-r--r--module/web/media/default/img/full.pngbin3543 -> 0 bytes
-rw-r--r--module/web/media/default/img/head-login.pngbin1288 -> 0 bytes
-rw-r--r--module/web/media/default/img/head-menu-collector.pngbin1953 -> 0 bytes
-rw-r--r--module/web/media/default/img/head-menu-config.pngbin1802 -> 0 bytes
-rw-r--r--module/web/media/default/img/head-menu-development.pngbin876 -> 0 bytes
-rw-r--r--module/web/media/default/img/head-menu-download.pngbin721 -> 0 bytes
-rw-r--r--module/web/media/default/img/head-menu-home.pngbin920 -> 0 bytes
-rw-r--r--module/web/media/default/img/head-menu-index.pngbin482 -> 0 bytes
-rw-r--r--module/web/media/default/img/head-menu-news.pngbin628 -> 0 bytes
-rw-r--r--module/web/media/default/img/head-menu-queue.pngbin2629 -> 0 bytes
-rw-r--r--module/web/media/default/img/head-menu-recent.pngbin932 -> 0 bytes
-rw-r--r--module/web/media/default/img/head-menu-wiki.pngbin1204 -> 0 bytes
-rw-r--r--module/web/media/default/img/head-search-noshadow.pngbin1187 -> 0 bytes
-rw-r--r--module/web/media/default/img/head_bg1.pngbin125 -> 0 bytes
-rw-r--r--module/web/media/default/img/images.pngbin661 -> 0 bytes
-rw-r--r--module/web/media/default/img/notice.pngbin778 -> 0 bytes
-rw-r--r--module/web/media/default/img/package_go.pngbin898 -> 0 bytes
-rw-r--r--module/web/media/default/img/page-tools-backlinks.pngbin540 -> 0 bytes
-rw-r--r--module/web/media/default/img/page-tools-edit.pngbin574 -> 0 bytes
-rw-r--r--module/web/media/default/img/page-tools-revisions.pngbin603 -> 0 bytes
-rw-r--r--module/web/media/default/img/parseUri.pngbin666 -> 0 bytes
-rw-r--r--module/web/media/default/img/pencil.pngbin450 -> 0 bytes
-rw-r--r--module/web/media/default/img/pyload-logo-edited3.5-new-font-small.pngbin8457 -> 0 bytes
-rw-r--r--module/web/media/default/img/reconnect.pngbin755 -> 0 bytes
-rw-r--r--module/web/media/default/img/status_None.pngbin7613 -> 0 bytes
-rw-r--r--module/web/media/default/img/status_downloading.pngbin943 -> 0 bytes
-rw-r--r--module/web/media/default/img/status_failed.pngbin701 -> 0 bytes
-rw-r--r--module/web/media/default/img/status_finished.pngbin781 -> 0 bytes
-rw-r--r--module/web/media/default/img/status_offline.pngbin700 -> 0 bytes
-rw-r--r--module/web/media/default/img/status_proc.pngbin512 -> 0 bytes
-rw-r--r--module/web/media/default/img/status_queue.pngbin7613 -> 0 bytes
-rw-r--r--module/web/media/default/img/status_waiting.pngbin889 -> 0 bytes
-rw-r--r--module/web/media/default/img/success.pngbin781 -> 0 bytes
-rw-r--r--module/web/media/default/img/tab-background.pngbin179 -> 0 bytes
-rw-r--r--module/web/media/default/img/tabs-border-bottom.pngbin163 -> 0 bytes
-rw-r--r--module/web/media/default/img/user-actions-logout.pngbin799 -> 0 bytes
-rw-r--r--module/web/media/default/img/user-actions-profile.pngbin628 -> 0 bytes
-rw-r--r--module/web/media/default/img/user-info.pngbin3963 -> 0 bytes
-rw-r--r--module/web/media/img/dialog-close.pngbin689 -> 0 bytes
-rw-r--r--module/web/media/img/dialog-question.pngbin2073 -> 0 bytes
-rw-r--r--module/web/media/img/favicon.icobin7206 -> 0 bytes
-rw-r--r--module/web/media/js/MooDialog_static.js401
-rw-r--r--module/web/media/js/MooDropMenu_static.js89
-rw-r--r--module/web/media/js/admin.coffee58
-rw-r--r--module/web/media/js/admin.js3
-rw-r--r--module/web/media/js/base.coffee173
-rw-r--r--module/web/media/js/base.js3
-rw-r--r--module/web/media/js/mootools-core-1.4.1.js476
-rw-r--r--module/web/media/js/mootools-more-1.4.0.1.js216
-rw-r--r--module/web/media/js/package_ui.js377
-rw-r--r--module/web/media/js/purr_static.js308
-rw-r--r--module/web/media/js/settings.coffee107
-rw-r--r--module/web/media/js/settings.js3
-rw-r--r--module/web/media/js/tinytab_static.js50
-rw-r--r--module/web/middlewares.py133
-rw-r--r--module/web/pyload_app.py533
-rw-r--r--module/web/servers/lighttpd_default.conf153
-rw-r--r--module/web/servers/nginx_default.conf87
-rw-r--r--module/web/templates/500.html10
-rw-r--r--module/web/templates/default/admin.html98
-rw-r--r--module/web/templates/default/base.html180
-rw-r--r--module/web/templates/default/captcha.html42
-rw-r--r--module/web/templates/default/downloads.html29
-rw-r--r--module/web/templates/default/filemanager.html80
-rw-r--r--module/web/templates/default/filemanager_ui.js291
-rw-r--r--module/web/templates/default/folder.html15
-rw-r--r--module/web/templates/default/home.html266
-rw-r--r--module/web/templates/default/info.html81
-rw-r--r--module/web/templates/default/login.html36
-rw-r--r--module/web/templates/default/logout.html9
-rw-r--r--module/web/templates/default/logs.html41
-rw-r--r--module/web/templates/default/pathchooser.html76
-rw-r--r--module/web/templates/default/queue.html104
-rw-r--r--module/web/templates/default/settings.html204
-rw-r--r--module/web/templates/default/settings_item.html48
-rw-r--r--module/web/templates/default/setup.html13
-rw-r--r--module/web/templates/default/window.html46
-rw-r--r--module/web/utils.py137
-rw-r--r--module/web/webinterface.py157
709 files changed, 0 insertions, 85247 deletions
diff --git a/module/Api.py b/module/Api.py
deleted file mode 100644
index f0bf5e264..000000000
--- a/module/Api.py
+++ /dev/null
@@ -1,1033 +0,0 @@
-#!/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
-"""
-
-from base64 import standard_b64encode
-from os.path import join
-from time import time
-import re
-
-from PyFile import PyFile
-from utils import freeSpace, compare_time
-from common.packagetools import parseNames
-from network.RequestFactory import getURL
-from remote import activated
-
-if activated:
- try:
- from remote.thriftbackend.thriftgen.pyload.ttypes import *
- from remote.thriftbackend.thriftgen.pyload.Pyload import Iface
- BaseObject = TBase
- except ImportError:
- print "Thrift not imported"
- from remote.socketbackend.ttypes import *
-else:
- from remote.socketbackend.ttypes import *
-
-# contains function names mapped to their permissions
-# unlisted functions are for admins only
-permMap = {}
-
-# decorator only called on init, never initialized, so has no effect on runtime
-def permission(bits):
- class _Dec(object):
- def __new__(cls, func, *args, **kwargs):
- permMap[func.__name__] = bits
- return func
-
- return _Dec
-
-
-urlmatcher = re.compile(r"((https?|ftps?|xdcc|sftp):((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-=\\\.&]*)", re.IGNORECASE)
-
-class PERMS:
- 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 queue and collector
- MODIFY = 32 # moddify some attribute of downloads
- DOWNLOAD = 64 # can download from webinterface
- SETTINGS = 128 # can access settings
- ACCOUNTS = 256 # can access accounts
- LOGS = 512 # can see server logs
-
-class ROLE:
- ADMIN = 0 #admin has all permissions implicit
- USER = 1
-
-def has_permission(userperms, perms):
- # bytewise or perms before if needed
- return perms == (userperms & perms)
-
-
-class Api(Iface):
- """
- **pyLoads API**
-
- This is accessible either internal via core.api or via thrift backend.
-
- see Thrift specification file remote/thriftbackend/pyload.thrift\
- for information about data structures and what methods are usuable with rpc.
-
- Most methods requires specific permissions, please look at the source code if you need to know.\
- These can be configured via webinterface.
- Admin user have all permissions, and are the only ones who can access the methods with no specific permission.
- """
-
- EXTERNAL = Iface # let the json api know which methods are external
-
- def __init__(self, core):
- self.core = core
-
- def _convertPyFile(self, p):
- f = FileData(p["id"], p["url"], p["name"], p["plugin"], p["size"],
- p["format_size"], p["status"], p["statusmsg"],
- p["package"], p["error"], p["order"])
- return f
-
- def _convertConfigFormat(self, c):
- sections = {}
- for sectionName, sub in c.iteritems():
- section = ConfigSection(sectionName, sub["desc"])
- items = []
- for key, data in sub.iteritems():
- if key in ("desc", "outline"):
- continue
- item = ConfigItem()
- item.name = key
- item.description = data["desc"]
- item.value = str(data["value"]) if not isinstance(data["value"], basestring) else data["value"]
- item.type = data["type"]
- items.append(item)
- section.items = items
- sections[sectionName] = section
- if "outline" in sub:
- section.outline = sub["outline"]
- return sections
-
- @permission(PERMS.SETTINGS)
- def getConfigValue(self, category, option, section="core"):
- """Retrieve config value.
-
- :param category: name of category, or plugin
- :param option: config option
- :param section: 'plugin' or 'core'
- :return: config value as string
- """
- if section == "core":
- value = self.core.config[category][option]
- else:
- value = self.core.config.getPlugin(category, option)
-
- return str(value) if not isinstance(value, basestring) else value
-
- @permission(PERMS.SETTINGS)
- def setConfigValue(self, category, option, value, section="core"):
- """Set new config value.
-
- :param category:
- :param option:
- :param value: new config value
- :param section: 'plugin' or 'core
- """
- self.core.hookManager.dispatchEvent("configChanged", category, option, value, section)
-
- if section == "core":
- self.core.config[category][option] = value
-
- if option in ("limit_speed", "max_speed"): #not so nice to update the limit
- self.core.requestFactory.updateBucket()
-
- elif section == "plugin":
- self.core.config.setPlugin(category, option, value)
-
- @permission(PERMS.SETTINGS)
- def getConfig(self):
- """Retrieves complete config of core.
-
- :return: list of `ConfigSection`
- """
- return self._convertConfigFormat(self.core.config.config)
-
- def getConfigDict(self):
- """Retrieves complete config in dict format, not for RPC.
-
- :return: dict
- """
- return self.core.config.config
-
- @permission(PERMS.SETTINGS)
- def getPluginConfig(self):
- """Retrieves complete config for all plugins.
-
- :return: list of `ConfigSection`
- """
- return self._convertConfigFormat(self.core.config.plugin)
-
- def getPluginConfigDict(self):
- """Plugin config as dict, not for RPC.
-
- :return: dict
- """
- return self.core.config.plugin
-
-
- @permission(PERMS.STATUS)
- def pauseServer(self):
- """Pause server: Tt wont start any new downloads, but nothing gets aborted."""
- self.core.threadManager.pause = True
-
- @permission(PERMS.STATUS)
- def unpauseServer(self):
- """Unpause server: New Downloads will be started."""
- self.core.threadManager.pause = False
-
- @permission(PERMS.STATUS)
- def togglePause(self):
- """Toggle pause state.
-
- :return: new pause state
- """
- self.core.threadManager.pause ^= True
- return self.core.threadManager.pause
-
- @permission(PERMS.STATUS)
- def toggleReconnect(self):
- """Toggle reconnect activation.
-
- :return: new reconnect state
- """
- self.core.config["reconnect"]["activated"] ^= True
- return self.core.config["reconnect"]["activated"]
-
- @permission(PERMS.LIST)
- def statusServer(self):
- """Some general information about the current status of pyLoad.
-
- :return: `ServerStatus`
- """
- serverStatus = ServerStatus(self.core.threadManager.pause, len(self.core.threadManager.processingIds()),
- self.core.files.getQueueCount(), self.core.files.getFileCount(), 0,
- not self.core.threadManager.pause and self.isTimeDownload(),
- self.core.config['reconnect']['activated'] and self.isTimeReconnect())
-
- for pyfile in [x.active for x in self.core.threadManager.threads if x.active and isinstance(x.active, PyFile)]:
- serverStatus.speed += pyfile.getSpeed() #bytes/s
-
- return serverStatus
-
- @permission(PERMS.STATUS)
- def freeSpace(self):
- """Available free space at download directory in bytes"""
- return freeSpace(self.core.config["general"]["download_folder"])
-
- @permission(PERMS.ALL)
- def getServerVersion(self):
- """pyLoad Core version """
- return self.core.version
-
- def kill(self):
- """Clean way to quit pyLoad"""
- self.core.do_kill = True
-
- def restart(self):
- """Restart pyload core"""
- self.core.do_restart = True
-
- @permission(PERMS.LOGS)
- def getLog(self, offset=0):
- """Returns most recent log entries.
-
- :param offset: line offset
- :return: List of log entries
- """
- filename = join(self.core.config['log']['log_folder'], 'log.txt')
- try:
- fh = open(filename, "r")
- lines = fh.readlines()
- fh.close()
- if offset >= len(lines):
- return []
- return lines[offset:]
- except:
- return ['No log available']
-
- @permission(PERMS.STATUS)
- def isTimeDownload(self):
- """Checks if pyload will start new downloads according to time in config.
-
- :return: bool
- """
- start = self.core.config['downloadTime']['start'].split(":")
- end = self.core.config['downloadTime']['end'].split(":")
- return compare_time(start, end)
-
- @permission(PERMS.STATUS)
- def isTimeReconnect(self):
- """Checks if pyload will try to make a reconnect
-
- :return: bool
- """
- start = self.core.config['reconnect']['startTime'].split(":")
- end = self.core.config['reconnect']['endTime'].split(":")
- return compare_time(start, end) and self.core.config["reconnect"]["activated"]
-
- @permission(PERMS.LIST)
- def statusDownloads(self):
- """ Status off all currently running downloads.
-
- :return: list of `DownloadStatus`
- """
- data = []
- for pyfile in self.core.threadManager.getActiveFiles():
- if not isinstance(pyfile, PyFile):
- continue
-
- data.append(DownloadInfo(
- pyfile.id, pyfile.name, pyfile.getSpeed(), pyfile.getETA(), pyfile.formatETA(),
- pyfile.getBytesLeft(), pyfile.getSize(), pyfile.formatSize(), pyfile.getPercent(),
- pyfile.status, pyfile.getStatusName(), pyfile.formatWait(),
- pyfile.waitUntil, pyfile.packageid, pyfile.package().name, pyfile.pluginname))
-
- return data
-
- @permission(PERMS.ADD)
- def addPackage(self, name, links, dest=Destination.Queue):
- """Adds a package, with links to desired destination.
-
- :param name: name of the new package
- :param links: list of urls
- :param dest: `Destination`
- :return: package id of the new package
- """
- if self.core.config['general']['folder_per_package']:
- folder = name
- else:
- folder = ""
-
- folder = folder.replace("http://", "").replace(":", "").replace("/", "_").replace("\\", "_")
-
- pid = self.core.files.addPackage(name, folder, dest)
-
- self.core.files.addLinks(links, pid)
-
- self.core.log.info(_("Added package %(name)s containing %(count)d links") % {"name": name, "count": len(links)})
-
- self.core.files.save()
-
- return pid
-
- @permission(PERMS.ADD)
- def parseURLs(self, html=None, url=None):
- """Parses html content or any arbitaty text for links and returns result of `checkURLs`
-
- :param html: html source
- :return:
- """
- urls = []
-
- if html:
- urls += [x[0] for x in urlmatcher.findall(html)]
-
- if url:
- page = getURL(url)
- urls += [x[0] for x in urlmatcher.findall(page)]
-
- # remove duplicates
- return self.checkURLs(set(urls))
-
-
- @permission(PERMS.ADD)
- def checkURLs(self, urls):
- """ Gets urls and returns pluginname mapped to list of matches urls.
-
- :param urls:
- :return: {plugin: urls}
- """
- data = self.core.pluginManager.parseUrls(urls)
- plugins = {}
-
- for url, plugin in data:
- if plugin in plugins:
- plugins[plugin].append(url)
- else:
- plugins[plugin] = [url]
-
- return plugins
-
- @permission(PERMS.ADD)
- def checkOnlineStatus(self, urls):
- """ initiates online status check
-
- :param urls:
- :return: initial set of data as `OnlineCheck` instance containing the result id
- """
- data = self.core.pluginManager.parseUrls(urls)
-
- rid = self.core.threadManager.createResultThread(data, False)
-
- tmp = [(url, (url, OnlineStatus(url, pluginname, "unknown", 3, 0))) for url, pluginname in data]
- data = parseNames(tmp)
- result = {}
-
- for k, v in data.iteritems():
- for url, status in v:
- status.packagename = k
- result[url] = status
-
- return OnlineCheck(rid, result)
-
- @permission(PERMS.ADD)
- def checkOnlineStatusContainer(self, urls, container, data):
- """ checks online status of urls and a submited container file
-
- :param urls: list of urls
- :param container: container file name
- :param data: file content
- :return: online check
- """
- th = open(join(self.core.config["general"]["download_folder"], "tmp_" + container), "wb")
- th.write(str(data))
- th.close()
-
- return self.checkOnlineStatus(urls + [th.name])
-
- @permission(PERMS.ADD)
- def pollResults(self, rid):
- """ Polls the result available for ResultID
-
- :param rid: `ResultID`
- :return: `OnlineCheck`, if rid is -1 then no more data available
- """
- result = self.core.threadManager.getInfoResult(rid)
-
- if "ALL_INFO_FETCHED" in result:
- del result["ALL_INFO_FETCHED"]
- return OnlineCheck(-1, result)
- else:
- return OnlineCheck(rid, result)
-
-
- @permission(PERMS.ADD)
- def generatePackages(self, links):
- """ Parses links, generates packages names from urls
-
- :param links: list of urls
- :return: package names mapped to urls
- """
- result = parseNames((x, x) for x in links)
- return result
-
- @permission(PERMS.ADD)
- def generateAndAddPackages(self, links, dest=Destination.Queue):
- """Generates and add packages
-
- :param links: list of urls
- :param dest: `Destination`
- :return: list of package ids
- """
- return [self.addPackage(name, urls, dest) for name, urls
- in self.generatePackages(links).iteritems()]
-
- @permission(PERMS.ADD)
- def checkAndAddPackages(self, links, dest=Destination.Queue):
- """Checks online status, retrieves names, and will add packages.\
- Because of this packages are not added immediatly, only for internal use.
-
- :param links: list of urls
- :param dest: `Destination`
- :return: None
- """
- data = self.core.pluginManager.parseUrls(links)
- self.core.threadManager.createResultThread(data, True)
-
-
- @permission(PERMS.LIST)
- def getPackageData(self, pid):
- """Returns complete information about package, and included files.
-
- :param pid: package id
- :return: `PackageData` with .links attribute
- """
- data = self.core.files.getPackageData(int(pid))
-
- if not data:
- raise PackageDoesNotExists(pid)
-
- pdata = PackageData(data["id"], data["name"], data["folder"], data["site"], data["password"],
- data["queue"], data["order"],
- links=[self._convertPyFile(x) for x in data["links"].itervalues()])
-
- return pdata
-
- @permission(PERMS.LIST)
- def getPackageInfo(self, pid):
- """Returns information about package, without detailed information about containing files
-
- :param pid: package id
- :return: `PackageData` with .fid attribute
- """
- data = self.core.files.getPackageData(int(pid))
-
- if not data:
- raise PackageDoesNotExists(pid)
-
- pdata = PackageData(data["id"], data["name"], data["folder"], data["site"], data["password"],
- data["queue"], data["order"],
- fids=[int(x) for x in data["links"]])
-
- return pdata
-
- @permission(PERMS.LIST)
- def getFileData(self, fid):
- """Get complete information about a specific file.
-
- :param fid: file id
- :return: `FileData`
- """
- info = self.core.files.getFileData(int(fid))
- if not info:
- raise FileDoesNotExists(fid)
-
- fdata = self._convertPyFile(info.values()[0])
- return fdata
-
- @permission(PERMS.DELETE)
- def deleteFiles(self, fids):
- """Deletes several file entries from pyload.
-
- :param fids: list of file ids
- """
- for id in fids:
- self.core.files.deleteLink(int(id))
-
- self.core.files.save()
-
- @permission(PERMS.DELETE)
- def deletePackages(self, pids):
- """Deletes packages and containing links.
-
- :param pids: list of package ids
- """
- for id in pids:
- self.core.files.deletePackage(int(id))
-
- self.core.files.save()
-
- @permission(PERMS.LIST)
- def getQueue(self):
- """Returns info about queue and packages, **not** about files, see `getQueueData` \
- or `getPackageData` instead.
-
- :return: list of `PackageInfo`
- """
- return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"],
- pack["password"], pack["queue"], pack["order"],
- pack["linksdone"], pack["sizedone"], pack["sizetotal"],
- pack["linkstotal"])
- for pack in self.core.files.getInfoData(Destination.Queue).itervalues()]
-
- @permission(PERMS.LIST)
- def getQueueData(self):
- """Return complete data about everything in queue, this is very expensive use it sparely.\
- See `getQueue` for alternative.
-
- :return: list of `PackageData`
- """
- return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"],
- pack["password"], pack["queue"], pack["order"],
- pack["linksdone"], pack["sizedone"], pack["sizetotal"],
- links=[self._convertPyFile(x) for x in pack["links"].itervalues()])
- for pack in self.core.files.getCompleteData(Destination.Queue).itervalues()]
-
- @permission(PERMS.LIST)
- def getCollector(self):
- """same as `getQueue` for collector.
-
- :return: list of `PackageInfo`
- """
- return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"],
- pack["password"], pack["queue"], pack["order"],
- pack["linksdone"], pack["sizedone"], pack["sizetotal"],
- pack["linkstotal"])
- for pack in self.core.files.getInfoData(Destination.Collector).itervalues()]
-
- @permission(PERMS.LIST)
- def getCollectorData(self):
- """same as `getQueueData` for collector.
-
- :return: list of `PackageInfo`
- """
- return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"],
- pack["password"], pack["queue"], pack["order"],
- pack["linksdone"], pack["sizedone"], pack["sizetotal"],
- links=[self._convertPyFile(x) for x in pack["links"].itervalues()])
- for pack in self.core.files.getCompleteData(Destination.Collector).itervalues()]
-
-
- @permission(PERMS.ADD)
- def addFiles(self, pid, links):
- """Adds files to specific package.
-
- :param pid: package id
- :param links: list of urls
- """
- self.core.files.addLinks(links, int(pid))
-
- self.core.log.info(_("Added %(count)d links to package #%(package)d ") % {"count": len(links), "package": pid})
- self.core.files.save()
-
- @permission(PERMS.MODIFY)
- def pushToQueue(self, pid):
- """Moves package from Collector to Queue.
-
- :param pid: package id
- """
- self.core.files.setPackageLocation(pid, Destination.Queue)
-
- @permission(PERMS.MODIFY)
- def pullFromQueue(self, pid):
- """Moves package from Queue to Collector.
-
- :param pid: package id
- """
- self.core.files.setPackageLocation(pid, Destination.Collector)
-
- @permission(PERMS.MODIFY)
- def restartPackage(self, pid):
- """Restarts a package, resets every containing files.
-
- :param pid: package id
- """
- self.core.files.restartPackage(int(pid))
-
- @permission(PERMS.MODIFY)
- def restartFile(self, fid):
- """Resets file status, so it will be downloaded again.
-
- :param fid: file id
- """
- self.core.files.restartFile(int(fid))
-
- @permission(PERMS.MODIFY)
- def recheckPackage(self, pid):
- """Proofes online status of all files in a package, also a default action when package is added.
-
- :param pid:
- :return:
- """
- self.core.files.reCheckPackage(int(pid))
-
- @permission(PERMS.MODIFY)
- def stopAllDownloads(self):
- """Aborts all running downloads."""
-
- pyfiles = self.core.files.cache.values()
- for pyfile in pyfiles:
- pyfile.abortDownload()
-
- @permission(PERMS.MODIFY)
- def stopDownloads(self, fids):
- """Aborts specific downloads.
-
- :param fids: list of file ids
- :return:
- """
- pyfiles = self.core.files.cache.values()
-
- for pyfile in pyfiles:
- if pyfile.id in fids:
- pyfile.abortDownload()
-
- @permission(PERMS.MODIFY)
- def setPackageName(self, pid, name):
- """Renames a package.
-
- :param pid: package id
- :param name: new package name
- """
- pack = self.core.files.getPackage(pid)
- pack.name = name
- pack.sync()
-
- @permission(PERMS.MODIFY)
- def movePackage(self, destination, pid):
- """Set a new package location.
-
- :param destination: `Destination`
- :param pid: package id
- """
- if destination not in (0, 1): return
- self.core.files.setPackageLocation(pid, destination)
-
- @permission(PERMS.MODIFY)
- def moveFiles(self, fids, pid):
- """Move multiple files to another package
-
- :param fids: list of file ids
- :param pid: destination package
- :return:
- """
- #TODO: implement
- pass
-
-
- @permission(PERMS.ADD)
- def uploadContainer(self, filename, data):
- """Uploads and adds a container file to pyLoad.
-
- :param filename: filename, extension is important so it can correctly decrypted
- :param data: file content
- """
- th = open(join(self.core.config["general"]["download_folder"], "tmp_" + filename), "wb")
- th.write(str(data))
- th.close()
-
- self.addPackage(th.name, [th.name], Destination.Queue)
-
- @permission(PERMS.MODIFY)
- def orderPackage(self, pid, position):
- """Gives a package a new position.
-
- :param pid: package id
- :param position:
- """
- self.core.files.reorderPackage(pid, position)
-
- @permission(PERMS.MODIFY)
- def orderFile(self, fid, position):
- """Gives a new position to a file within its package.
-
- :param fid: file id
- :param position:
- """
- self.core.files.reorderFile(fid, position)
-
- @permission(PERMS.MODIFY)
- def setPackageData(self, pid, data):
- """Allows to modify several package attributes.
-
- :param pid: package id
- :param data: dict that maps attribute to desired value
- """
- p = self.core.files.getPackage(pid)
- if not p: raise PackageDoesNotExists(pid)
-
- for key, value in data.iteritems():
- if key == "id": continue
- setattr(p, key, value)
-
- p.sync()
- self.core.files.save()
-
- @permission(PERMS.DELETE)
- def deleteFinished(self):
- """Deletes all finished files and completly finished packages.
-
- :return: list of deleted package ids
- """
- return self.core.files.deleteFinishedLinks()
-
- @permission(PERMS.MODIFY)
- def restartFailed(self):
- """Restarts all failed failes."""
- self.core.files.restartFailed()
-
- @permission(PERMS.LIST)
- def getPackageOrder(self, destination):
- """Returns information about package order.
-
- :param destination: `Destination`
- :return: dict mapping order to package id
- """
-
- packs = self.core.files.getInfoData(destination)
- order = {}
-
- for pid in packs:
- pack = self.core.files.getPackageData(int(pid))
- while pack["order"] in order.keys(): #just in case
- pack["order"] += 1
- order[pack["order"]] = pack["id"]
- return order
-
- @permission(PERMS.LIST)
- def getFileOrder(self, pid):
- """Information about file order within package.
-
- :param pid:
- :return: dict mapping order to file id
- """
- rawData = self.core.files.getPackageData(int(pid))
- order = {}
- for id, pyfile in rawData["links"].iteritems():
- while pyfile["order"] in order.keys(): #just in case
- pyfile["order"] += 1
- order[pyfile["order"]] = pyfile["id"]
- return order
-
-
- @permission(PERMS.STATUS)
- def isCaptchaWaiting(self):
- """Indicates wether a captcha task is available
-
- :return: bool
- """
- self.core.lastClientConnected = time()
- task = self.core.captchaManager.getTask()
- return not task is None
-
- @permission(PERMS.STATUS)
- def getCaptchaTask(self, exclusive=False):
- """Returns a captcha task
-
- :param exclusive: unused
- :return: `CaptchaTask`
- """
- self.core.lastClientConnected = time()
- task = self.core.captchaManager.getTask()
- if task:
- task.setWatingForUser(exclusive=exclusive)
- data, type, result = task.getCaptcha()
- t = CaptchaTask(int(task.id), standard_b64encode(data), type, result)
- return t
- else:
- return CaptchaTask(-1)
-
- @permission(PERMS.STATUS)
- def getCaptchaTaskStatus(self, tid):
- """Get information about captcha task
-
- :param tid: task id
- :return: string
- """
- self.core.lastClientConnected = time()
- t = self.core.captchaManager.getTaskByID(tid)
- return t.getStatus() if t else ""
-
- @permission(PERMS.STATUS)
- def setCaptchaResult(self, tid, result):
- """Set result for a captcha task
-
- :param tid: task id
- :param result: captcha result
- """
- self.core.lastClientConnected = time()
- task = self.core.captchaManager.getTaskByID(tid)
- if task:
- task.setResult(result)
- self.core.captchaManager.removeTask(task)
-
-
- @permission(PERMS.STATUS)
- def getEvents(self, uuid):
- """Lists occured events, may be affected to changes in future.
-
- :param uuid:
- :return: list of `Events`
- """
- events = self.core.pullManager.getEvents(uuid)
- newEvents = []
-
- def convDest(d):
- return Destination.Queue if d == "queue" else Destination.Collector
-
- for e in events:
- event = EventInfo()
- event.eventname = e[0]
- if e[0] in ("update", "remove", "insert"):
- event.id = e[3]
- event.type = ElementType.Package if e[2] == "pack" else ElementType.File
- event.destination = convDest(e[1])
- elif e[0] == "order":
- if e[1]:
- event.id = e[1]
- event.type = ElementType.Package if e[2] == "pack" else ElementType.File
- event.destination = convDest(e[3])
- elif e[0] == "reload":
- event.destination = convDest(e[1])
- newEvents.append(event)
- return newEvents
-
- @permission(PERMS.ACCOUNTS)
- def getAccounts(self, refresh):
- """Get information about all entered accounts.
-
- :param refresh: reload account info
- :return: list of `AccountInfo`
- """
- accs = self.core.accountManager.getAccountInfos(False, refresh)
- accounts = []
- for group in accs.values():
- accounts.extend([AccountInfo(acc["validuntil"], acc["login"], acc["options"], acc["valid"],
- acc["trafficleft"], acc["maxtraffic"], acc["premium"], acc["type"])
- for acc in group])
- return accounts
-
- @permission(PERMS.ALL)
- def getAccountTypes(self):
- """All available account types.
-
- :return: list
- """
- return self.core.accountManager.accounts.keys()
-
- @permission(PERMS.ACCOUNTS)
- def updateAccount(self, plugin, account, password=None, options={}):
- """Changes pw/options for specific account."""
- self.core.accountManager.updateAccount(plugin, account, password, options)
-
- @permission(PERMS.ACCOUNTS)
- def removeAccount(self, plugin, account):
- """Remove account from pyload.
-
- :param plugin: pluginname
- :param account: accountname
- """
- self.core.accountManager.removeAccount(plugin, account)
-
- @permission(PERMS.ALL)
- def login(self, username, password, remoteip=None):
- """Login into pyLoad, this **must** be called when using rpc before any methods can be used.
-
- :param username:
- :param password:
- :param remoteip: Omit this argument, its only used internal
- :return: bool indicating login was successful
- """
- return True if self.checkAuth(username, password, remoteip) else False
-
- def checkAuth(self, username, password, remoteip=None):
- """Check authentication and returns details
-
- :param username:
- :param password:
- :param remoteip:
- :return: dict with info, empty when login is incorrect
- """
- if self.core.config["remote"]["nolocalauth"] and remoteip == "127.0.0.1":
- return "local"
- if self.core.startedInGui and remoteip == "127.0.0.1":
- return "local"
-
- return self.core.db.checkAuth(username, password)
-
- def isAuthorized(self, func, userdata):
- """checks if the user is authorized for specific method
-
- :param func: function name
- :param userdata: dictionary of user data
- :return: boolean
- """
- if userdata == "local" or userdata["role"] == ROLE.ADMIN:
- return True
- elif func in permMap and has_permission(userdata["permission"], permMap[func]):
- return True
- else:
- return False
-
-
- @permission(PERMS.ALL)
- def getUserData(self, username, password):
- """similar to `checkAuth` but returns UserData thrift type """
- user = self.checkAuth(username, password)
- if user:
- return UserData(user["name"], user["email"], user["role"], user["permission"], user["template"])
- else:
- return UserData()
-
-
- def getAllUserData(self):
- """returns all known user and info"""
- res = {}
- for user, data in self.core.db.getAllUserData().iteritems():
- res[user] = UserData(user, data["email"], data["role"], data["permission"], data["template"])
-
- return res
-
- @permission(PERMS.STATUS)
- def getServices(self):
- """ A dict of available services, these can be defined by hook plugins.
-
- :return: dict with this style: {"plugin": {"method": "description"}}
- """
- data = {}
- for plugin, funcs in self.core.hookManager.methods.iteritems():
- data[plugin] = funcs
-
- return data
-
- @permission(PERMS.STATUS)
- def hasService(self, plugin, func):
- """Checks wether a service is available.
-
- :param plugin:
- :param func:
- :return: bool
- """
- cont = self.core.hookManager.methods
- return plugin in cont and func in cont[plugin]
-
- @permission(PERMS.STATUS)
- def call(self, info):
- """Calls a service (a method in hook plugin).
-
- :param info: `ServiceCall`
- :return: result
- :raises: ServiceDoesNotExists, when its not available
- :raises: ServiceException, when a exception was raised
- """
- plugin = info.plugin
- func = info.func
- args = info.arguments
- parse = info.parseArguments
-
- if not self.hasService(plugin, func):
- raise ServiceDoesNotExists(plugin, func)
-
- try:
- ret = self.core.hookManager.callRPC(plugin, func, args, parse)
- return str(ret)
- except Exception, e:
- raise ServiceException(e.message)
-
- @permission(PERMS.STATUS)
- def getAllInfo(self):
- """Returns all information stored by hook plugins. Values are always strings
-
- :return: {"plugin": {"name": value } }
- """
- return self.core.hookManager.getAllInfo()
-
- @permission(PERMS.STATUS)
- def getInfoByPlugin(self, plugin):
- """Returns information stored by a specific plugin.
-
- :param plugin: pluginname
- :return: dict of attr names mapped to value {"name": value}
- """
- return self.core.hookManager.getInfo(plugin)
-
- def changePassword(self, user, oldpw, newpw):
- """ changes password for specific user """
- return self.core.db.changePassword(user, oldpw, newpw)
-
- def setUserPermission(self, user, permission, role):
- self.core.db.setPermission(user, permission)
- self.core.db.setRole(user, role) \ No newline at end of file
diff --git a/module/CaptchaManager.py b/module/CaptchaManager.py
deleted file mode 100644
index 02cd10a11..000000000
--- a/module/CaptchaManager.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# -*- 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: mkaay, RaNaN
-"""
-
-from time import time
-from traceback import print_exc
-from threading import Lock
-
-class CaptchaManager():
- def __init__(self, core):
- self.lock = Lock()
- self.core = core
- self.tasks = [] #task store, for outgoing tasks only
-
- self.ids = 0 #only for internal purpose
-
- def newTask(self, img, format, file, result_type):
- task = CaptchaTask(self.ids, img, format, file, result_type)
- self.ids += 1
- return task
-
- def removeTask(self, task):
- self.lock.acquire()
- if task in self.tasks:
- self.tasks.remove(task)
- self.lock.release()
-
- def getTask(self):
- self.lock.acquire()
- for task in self.tasks:
- if task.status in ("waiting", "shared-user"):
- self.lock.release()
- return task
- self.lock.release()
- return None
-
- def getTaskByID(self, tid):
- self.lock.acquire()
- for task in self.tasks:
- if task.id == str(tid): #task ids are strings
- self.lock.release()
- return task
- self.lock.release()
- return None
-
- def handleCaptcha(self, task):
- cli = self.core.isClientConnected()
-
- if cli: #client connected -> should solve the captcha
- task.setWaiting(50) #wait 50 sec for response
-
- for plugin in self.core.hookManager.activePlugins():
- try:
- plugin.newCaptchaTask(task)
- except:
- if self.core.debug:
- print_exc()
-
- if task.handler or cli: #the captcha was handled
- self.tasks.append(task)
- return True
-
- task.error = _("No Client connected for captcha decrypting")
-
- return False
-
-
-class CaptchaTask():
- def __init__(self, id, img, format, file, result_type='textual'):
- self.id = str(id)
- self.captchaImg = img
- self.captchaFormat = format
- self.captchaFile = file
- self.captchaResultType = result_type
- self.handler = [] #the hook plugins that will take care of the solution
- self.result = None
- self.waitUntil = None
- self.error = None #error message
-
- self.status = "init"
- self.data = {} #handler can store data here
-
- def getCaptcha(self):
- return self.captchaImg, self.captchaFormat, self.captchaResultType
-
- def setResult(self, text):
- if self.isTextual():
- self.result = text
- if self.isPositional():
- try:
- parts = text.split(',')
- self.result = (int(parts[0]), int(parts[1]))
- except:
- self.result = None
-
- def getResult(self):
- try:
- res = self.result.encode("utf8", "replace")
- except:
- res = self.result
-
- return res
-
- def getStatus(self):
- return self.status
-
- def setWaiting(self, sec):
- """ let the captcha wait secs for the solution """
- self.waitUntil = max(time() + sec, self.waitUntil)
- self.status = "waiting"
-
- def isWaiting(self):
- if self.result or self.error or time() > self.waitUntil:
- return False
-
- return True
-
- def isTextual(self):
- """ returns if text is written on the captcha """
- return self.captchaResultType == 'textual'
-
- def isPositional(self):
- """ returns if user have to click a specific region on the captcha """
- return self.captchaResultType == 'positional'
-
- def setWatingForUser(self, exclusive):
- if exclusive:
- self.status = "user"
- else:
- self.status = "shared-user"
-
- def timedOut(self):
- return time() > self.waitUntil
-
- def invalid(self):
- """ indicates the captcha was not correct """
- [x.captchaInvalid(self) for x in self.handler]
-
- def correct(self):
- [x.captchaCorrect(self) for x in self.handler]
-
- def __str__(self):
- return "<CaptchaTask '%s'>" % self.id
diff --git a/module/ConfigParser.py b/module/ConfigParser.py
deleted file mode 100644
index 78b612f13..000000000
--- a/module/ConfigParser.py
+++ /dev/null
@@ -1,392 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-from time import sleep
-from os.path import exists, join
-from shutil import copy
-
-from traceback import print_exc
-from utils import chmod
-
-# ignore these plugin configs, mainly because plugins were wiped out
-IGNORE = (
- "FreakshareNet", "SpeedManager", "ArchiveTo", "ShareCx", ('hooks', 'UnRar'),
- 'EasyShareCom', 'FlyshareCz'
- )
-
-CONF_VERSION = 1
-
-class ConfigParser:
- """
- holds and manage the configuration
-
- current dict layout:
-
- {
-
- section : {
- option : {
- value:
- type:
- desc:
- }
- desc:
-
- }
-
-
- """
-
-
- def __init__(self):
- """Constructor"""
- self.config = {} # the config values
- self.plugin = {} # the config for plugins
- self.oldRemoteData = {}
-
- self.pluginCB = None # callback when plugin config value is changed
-
- self.checkVersion()
-
- self.readConfig()
-
- self.deleteOldPlugins()
-
-
- def checkVersion(self, n=0):
- """determines if config need to be copied"""
- try:
- if not exists("pyload.conf"):
- copy(join(pypath, "module", "config", "default.conf"), "pyload.conf")
-
- if not exists("plugin.conf"):
- f = open("plugin.conf", "wb")
- f.write("version: " + str(CONF_VERSION))
- f.close()
-
- f = open("pyload.conf", "rb")
- v = f.readline()
- f.close()
- v = v[v.find(":") + 1:].strip()
-
- if not v or int(v) < CONF_VERSION:
- copy(join(pypath, "module", "config", "default.conf"), "pyload.conf")
- print "Old version of config was replaced"
-
- f = open("plugin.conf", "rb")
- v = f.readline()
- f.close()
- v = v[v.find(":") + 1:].strip()
-
- if not v or int(v) < CONF_VERSION:
- f = open("plugin.conf", "wb")
- f.write("version: " + str(CONF_VERSION))
- f.close()
- print "Old version of plugin-config replaced"
- except:
- if n < 3:
- sleep(0.3)
- self.checkVersion(n + 1)
- else:
- raise
-
- def readConfig(self):
- """reads the config file"""
-
- self.config = self.parseConfig(join(pypath, "module", "config", "default.conf"))
- self.plugin = self.parseConfig("plugin.conf")
-
- try:
- homeconf = self.parseConfig("pyload.conf")
- if "username" in homeconf["remote"]:
- if "password" in homeconf["remote"]:
- self.oldRemoteData = {"username": homeconf["remote"]["username"]["value"],
- "password": homeconf["remote"]["username"]["value"]}
- del homeconf["remote"]["password"]
- del homeconf["remote"]["username"]
- self.updateValues(homeconf, self.config)
-
- except Exception, e:
- print "Config Warning"
- print_exc()
-
-
- def parseConfig(self, config):
- """parses a given configfile"""
-
- f = open(config)
-
- config = f.read()
-
- config = config.splitlines()[1:]
-
- conf = {}
-
- section, option, value, typ, desc = "", "", "", "", ""
-
- listmode = False
-
- for line in config:
- comment = line.rfind("#")
- if line.find(":", comment) < 0 > line.find("=", comment) and comment > 0 and line[comment - 1].isspace():
- line = line.rpartition("#") # removes comments
- if line[1]:
- line = line[0]
- else:
- line = line[2]
-
- line = line.strip()
-
- try:
- if line == "":
- continue
- elif line.endswith(":"):
- section, none, desc = line[:-1].partition('-')
- section = section.strip()
- desc = desc.replace('"', "").strip()
- conf[section] = {"desc": desc}
- else:
- if listmode:
- if line.endswith("]"):
- listmode = False
- line = line.replace("]", "")
-
- value += [self.cast(typ, x.strip()) for x in line.split(",") if x]
-
- if not listmode:
- conf[section][option] = {"desc": desc,
- "type": typ,
- "value": value}
-
-
- else:
- content, none, value = line.partition("=")
-
- content, none, desc = content.partition(":")
-
- desc = desc.replace('"', "").strip()
-
- typ, none, option = content.strip().rpartition(" ")
-
- value = value.strip()
-
- if value.startswith("["):
- if value.endswith("]"):
- listmode = False
- value = value[:-1]
- else:
- listmode = True
-
- value = [self.cast(typ, x.strip()) for x in value[1:].split(",") if x]
- else:
- value = self.cast(typ, value)
-
- if not listmode:
- conf[section][option] = {"desc": desc,
- "type": typ,
- "value": value}
-
- except Exception, e:
- print "Config Warning"
- print_exc()
-
- f.close()
- return conf
-
-
- def updateValues(self, config, dest):
- """sets the config values from a parsed config file to values in destination"""
-
- for section in config.iterkeys():
- if section in dest:
- for option in config[section].iterkeys():
- if option in ("desc", "outline"): continue
-
- if option in dest[section]:
- dest[section][option]["value"] = config[section][option]["value"]
-
- #else:
- # dest[section][option] = config[section][option]
-
-
- #else:
- # dest[section] = config[section]
-
- def saveConfig(self, config, filename):
- """saves config to filename"""
- with open(filename, "wb") as f:
- chmod(filename, 0600)
- f.write("version: %i \n" % CONF_VERSION)
- for section in config.iterkeys():
- f.write('\n%s - "%s":\n' % (section, config[section]["desc"]))
-
- for option, data in config[section].iteritems():
- if option in ("desc", "outline"): continue
-
- if isinstance(data["value"], list):
- value = "[ \n"
- for x in data["value"]:
- value += "\t\t" + str(x) + ",\n"
- value += "\t\t]\n"
- else:
- if type(data["value"]) in (str, unicode):
- value = data["value"] + "\n"
- else:
- value = str(data["value"]) + "\n"
- try:
- f.write('\t%s %s : "%s" = %s' % (data["type"], option, data["desc"], value))
- except UnicodeEncodeError:
- f.write('\t%s %s : "%s" = %s' % (data["type"], option, data["desc"], value.encode("utf8")))
-
- def cast(self, typ, value):
- """cast value to given format"""
- if type(value) not in (str, unicode):
- return value
-
- elif typ == "int":
- return int(value)
- elif typ == "bool":
- return True if value.lower() in ("1", "true", "on", "an", "yes") else False
- elif typ == "time":
- if not value: value = "0:00"
- if not ":" in value: value += ":00"
- return value
- elif typ in ("str", "file", "folder"):
- try:
- return value.encode("utf8")
- except:
- return value
- else:
- return value
-
-
- def save(self):
- """saves the configs to disk"""
-
- self.saveConfig(self.config, "pyload.conf")
- self.saveConfig(self.plugin, "plugin.conf")
-
-
- def __getitem__(self, section):
- """provides dictonary like access: c['section']['option']"""
- return Section(self, section)
-
-
- def get(self, section, option):
- """get value"""
- val = self.config[section][option]["value"]
- try:
- if type(val) in (str, unicode):
- return val.decode("utf8")
- else:
- return val
- except:
- return val
-
- def set(self, section, option, value):
- """set value"""
-
- value = self.cast(self.config[section][option]["type"], value)
-
- self.config[section][option]["value"] = value
- self.save()
-
- def getPlugin(self, plugin, option):
- """gets a value for a plugin"""
- val = self.plugin[plugin][option]["value"]
- try:
- if type(val) in (str, unicode):
- return val.decode("utf8")
- else:
- return val
- except:
- return val
-
- def setPlugin(self, plugin, option, value):
- """sets a value for a plugin"""
-
- value = self.cast(self.plugin[plugin][option]["type"], value)
-
- if self.pluginCB: self.pluginCB(plugin, option, value)
-
- self.plugin[plugin][option]["value"] = value
- self.save()
-
- def getMetaData(self, section, option):
- """ get all config data for an option """
- return self.config[section][option]
-
- def addPluginConfig(self, name, config, outline=""):
- """adds config options with tuples (name, type, desc, default)"""
- if name not in self.plugin:
- conf = {"desc": name,
- "outline": outline}
- self.plugin[name] = conf
- else:
- conf = self.plugin[name]
- conf["outline"] = outline
-
- for item in config:
- if item[0] in conf:
- conf[item[0]]["type"] = item[1]
- conf[item[0]]["desc"] = item[2]
- else:
- conf[item[0]] = {
- "desc": item[2],
- "type": item[1],
- "value": self.cast(item[1], item[3])
- }
-
- values = [x[0] for x in config] + ["desc", "outline"]
- #delete old values
- for item in conf.keys():
- if item not in values:
- del conf[item]
-
- def deleteConfig(self, name):
- """Removes a plugin config"""
- if name in self.plugin:
- del self.plugin[name]
-
-
- def deleteOldPlugins(self):
- """ remove old plugins from config """
-
- for name in IGNORE:
- if name in self.plugin:
- del self.plugin[name]
-
-
-class Section:
- """provides dictionary like access for configparser"""
-
- def __init__(self, parser, section):
- """Constructor"""
- self.parser = parser
- self.section = section
-
- def __getitem__(self, item):
- """getitem"""
- return self.parser.get(self.section, item)
-
- def __setitem__(self, item, value):
- """setitem"""
- self.parser.set(self.section, item, value)
-
-
-if __name__ == "__main__":
- pypath = ""
-
- from time import time
-
- a = time()
-
- c = ConfigParser()
-
- b = time()
-
- print "sec", b - a
-
- print c.config
-
- c.saveConfig(c.config, "user.conf")
diff --git a/module/HookManager.py b/module/HookManager.py
deleted file mode 100644
index 16f692d76..000000000
--- a/module/HookManager.py
+++ /dev/null
@@ -1,315 +0,0 @@
-# -*- 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, mkaay
- @interface-version: 0.1
-"""
-import __builtin__
-
-import traceback
-from thread import start_new_thread
-from threading import RLock
-
-from types import MethodType
-
-from module.PluginThread import HookThread
-from module.plugins.PluginManager import literal_eval
-from utils import lock
-
-class HookManager:
- """Manages hooks, delegates and handles Events.
-
- Every plugin can define events, \
- but some very usefull events are called by the Core.
- Contrary to overwriting hook methods you can use event listener,
- which provides additional entry point in the control flow.
- Only do very short tasks or use threads.
-
- **Known Events:**
- Most hook methods exists as events. These are the additional known events.
-
- ===================== ============== ==================================
- Name Arguments Description
- ===================== ============== ==================================
- downloadPreparing fid A download was just queued and will be prepared now.
- downloadStarts fid A plugin will immediately starts the download afterwards.
- linksAdded links, pid Someone just added links, you are able to modify the links.
- allDownloadsProcessed Every link was handled, pyload would idle afterwards.
- allDownloadsFinished Every download in queue is finished.
- unrarFinished folder, fname An Unrar job finished
- configChanged The config was changed via the api.
- pluginConfigChanged The plugin config changed, due to api or internal process.
- ===================== ============== ==================================
-
- | Notes:
- | allDownloadsProcessed is *always* called before allDownloadsFinished.
- | configChanged is *always* called before pluginConfigChanged.
-
-
- """
-
- def __init__(self, core):
- self.core = core
- self.config = self.core.config
-
- __builtin__.hookManager = self #needed to let hooks register themself
-
- self.log = self.core.log
- self.plugins = []
- self.pluginMap = {}
- self.methods = {} #dict of names and list of methods usable by rpc
-
- self.events = {} # contains events
-
- #registering callback for config event
- self.config.pluginCB = MethodType(self.dispatchEvent, "pluginConfigChanged", basestring)
-
- self.addEvent("pluginConfigChanged", self.manageHooks)
-
- self.lock = RLock()
- self.createIndex()
-
- def try_catch(func):
- def new(*args):
- try:
- return func(*args)
- except Exception, e:
- args[0].log.error(_("Error executing hooks: %s") % str(e))
- if args[0].core.debug:
- traceback.print_exc()
-
- return new
-
-
- def addRPC(self, plugin, func, doc):
- plugin = plugin.rpartition(".")[2]
- doc = doc.strip() if doc else ""
-
- if plugin in self.methods:
- self.methods[plugin][func] = doc
- else:
- self.methods[plugin] = {func: doc}
-
- def callRPC(self, plugin, func, args, parse):
- if not args: args = tuple()
- if parse:
- args = tuple([literal_eval(x) for x in args])
-
- plugin = self.pluginMap[plugin]
- f = getattr(plugin, func)
- return f(*args)
-
-
- def createIndex(self):
- plugins = []
-
- active = []
- deactive = []
-
- for pluginname in self.core.pluginManager.hookPlugins:
- try:
- #hookClass = getattr(plugin, plugin.__name__)
-
- if self.core.config.getPlugin(pluginname, "activated"):
- pluginClass = self.core.pluginManager.loadClass("hooks", pluginname)
- if not pluginClass: continue
-
- plugin = pluginClass(self.core, self)
- plugins.append(plugin)
- self.pluginMap[pluginClass.__name__] = plugin
- if plugin.isActivated():
- active.append(pluginClass.__name__)
- else:
- deactive.append(pluginname)
-
-
- except:
- self.log.warning(_("Failed activating %(name)s") % {"name": pluginname})
- if self.core.debug:
- traceback.print_exc()
-
- self.log.info(_("Activated plugins: %s") % ", ".join(sorted(active)))
- self.log.info(_("Deactivate plugins: %s") % ", ".join(sorted(deactive)))
-
- self.plugins = plugins
-
- def manageHooks(self, plugin, name, value):
- if name == "activated" and value:
- self.activateHook(plugin)
- elif name == "activated" and not value:
- self.deactivateHook(plugin)
-
- def activateHook(self, plugin):
-
- #check if already loaded
- for inst in self.plugins:
- if inst.__name__ == plugin:
- return
-
- pluginClass = self.core.pluginManager.loadClass("hooks", plugin)
-
- if not pluginClass: return
-
- self.log.debug("Plugin loaded: %s" % plugin)
-
- plugin = pluginClass(self.core, self)
- self.plugins.append(plugin)
- self.pluginMap[pluginClass.__name__] = plugin
-
- # call core Ready
- start_new_thread(plugin.coreReady, tuple())
-
- def deactivateHook(self, plugin):
-
- hook = None
- for inst in self.plugins:
- if inst.__name__ == plugin:
- hook = inst
-
- if not hook: return
-
- self.log.debug("Plugin unloaded: %s" % plugin)
-
- hook.unload()
-
- #remove periodic call
- self.log.debug("Removed callback %s" % self.core.scheduler.removeJob(hook.cb))
- self.plugins.remove(hook)
- del self.pluginMap[hook.__name__]
-
-
- @try_catch
- def coreReady(self):
- for plugin in self.plugins:
- if plugin.isActivated():
- plugin.coreReady()
-
- self.dispatchEvent("coreReady")
-
- @try_catch
- def coreExiting(self):
- for plugin in self.plugins:
- if plugin.isActivated():
- plugin.coreExiting()
-
- self.dispatchEvent("coreExiting")
-
- @lock
- def downloadPreparing(self, pyfile):
- for plugin in self.plugins:
- if plugin.isActivated():
- plugin.downloadPreparing(pyfile)
-
- self.dispatchEvent("downloadPreparing", pyfile)
-
- @lock
- def downloadFinished(self, pyfile):
- for plugin in self.plugins:
- if plugin.isActivated():
- if "downloadFinished" in plugin.__threaded__:
- self.startThread(plugin.downloadFinished, pyfile)
- else:
- plugin.downloadFinished(pyfile)
-
- self.dispatchEvent("downloadFinished", pyfile)
-
- @lock
- @try_catch
- def downloadFailed(self, pyfile):
- for plugin in self.plugins:
- if plugin.isActivated():
- if "downloadFailed" in plugin.__threaded__:
- self.startThread(plugin.downloadFinished, pyfile)
- else:
- plugin.downloadFailed(pyfile)
-
- self.dispatchEvent("downloadFailed", pyfile)
-
- @lock
- def packageFinished(self, package):
- for plugin in self.plugins:
- if plugin.isActivated():
- if "packageFinished" in plugin.__threaded__:
- self.startThread(plugin.packageFinished, package)
- else:
- plugin.packageFinished(package)
-
- self.dispatchEvent("packageFinished", package)
-
- @lock
- def beforeReconnecting(self, ip):
- for plugin in self.plugins:
- plugin.beforeReconnecting(ip)
-
- self.dispatchEvent("beforeReconnecting", ip)
-
- @lock
- def afterReconnecting(self, ip):
- for plugin in self.plugins:
- if plugin.isActivated():
- plugin.afterReconnecting(ip)
-
- self.dispatchEvent("afterReconnecting", ip)
-
- def startThread(self, function, *args, **kwargs):
- t = HookThread(self.core.threadManager, function, args, kwargs)
-
- def activePlugins(self):
- """ returns all active plugins """
- return [x for x in self.plugins if x.isActivated()]
-
- def getAllInfo(self):
- """returns info stored by hook plugins"""
- info = {}
- for name, plugin in self.pluginMap.iteritems():
- if plugin.info:
- #copy and convert so str
- info[name] = dict([(x, str(y) if not isinstance(y, basestring) else y) for x, y in plugin.info.iteritems()])
- return info
-
-
- def getInfo(self, plugin):
- info = {}
- if plugin in self.pluginMap and self.pluginMap[plugin].info:
- info = dict([(x, str(y) if not isinstance(y, basestring) else y)
- for x, y in self.pluginMap[plugin].info.iteritems()])
-
- return info
-
- def addEvent(self, event, func):
- """Adds an event listener for event name"""
- if event in self.events:
- self.events[event].append(func)
- else:
- self.events[event] = [func]
-
- def removeEvent(self, event, func):
- """removes previously added event listener"""
- if event in self.events:
- self.events[event].remove(func)
-
- def dispatchEvent(self, event, *args):
- """dispatches event with args"""
- if event in self.events:
- for f in self.events[event]:
- try:
- f(*args)
- except Exception, e:
- self.log.warning("Error calling event handler %s: %s, %s, %s"
- % (event, f, args, str(e)))
- if self.core.debug:
- traceback.print_exc()
-
diff --git a/module/InitHomeDir.py b/module/InitHomeDir.py
deleted file mode 100644
index 156c9f932..000000000
--- a/module/InitHomeDir.py
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/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
-
- This modules inits working directories and global variables, pydir and homedir
-"""
-
-from os import makedirs, path, chdir
-from os.path import join
-import sys
-from sys import argv, platform
-
-import __builtin__
-__builtin__.owd = path.abspath("") #original working directory
-__builtin__.pypath = path.abspath(path.join(__file__, "..", ".."))
-
-sys.path.append(join(pypath, "module", "lib"))
-
-homedir = ""
-
-if platform == 'nt':
- homedir = path.expanduser("~")
- if homedir == "~":
- import ctypes
-
- CSIDL_APPDATA = 26
- _SHGetFolderPath = ctypes.windll.shell32.SHGetFolderPathW
- _SHGetFolderPath.argtypes = [ctypes.wintypes.HWND,
- ctypes.c_int,
- ctypes.wintypes.HANDLE,
- ctypes.wintypes.DWORD, ctypes.wintypes.LPCWSTR]
-
- path_buf = ctypes.wintypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH)
- result = _SHGetFolderPath(0, CSIDL_APPDATA, 0, 0, path_buf)
- homedir = path_buf.value
-else:
- homedir = path.expanduser("~")
-
-__builtin__.homedir = homedir
-
-args = " ".join(argv[1:])
-
-# dirty method to set configdir from commandline arguments
-if "--configdir=" in args:
- pos = args.find("--configdir=")
- end = args.find("-", pos + 12)
-
- if end == -1:
- configdir = args[pos + 12:].strip()
- else:
- configdir = args[pos + 12:end].strip()
-elif path.exists(path.join(pypath, "module", "config", "configdir")):
- f = open(path.join(pypath, "module", "config", "configdir"), "rb")
- c = f.read().strip()
- f.close()
- configdir = path.join(pypath, c)
-else:
- if platform in ("posix", "linux2"):
- configdir = path.join(homedir, ".pyload")
- else:
- configdir = path.join(homedir, "pyload")
-
-if not path.exists(configdir):
- makedirs(configdir, 0700)
-
-__builtin__.configdir = configdir
-chdir(configdir)
-
-#print "Using %s as working directory." % configdir
diff --git a/module/PluginThread.py b/module/PluginThread.py
deleted file mode 100644
index 56c36c778..000000000
--- a/module/PluginThread.py
+++ /dev/null
@@ -1,677 +0,0 @@
-#!/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
-"""
-
-from Queue import Queue
-from threading import Thread
-from os import listdir, stat
-from os.path import join
-from time import sleep, time, strftime, gmtime
-from traceback import print_exc, format_exc
-from pprint import pformat
-from sys import exc_info, exc_clear
-from copy import copy
-from types import MethodType
-
-from pycurl import error
-
-from PyFile import PyFile
-from plugins.Plugin import Abort, Fail, Reconnect, Retry, SkipDownload
-from common.packagetools import parseNames
-from utils import save_join
-from Api import OnlineStatus
-
-class PluginThread(Thread):
- """abstract base class for thread types"""
-
- #----------------------------------------------------------------------
- def __init__(self, manager):
- """Constructor"""
- Thread.__init__(self)
- self.setDaemon(True)
- self.m = manager #thread manager
-
-
- def writeDebugReport(self, pyfile):
- """ writes a
- :return:
- """
-
- dump_name = "debug_%s_%s.zip" % (pyfile.pluginname, strftime("%d-%m-%Y_%H-%M-%S"))
- dump = self.getDebugDump(pyfile)
-
- try:
- import zipfile
-
- zip = zipfile.ZipFile(dump_name, "w")
-
- for f in listdir(join("tmp", pyfile.pluginname)):
- try:
- # avoid encoding errors
- zip.write(join("tmp", pyfile.pluginname, f), save_join(pyfile.pluginname, f))
- except:
- pass
-
- info = zipfile.ZipInfo(save_join(pyfile.pluginname, "debug_Report.txt"), gmtime())
- info.external_attr = 0644 << 16L # change permissions
-
- zip.writestr(info, dump)
- zip.close()
-
- if not stat(dump_name).st_size:
- raise Exception("Empty Zipfile")
-
- except Exception, e:
- self.m.log.debug("Error creating zip file: %s" % e)
-
- dump_name = dump_name.replace(".zip", ".txt")
- f = open(dump_name, "wb")
- f.write(dump)
- f.close()
-
- self.m.core.log.info("Debug Report written to %s" % dump_name)
-
- def getDebugDump(self, pyfile):
- dump = "pyLoad %s Debug Report of %s %s \n\nTRACEBACK:\n %s \n\nFRAMESTACK:\n" % (
- self.m.core.api.getServerVersion(), pyfile.pluginname, pyfile.plugin.__version__, format_exc())
-
- tb = exc_info()[2]
- stack = []
- while tb:
- stack.append(tb.tb_frame)
- tb = tb.tb_next
-
- for frame in stack[1:]:
- dump += "\nFrame %s in %s at line %s\n" % (frame.f_code.co_name,
- frame.f_code.co_filename,
- frame.f_lineno)
-
- for key, value in frame.f_locals.items():
- dump += "\t%20s = " % key
- try:
- dump += pformat(value) + "\n"
- except Exception, e:
- dump += "<ERROR WHILE PRINTING VALUE> " + str(e) + "\n"
-
- del frame
-
- del stack #delete it just to be sure...
-
- dump += "\n\nPLUGIN OBJECT DUMP: \n\n"
-
- for name in dir(pyfile.plugin):
- attr = getattr(pyfile.plugin, name)
- if not name.endswith("__") and type(attr) != MethodType:
- dump += "\t%20s = " % name
- try:
- dump += pformat(attr) + "\n"
- except Exception, e:
- dump += "<ERROR WHILE PRINTING VALUE> " + str(e) + "\n"
-
- dump += "\nPYFILE OBJECT DUMP: \n\n"
-
- for name in dir(pyfile):
- attr = getattr(pyfile, name)
- if not name.endswith("__") and type(attr) != MethodType:
- dump += "\t%20s = " % name
- try:
- dump += pformat(attr) + "\n"
- except Exception, e:
- dump += "<ERROR WHILE PRINTING VALUE> " + str(e) + "\n"
-
- if pyfile.pluginname in self.m.core.config.plugin:
- dump += "\n\nCONFIG: \n\n"
- dump += pformat(self.m.core.config.plugin[pyfile.pluginname]) + "\n"
-
- return dump
-
- def clean(self, pyfile):
- """ set thread unactive and release pyfile """
- self.active = False
- pyfile.release()
-
-
-class DownloadThread(PluginThread):
- """thread for downloading files from 'real' hoster plugins"""
-
- #----------------------------------------------------------------------
- def __init__(self, manager):
- """Constructor"""
- PluginThread.__init__(self, manager)
-
- self.queue = Queue() # job queue
- self.active = False
-
- self.start()
-
- #----------------------------------------------------------------------
- def run(self):
- """run method"""
- pyfile = None
-
- while True:
- del pyfile
- self.active = self.queue.get()
- pyfile = self.active
-
- if self.active == "quit":
- self.active = False
- self.m.threads.remove(self)
- return True
-
- try:
- if not pyfile.hasPlugin(): continue
- #this pyfile was deleted while queueing
-
- pyfile.plugin.checkForSameFiles(starting=True)
- self.m.log.info(_("Download starts: %s" % pyfile.name))
-
- # start download
- self.m.core.hookManager.downloadPreparing(pyfile)
- pyfile.plugin.preprocessing(self)
-
- self.m.log.info(_("Download finished: %s") % pyfile.name)
- self.m.core.hookManager.downloadFinished(pyfile)
- self.m.core.files.checkPackageFinished(pyfile)
-
- except NotImplementedError:
- self.m.log.error(_("Plugin %s is missing a function.") % pyfile.pluginname)
- pyfile.setStatus("failed")
- pyfile.error = "Plugin does not work"
- self.clean(pyfile)
- continue
-
- except Abort:
- try:
- self.m.log.info(_("Download aborted: %s") % pyfile.name)
- except:
- pass
-
- pyfile.setStatus("aborted")
-
- self.clean(pyfile)
- continue
-
- except Reconnect:
- self.queue.put(pyfile)
- #pyfile.req.clearCookies()
-
- while self.m.reconnecting.isSet():
- sleep(0.5)
-
- continue
-
- except Retry, e:
- reason = e.args[0]
- self.m.log.info(_("Download restarted: %(name)s | %(msg)s") % {"name": pyfile.name, "msg": reason})
- self.queue.put(pyfile)
- continue
-
- except Fail, e:
- msg = e.args[0]
-
- if msg == "offline":
- pyfile.setStatus("offline")
- self.m.log.warning(_("Download is offline: %s") % pyfile.name)
- elif msg == "temp. offline":
- pyfile.setStatus("temp. offline")
- self.m.log.warning(_("Download is temporary offline: %s") % pyfile.name)
- else:
- pyfile.setStatus("failed")
- self.m.log.warning(_("Download failed: %(name)s | %(msg)s") % {"name": pyfile.name, "msg": msg})
- pyfile.error = msg
-
- self.m.core.hookManager.downloadFailed(pyfile)
- self.clean(pyfile)
- continue
-
- except error, e:
- if len(e.args) == 2:
- code, msg = e.args
- else:
- code = 0
- msg = e.args
-
- self.m.log.debug("pycurl exception %s: %s" % (code, msg))
-
- if code in (7, 18, 28, 52, 56):
- self.m.log.warning(_("Couldn't connect to host or connection reset, waiting 1 minute and retry."))
- wait = time() + 60
-
- pyfile.waitUntil = wait
- pyfile.setStatus("waiting")
- while time() < wait:
- sleep(1)
- if pyfile.abort:
- break
-
- if pyfile.abort:
- self.m.log.info(_("Download aborted: %s") % pyfile.name)
- pyfile.setStatus("aborted")
-
- self.clean(pyfile)
- else:
- self.queue.put(pyfile)
-
- continue
-
- else:
- pyfile.setStatus("failed")
- self.m.log.error("pycurl error %s: %s" % (code, msg))
- if self.m.core.debug:
- print_exc()
- self.writeDebugReport(pyfile)
-
- self.m.core.hookManager.downloadFailed(pyfile)
-
- self.clean(pyfile)
- continue
-
- except SkipDownload, e:
- pyfile.setStatus("skipped")
-
- self.m.log.info(
- _("Download skipped: %(name)s due to %(plugin)s") % {"name": pyfile.name, "plugin": e.message})
-
- self.clean(pyfile)
-
- self.m.core.files.checkPackageFinished(pyfile)
-
- self.active = False
- self.m.core.files.save()
-
- continue
-
-
- except Exception, e:
- pyfile.setStatus("failed")
- self.m.log.warning(_("Download failed: %(name)s | %(msg)s") % {"name": pyfile.name, "msg": str(e)})
- pyfile.error = str(e)
-
- if self.m.core.debug:
- print_exc()
- self.writeDebugReport(pyfile)
-
- self.m.core.hookManager.downloadFailed(pyfile)
- self.clean(pyfile)
- continue
-
- finally:
- self.m.core.files.save()
- pyfile.checkIfProcessed()
- exc_clear()
-
-
- #pyfile.plugin.req.clean()
-
- self.active = False
- pyfile.finishIfDone()
- self.m.core.files.save()
-
-
- def put(self, job):
- """assing job to thread"""
- self.queue.put(job)
-
-
- def stop(self):
- """stops the thread"""
- self.put("quit")
-
-
-class DecrypterThread(PluginThread):
- """thread for decrypting"""
-
- def __init__(self, manager, pyfile):
- """constructor"""
- PluginThread.__init__(self, manager)
-
- self.active = pyfile
- manager.localThreads.append(self)
-
- pyfile.setStatus("decrypting")
-
- self.start()
-
- def getActiveFiles(self):
- return [self.active]
-
- def run(self):
- """run method"""
-
- pyfile = self.active
- retry = False
-
- try:
- self.m.log.info(_("Decrypting starts: %s") % self.active.name)
- self.active.plugin.preprocessing(self)
-
- except NotImplementedError:
- self.m.log.error(_("Plugin %s is missing a function.") % self.active.pluginname)
- return
-
- except Fail, e:
- msg = e.args[0]
-
- if msg == "offline":
- self.active.setStatus("offline")
- self.m.log.warning(_("Download is offline: %s") % self.active.name)
- else:
- self.active.setStatus("failed")
- self.m.log.error(_("Decrypting failed: %(name)s | %(msg)s") % {"name": self.active.name, "msg": msg})
- self.active.error = msg
-
- return
-
- except Abort:
- self.m.log.info(_("Download aborted: %s") % pyfile.name)
- pyfile.setStatus("aborted")
-
- return
-
- except Retry:
- self.m.log.info(_("Retrying %s") % self.active.name)
- retry = True
- return self.run()
-
- except Exception, e:
- self.active.setStatus("failed")
- self.m.log.error(_("Decrypting failed: %(name)s | %(msg)s") % {"name": self.active.name, "msg": str(e)})
- self.active.error = str(e)
-
- if self.m.core.debug:
- print_exc()
- self.writeDebugReport(pyfile)
-
- return
-
-
- finally:
- if not retry:
- self.active.release()
- self.active = False
- self.m.core.files.save()
- self.m.localThreads.remove(self)
- exc_clear()
-
-
- #self.m.core.hookManager.downloadFinished(pyfile)
-
-
- #self.m.localThreads.remove(self)
- #self.active.finishIfDone()
- if not retry:
- pyfile.delete()
-
-
-class HookThread(PluginThread):
- """thread for hooks"""
-
- #----------------------------------------------------------------------
- def __init__(self, m, function, args, kwargs):
- """Constructor"""
- PluginThread.__init__(self, m)
-
- self.f = function
- self.args = args
- self.kwargs = kwargs
-
- self.active = []
-
- m.localThreads.append(self)
-
- self.start()
-
- def getActiveFiles(self):
- return self.active
-
- def addActive(self, pyfile):
- """ Adds a pyfile to active list and thus will be displayed on overview"""
- if pyfile not in self.active:
- self.active.append(pyfile)
-
- def finishFile(self, pyfile):
- if pyfile in self.active:
- self.active.remove(pyfile)
-
- pyfile.finishIfDone()
-
- def run(self):
- try:
- try:
- self.kwargs["thread"] = self
- self.f(*self.args, **self.kwargs)
- except TypeError, e:
- #dirty method to filter out exceptions
- if "unexpected keyword argument 'thread'" not in e.args[0]:
- raise
-
- del self.kwargs["thread"]
- self.f(*self.args, **self.kwargs)
- finally:
- local = copy(self.active)
- for x in local:
- self.finishFile(x)
-
- self.m.localThreads.remove(self)
-
-
-class InfoThread(PluginThread):
- def __init__(self, manager, data, pid=-1, rid=-1, add=False):
- """Constructor"""
- PluginThread.__init__(self, manager)
-
- self.data = data
- self.pid = pid # package id
- # [ .. (name, plugin) .. ]
-
- self.rid = rid #result id
- self.add = add #add packages instead of return result
-
- self.cache = [] #accumulated data
-
- self.start()
-
- def run(self):
- """run method"""
-
- plugins = {}
- container = []
-
- for url, plugin in self.data:
- if plugin in plugins:
- plugins[plugin].append(url)
- else:
- plugins[plugin] = [url]
-
-
- # filter out container plugins
- for name in self.m.core.pluginManager.containerPlugins:
- if name in plugins:
- container.extend([(name, url) for url in plugins[name]])
-
- del plugins[name]
-
- #directly write to database
- if self.pid > -1:
- for pluginname, urls in plugins.iteritems():
- plugin = self.m.core.pluginManager.getPlugin(pluginname, True)
- if hasattr(plugin, "getInfo"):
- self.fetchForPlugin(pluginname, plugin, urls, self.updateDB)
- self.m.core.files.save()
-
- elif self.add:
- for pluginname, urls in plugins.iteritems():
- plugin = self.m.core.pluginManager.getPlugin(pluginname, True)
- if hasattr(plugin, "getInfo"):
- self.fetchForPlugin(pluginname, plugin, urls, self.updateCache, True)
-
- else:
- #generate default result
- result = [(url, 0, 3, url) for url in urls]
-
- self.updateCache(pluginname, result)
-
- packs = parseNames([(name, url) for name, x, y, url in self.cache])
-
- self.m.log.debug("Fetched and generated %d packages" % len(packs))
-
- for k, v in packs:
- self.m.core.api.addPackage(k, v)
-
- #empty cache
- del self.cache[:]
-
- else: #post the results
-
-
- for name, url in container:
- #attach container content
- try:
- data = self.decryptContainer(name, url)
- except:
- print_exc()
- self.m.log.error("Could not decrypt container.")
- data = []
-
- for url, plugin in data:
- if plugin in plugins:
- plugins[plugin].append(url)
- else:
- plugins[plugin] = [url]
-
- self.m.infoResults[self.rid] = {}
-
- for pluginname, urls in plugins.iteritems():
- plugin = self.m.core.pluginManager.getPlugin(pluginname, True)
- if hasattr(plugin, "getInfo"):
- self.fetchForPlugin(pluginname, plugin, urls, self.updateResult, True)
-
- #force to process cache
- if self.cache:
- self.updateResult(pluginname, [], True)
-
- else:
- #generate default result
- result = [(url, 0, 3, url) for url in urls]
-
- self.updateResult(pluginname, result, True)
-
- self.m.infoResults[self.rid]["ALL_INFO_FETCHED"] = {}
-
- self.m.timestamp = time() + 5 * 60
-
-
- def updateDB(self, plugin, result):
- self.m.core.files.updateFileInfo(result, self.pid)
-
- def updateResult(self, plugin, result, force=False):
- #parse package name and generate result
- #accumulate results
-
- self.cache.extend(result)
-
- if len(self.cache) >= 20 or force:
- #used for package generating
- tmp = [(name, (url, OnlineStatus(name, plugin, "unknown", status, int(size))))
- for name, size, status, url in self.cache]
-
- data = parseNames(tmp)
- result = {}
- for k, v in data.iteritems():
- for url, status in v:
- status.packagename = k
- result[url] = status
-
- self.m.setInfoResults(self.rid, result)
-
- self.cache = []
-
- def updateCache(self, plugin, result):
- self.cache.extend(result)
-
- def fetchForPlugin(self, pluginname, plugin, urls, cb, err=None):
- try:
- result = [] #result loaded from cache
- process = [] #urls to process
- for url in urls:
- if url in self.m.infoCache:
- result.append(self.m.infoCache[url])
- else:
- process.append(url)
-
- if result:
- self.m.log.debug("Fetched %d values from cache for %s" % (len(result), pluginname))
- cb(pluginname, result)
-
- if process:
- self.m.log.debug("Run Info Fetching for %s" % pluginname)
- for result in plugin.getInfo(process):
- #result = [ .. (name, size, status, url) .. ]
- if not type(result) == list: result = [result]
-
- for res in result:
- self.m.infoCache[res[3]] = res
-
- cb(pluginname, result)
-
- self.m.log.debug("Finished Info Fetching for %s" % pluginname)
- except Exception, e:
- self.m.log.warning(_("Info Fetching for %(name)s failed | %(err)s") %
- {"name": pluginname, "err": str(e)})
- if self.m.core.debug:
- print_exc()
-
- # generate default results
- if err:
- result = [(url, 0, 3, url) for url in urls]
- cb(pluginname, result)
-
-
- def decryptContainer(self, plugin, url):
- data = []
- # only works on container plugins
-
- self.m.log.debug("Pre decrypting %s with %s" % (url, plugin))
-
- # dummy pyfile
- pyfile = PyFile(self.m.core.files, -1, url, url, 0, 0, "", plugin, -1, -1)
-
- pyfile.initPlugin()
-
- # little plugin lifecycle
- try:
- pyfile.plugin.setup()
- pyfile.plugin.loadToDisk()
- pyfile.plugin.decrypt(pyfile)
- pyfile.plugin.deleteTmp()
-
- for pack in pyfile.plugin.packages:
- pyfile.plugin.urls.extend(pack[1])
-
- data = self.m.core.pluginManager.parseUrls(pyfile.plugin.urls)
-
- self.m.log.debug("Got %d links." % len(data))
-
- except Exception, e:
- self.m.log.debug("Pre decrypting error: %s" % str(e))
- finally:
- pyfile.release()
-
- return data
diff --git a/module/PullEvents.py b/module/PullEvents.py
deleted file mode 100644
index 5ec76765e..000000000
--- a/module/PullEvents.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from time import time
-from module.utils import uniqify
-
-class PullManager():
- def __init__(self, core):
- self.core = core
- self.clients = []
-
- def newClient(self, uuid):
- self.clients.append(Client(uuid))
-
- def clean(self):
- for n, client in enumerate(self.clients):
- if client.lastActive + 30 < time():
- del self.clients[n]
-
- def getEvents(self, uuid):
- events = []
- validUuid = False
- for client in self.clients:
- if client.uuid == uuid:
- client.lastActive = time()
- validUuid = True
- while client.newEvents():
- events.append(client.popEvent().toList())
- break
- if not validUuid:
- self.newClient(uuid)
- events = [ReloadAllEvent("queue").toList(), ReloadAllEvent("collector").toList()]
- return uniqify(events, repr)
-
- def addEvent(self, event):
- for client in self.clients:
- client.addEvent(event)
-
-class Client():
- def __init__(self, uuid):
- self.uuid = uuid
- self.lastActive = time()
- self.events = []
-
- def newEvents(self):
- return len(self.events) > 0
-
- def popEvent(self):
- if not len(self.events):
- return None
- return self.events.pop(0)
-
- def addEvent(self, event):
- self.events.append(event)
-
-class UpdateEvent():
- def __init__(self, itype, iid, destination):
- assert itype == "pack" or itype == "file"
- assert destination == "queue" or destination == "collector"
- self.type = itype
- self.id = iid
- self.destination = destination
-
- def toList(self):
- return ["update", self.destination, self.type, self.id]
-
-class RemoveEvent():
- def __init__(self, itype, iid, destination):
- assert itype == "pack" or itype == "file"
- assert destination == "queue" or destination == "collector"
- self.type = itype
- self.id = iid
- self.destination = destination
-
- def toList(self):
- return ["remove", self.destination, self.type, self.id]
-
-class InsertEvent():
- def __init__(self, itype, iid, after, destination):
- assert itype == "pack" or itype == "file"
- assert destination == "queue" or destination == "collector"
- self.type = itype
- self.id = iid
- self.after = after
- self.destination = destination
-
- def toList(self):
- return ["insert", self.destination, self.type, self.id, self.after]
-
-class ReloadAllEvent():
- def __init__(self, destination):
- assert destination == "queue" or destination == "collector"
- self.destination = destination
-
- def toList(self):
- return ["reload", self.destination]
-
-class AccountUpdateEvent():
- def toList(self):
- return ["account"]
-
-class ConfigUpdateEvent():
- def toList(self):
- return ["config"]
diff --git a/module/PyFile.py b/module/PyFile.py
deleted file mode 100644
index 3dede9360..000000000
--- a/module/PyFile.py
+++ /dev/null
@@ -1,285 +0,0 @@
-#!/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
-"""
-
-from module.PullEvents import UpdateEvent
-from module.utils import formatSize, lock
-
-from time import sleep, time
-
-from threading import RLock
-
-statusMap = {
- "finished": 0,
- "offline": 1,
- "online": 2,
- "queued": 3,
- "skipped": 4,
- "waiting": 5,
- "temp. offline": 6,
- "starting": 7,
- "failed": 8,
- "aborted": 9,
- "decrypting": 10,
- "custom": 11,
- "downloading": 12,
- "processing": 13,
- "unknown": 14,
-}
-
-
-def setSize(self, value):
- self._size = int(value)
-
-class PyFile(object):
- """
- Represents a file object at runtime
- """
- __slots__ = ("m", "id", "url", "name", "size", "_size", "status", "pluginname", "packageid",
- "error", "order", "lock", "plugin", "waitUntil", "active", "abort", "statusname",
- "reconnected", "progress", "maxprogress", "pluginmodule", "pluginclass")
-
- def __init__(self, manager, id, url, name, size, status, error, pluginname, package, order):
- self.m = manager
-
- self.id = int(id)
- self.url = url
- self.name = name
- self.size = size
- self.status = status
- self.pluginname = pluginname
- self.packageid = package #should not be used, use package() instead
- self.error = error
- self.order = order
- # database information ends here
-
- self.lock = RLock()
-
- self.plugin = None
- #self.download = None
-
- self.waitUntil = 0 # time() + time to wait
-
- # status attributes
- self.active = False #obsolete?
- self.abort = False
- self.reconnected = False
-
- self.statusname = None
-
- self.progress = 0
- self.maxprogress = 100
-
- self.m.cache[int(id)] = self
-
-
- # will convert all sizes to ints
- size = property(lambda self: self._size, setSize)
-
- def __repr__(self):
- return "PyFile %s: %s@%s" % (self.id, self.name, self.pluginname)
-
- @lock
- def initPlugin(self):
- """ inits plugin instance """
- if not self.plugin:
- self.pluginmodule = self.m.core.pluginManager.getPlugin(self.pluginname)
- self.pluginclass = getattr(self.pluginmodule, self.m.core.pluginManager.getPluginName(self.pluginname))
- self.plugin = self.pluginclass(self)
-
- @lock
- def hasPlugin(self):
- """Thread safe way to determine this file has initialized plugin attribute
-
- :return:
- """
- return hasattr(self, "plugin") and self.plugin
-
- def package(self):
- """ return package instance"""
- return self.m.getPackage(self.packageid)
-
- def setStatus(self, status):
- self.status = statusMap[status]
- self.sync() #@TODO needed aslong no better job approving exists
-
- def setCustomStatus(self, msg, status="processing"):
- self.statusname = msg
- self.setStatus(status)
-
- def getStatusName(self):
- if self.status not in (13, 14) or not self.statusname:
- return self.m.statusMsg[self.status]
- else:
- return self.statusname
-
- def hasStatus(self, status):
- return statusMap[status] == self.status
-
- def sync(self):
- """sync PyFile instance with database"""
- self.m.updateLink(self)
-
- @lock
- def release(self):
- """sync and remove from cache"""
- # file has valid package
- if self.packageid > 0:
- self.sync()
-
- if hasattr(self, "plugin") and self.plugin:
- self.plugin.clean()
- del self.plugin
-
- self.m.releaseLink(self.id)
-
- def delete(self):
- """delete pyfile from database"""
- self.m.deleteLink(self.id)
-
- def toDict(self):
- """return dict with all information for interface"""
- return self.toDbDict()
-
- def toDbDict(self):
- """return data as dict for databse
-
- format:
-
- {
- id: {'url': url, 'name': name ... }
- }
-
- """
- return {
- self.id: {
- 'id': self.id,
- 'url': self.url,
- 'name': self.name,
- 'plugin': self.pluginname,
- 'size': self.getSize(),
- 'format_size': self.formatSize(),
- 'status': self.status,
- 'statusmsg': self.getStatusName(),
- 'package': self.packageid,
- 'error': self.error,
- 'order': self.order
- }
- }
-
- def abortDownload(self):
- """abort pyfile if possible"""
- while self.id in self.m.core.threadManager.processingIds():
- self.abort = True
- if self.plugin and self.plugin.req:
- self.plugin.req.abortDownloads()
- sleep(0.1)
-
- self.abort = False
- if self.hasPlugin() and self.plugin.req:
- self.plugin.req.abortDownloads()
-
- self.release()
-
- def finishIfDone(self):
- """set status to finish and release file if every thread is finished with it"""
-
- if self.id in self.m.core.threadManager.processingIds():
- return False
-
- self.setStatus("finished")
- self.release()
- self.m.checkAllLinksFinished()
- return True
-
- def checkIfProcessed(self):
- self.m.checkAllLinksProcessed(self.id)
-
- def formatWait(self):
- """ formats and return wait time in humanreadable format """
- seconds = self.waitUntil - time()
-
- if seconds < 0: return "00:00:00"
-
- hours, seconds = divmod(seconds, 3600)
- minutes, seconds = divmod(seconds, 60)
- return "%.2i:%.2i:%.2i" % (hours, minutes, seconds)
-
- def formatSize(self):
- """ formats size to readable format """
- return formatSize(self.getSize())
-
- def formatETA(self):
- """ formats eta to readable format """
- seconds = self.getETA()
-
- if seconds < 0: return "00:00:00"
-
- hours, seconds = divmod(seconds, 3600)
- minutes, seconds = divmod(seconds, 60)
- return "%.2i:%.2i:%.2i" % (hours, minutes, seconds)
-
- def getSpeed(self):
- """ calculates speed """
- try:
- return self.plugin.req.speed
- except:
- return 0
-
- def getETA(self):
- """ gets established time of arrival"""
- try:
- return self.getBytesLeft() / self.getSpeed()
- except:
- return 0
-
- def getBytesLeft(self):
- """ gets bytes left """
- try:
- return self.plugin.req.size - self.plugin.req.arrived
- except:
- return 0
-
- def getPercent(self):
- """ get % of download """
- if self.status == 12:
- try:
- return self.plugin.req.percent
- except:
- return 0
- else:
- return self.progress
-
- def getSize(self):
- """ get size of download """
- try:
- if self.plugin.req.size:
- return self.plugin.req.size
- else:
- return self.size
- except:
- return self.size
-
- def notifyChange(self):
- e = UpdateEvent("file", self.id, "collector" if not self.package().queue else "queue")
- self.m.core.pullManager.addEvent(e)
-
- def setProgress(self, value):
- if not value == self.progress:
- self.progress = value
- self.notifyChange()
diff --git a/module/PyPackage.py b/module/PyPackage.py
deleted file mode 100644
index f3be6c886..000000000
--- a/module/PyPackage.py
+++ /dev/null
@@ -1,80 +0,0 @@
-#!/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
-"""
-
-from module.PullEvents import UpdateEvent
-from module.utils import save_path
-
-class PyPackage():
- """
- Represents a package object at runtime
- """
- def __init__(self, manager, id, name, folder, site, password, queue, order):
- self.m = manager
- self.m.packageCache[int(id)] = self
-
- self.id = int(id)
- self.name = name
- self._folder = folder
- self.site = site
- self.password = password
- self.queue = queue
- self.order = order
- self.setFinished = False
-
- @property
- def folder(self):
- return save_path(self._folder)
-
- def toDict(self):
- """ Returns a dictionary representation of the data.
-
- :return: dict: {id: { attr: value }}
- """
- return {
- self.id: {
- 'id': self.id,
- 'name': self.name,
- 'folder': self.folder,
- 'site': self.site,
- 'password': self.password,
- 'queue': self.queue,
- 'order': self.order,
- 'links': {}
- }
- }
-
- def getChildren(self):
- """get information about contained links"""
- return self.m.getPackageData(self.id)["links"]
-
- def sync(self):
- """sync with db"""
- self.m.updatePackage(self)
-
- def release(self):
- """sync and delete from cache"""
- self.sync()
- self.m.releasePackage(self.id)
-
- def delete(self):
- self.m.deletePackage(self.id)
-
- def notifyChange(self):
- e = UpdateEvent("pack", self.id, "collector" if not self.queue else "queue")
- self.m.core.pullManager.addEvent(e)
diff --git a/module/Scheduler.py b/module/Scheduler.py
deleted file mode 100644
index 0bc396b69..000000000
--- a/module/Scheduler.py
+++ /dev/null
@@ -1,141 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from time import time
-from heapq import heappop, heappush
-from thread import start_new_thread
-from threading import Lock
-
-class AlreadyCalled(Exception):
- pass
-
-
-class Deferred():
- def __init__(self):
- self.call = []
- self.result = ()
-
- def addCallback(self, f, *cargs, **ckwargs):
- self.call.append((f, cargs, ckwargs))
-
- def callback(self, *args, **kwargs):
- if self.result:
- raise AlreadyCalled
- self.result = (args, kwargs)
- for f, cargs, ckwargs in self.call:
- args += tuple(cargs)
- kwargs.update(ckwargs)
- f(*args ** kwargs)
-
-
-class Scheduler():
- def __init__(self, core):
- self.core = core
-
- self.queue = PriorityQueue()
-
- def addJob(self, t, call, args=[], kwargs={}, threaded=True):
- d = Deferred()
- t += time()
- j = Job(t, call, args, kwargs, d, threaded)
- self.queue.put((t, j))
- return d
-
-
- def removeJob(self, d):
- """
- :param d: defered object
- :return: if job was deleted
- """
- index = -1
-
- for i, j in enumerate(self.queue):
- if j[1].deferred == d:
- index = i
-
- if index >= 0:
- del self.queue[index]
- return True
-
- return False
-
- def work(self):
- while True:
- t, j = self.queue.get()
- if not j:
- break
- else:
- if t <= time():
- j.start()
- else:
- self.queue.put((t, j))
- break
-
-
-class Job():
- def __init__(self, time, call, args=[], kwargs={}, deferred=None, threaded=True):
- self.time = float(time)
- self.call = call
- self.args = args
- self.kwargs = kwargs
- self.deferred = deferred
- self.threaded = threaded
-
- def run(self):
- ret = self.call(*self.args, **self.kwargs)
- if self.deferred is None:
- return
- else:
- self.deferred.callback(ret)
-
- def start(self):
- if self.threaded:
- start_new_thread(self.run, ())
- else:
- self.run()
-
-
-class PriorityQueue():
- """ a non blocking priority queue """
-
- def __init__(self):
- self.queue = []
- self.lock = Lock()
-
- def __iter__(self):
- return iter(self.queue)
-
- def __delitem__(self, key):
- del self.queue[key]
-
- def put(self, element):
- self.lock.acquire()
- heappush(self.queue, element)
- self.lock.release()
-
- def get(self):
- """ return element or None """
- self.lock.acquire()
- try:
- el = heappop(self.queue)
- return el
- except IndexError:
- return None, None
- finally:
- self.lock.release() \ No newline at end of file
diff --git a/module/ThreadManager.py b/module/ThreadManager.py
deleted file mode 100644
index 8937f4a29..000000000
--- a/module/ThreadManager.py
+++ /dev/null
@@ -1,318 +0,0 @@
-#!/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
-"""
-
-from os.path import exists, join
-import re
-from subprocess import Popen
-from threading import Event, Lock
-from time import sleep, time
-from traceback import print_exc
-from random import choice
-
-import pycurl
-
-import PluginThread
-from module.PyFile import PyFile
-from module.network.RequestFactory import getURL
-from module.utils import freeSpace, lock
-
-
-class ThreadManager:
- """manages the download threads, assign jobs, reconnect etc"""
-
-
- def __init__(self, core):
- """Constructor"""
- self.core = core
- self.log = core.log
-
- self.threads = [] # thread list
- self.localThreads = [] #hook+decrypter threads
-
- self.pause = True
-
- self.reconnecting = Event()
- self.reconnecting.clear()
- self.downloaded = 0 #number of files downloaded since last cleanup
-
- self.lock = Lock()
-
- # some operations require to fetch url info from hoster, so we caching them so it wont be done twice
- # contains a timestamp and will be purged after timeout
- self.infoCache = {}
-
- # pool of ids for online check
- self.resultIDs = 0
-
- # threads which are fetching hoster results
- self.infoResults = {}
- #timeout for cache purge
- self.timestamp = 0
-
- pycurl.global_init(pycurl.GLOBAL_DEFAULT)
-
- for i in range(0, self.core.config.get("download", "max_downloads")):
- self.createThread()
-
-
- def createThread(self):
- """create a download thread"""
-
- thread = PluginThread.DownloadThread(self)
- self.threads.append(thread)
-
- def createInfoThread(self, data, pid):
- """
- start a thread whichs fetches online status and other infos
- data = [ .. () .. ]
- """
- self.timestamp = time() + 5 * 60
-
- PluginThread.InfoThread(self, data, pid)
-
- @lock
- def createResultThread(self, data, add=False):
- """ creates a thread to fetch online status, returns result id """
- self.timestamp = time() + 5 * 60
-
- rid = self.resultIDs
- self.resultIDs += 1
-
- PluginThread.InfoThread(self, data, rid=rid, add=add)
-
- return rid
-
-
- @lock
- def getInfoResult(self, rid):
- """returns result and clears it"""
- self.timestamp = time() + 5 * 60
-
- if rid in self.infoResults:
- data = self.infoResults[rid]
- self.infoResults[rid] = {}
- return data
- else:
- return {}
-
- @lock
- def setInfoResults(self, rid, result):
- self.infoResults[rid].update(result)
-
- def getActiveFiles(self):
- active = [x.active for x in self.threads if x.active and isinstance(x.active, PyFile)]
-
- for t in self.localThreads:
- active.extend(t.getActiveFiles())
-
- return active
-
- def processingIds(self):
- """get a id list of all pyfiles processed"""
- return [x.id for x in self.getActiveFiles()]
-
-
- def work(self):
- """run all task which have to be done (this is for repetivive call by core)"""
- try:
- self.tryReconnect()
- except Exception, e:
- self.log.error(_("Reconnect Failed: %s") % str(e) )
- self.reconnecting.clear()
- if self.core.debug:
- print_exc()
- self.checkThreadCount()
-
- try:
- self.assignJob()
- except Exception, e:
- self.log.warning("Assign job error", e)
- if self.core.debug:
- print_exc()
-
- sleep(0.5)
- self.assignJob()
- #it may be failed non critical so we try it again
-
- if (self.infoCache or self.infoResults) and self.timestamp < time():
- self.infoCache.clear()
- self.infoResults.clear()
- self.log.debug("Cleared Result cache")
-
- #----------------------------------------------------------------------
- def tryReconnect(self):
- """checks if reconnect needed"""
-
- if not (self.core.config["reconnect"]["activated"] and self.core.api.isTimeReconnect()):
- return False
-
- active = [x.active.plugin.wantReconnect and x.active.plugin.waiting for x in self.threads if x.active]
-
- if not (0 < active.count(True) == len(active)):
- return False
-
- if not exists(self.core.config['reconnect']['method']):
- if exists(join(pypath, self.core.config['reconnect']['method'])):
- self.core.config['reconnect']['method'] = join(pypath, self.core.config['reconnect']['method'])
- else:
- self.core.config["reconnect"]["activated"] = False
- self.log.warning(_("Reconnect script not found!"))
- return
-
- self.reconnecting.set()
-
- #Do reconnect
- self.log.info(_("Starting reconnect"))
-
- while [x.active.plugin.waiting for x in self.threads if x.active].count(True) != 0:
- sleep(0.25)
-
- ip = self.getIP()
-
- self.core.hookManager.beforeReconnecting(ip)
-
- self.log.debug("Old IP: %s" % ip)
-
- try:
- reconn = Popen(self.core.config['reconnect']['method'], bufsize=-1, shell=True)#, stdout=subprocess.PIPE)
- except:
- self.log.warning(_("Failed executing reconnect script!"))
- self.core.config["reconnect"]["activated"] = False
- self.reconnecting.clear()
- if self.core.debug:
- print_exc()
- return
-
- reconn.wait()
- sleep(1)
- ip = self.getIP()
- self.core.hookManager.afterReconnecting(ip)
-
- self.log.info(_("Reconnected, new IP: %s") % ip)
-
- self.reconnecting.clear()
-
- def getIP(self):
- """retrieve current ip"""
- services = [("http://automation.whatismyip.com/n09230945.asp", "(\S+)"),
- ("http://checkip.dyndns.org/",".*Current IP Address: (\S+)</body>.*")]
-
- ip = ""
- for i in range(10):
- try:
- sv = choice(services)
- ip = getURL(sv[0])
- ip = re.match(sv[1], ip).group(1)
- break
- except:
- ip = ""
- sleep(1)
-
- return ip
-
- #----------------------------------------------------------------------
- def checkThreadCount(self):
- """checks if there are need for increasing or reducing thread count"""
-
- if len(self.threads) == self.core.config.get("download", "max_downloads"):
- return True
- elif len(self.threads) < self.core.config.get("download", "max_downloads"):
- self.createThread()
- else:
- free = [x for x in self.threads if not x.active]
- if free:
- free[0].put("quit")
-
-
- def cleanPycurl(self):
- """ make a global curl cleanup (currently ununused) """
- if self.processingIds():
- return False
- pycurl.global_cleanup()
- pycurl.global_init(pycurl.GLOBAL_DEFAULT)
- self.downloaded = 0
- self.log.debug("Cleaned up pycurl")
- return True
-
- #----------------------------------------------------------------------
- def assignJob(self):
- """assing a job to a thread if possible"""
-
- if self.pause or not self.core.api.isTimeDownload(): return
-
- #if self.downloaded > 20:
- # if not self.cleanPyCurl(): return
-
- free = [x for x in self.threads if not x.active]
-
- inuse = set([(x.active.pluginname,self.getLimit(x)) for x in self.threads if x.active and x.active.hasPlugin() and x.active.plugin.account])
- inuse = map(lambda x : (x[0], x[1], len([y for y in self.threads if y.active and y.active.pluginname == x[0]])) ,inuse)
- onlimit = [x[0] for x in inuse if x[1] > 0 and x[2] >= x[1]]
-
- occ = [x.active.pluginname for x in self.threads if x.active and x.active.hasPlugin() and not x.active.plugin.multiDL] + onlimit
-
- occ.sort()
- occ = tuple(set(occ))
- job = self.core.files.getJob(occ)
- if job:
- try:
- job.initPlugin()
- except Exception, e:
- self.log.critical(str(e))
- print_exc()
- job.setStatus("failed")
- job.error = str(e)
- job.release()
- return
-
- if job.plugin.__type__ == "hoster":
- spaceLeft = freeSpace(self.core.config["general"]["download_folder"]) / 1024 / 1024
- if spaceLeft < self.core.config["general"]["min_free_space"]:
- self.log.warning(_("Not enough space left on device"))
- self.pause = True
-
- if free and not self.pause:
- thread = free[0]
- #self.downloaded += 1
-
- thread.put(job)
- else:
- #put job back
- if occ not in self.core.files.jobCache:
- self.core.files.jobCache[occ] = []
- self.core.files.jobCache[occ].append(job.id)
-
- #check for decrypt jobs
- job = self.core.files.getDecryptJob()
- if job:
- job.initPlugin()
- thread = PluginThread.DecrypterThread(self, job)
-
-
- else:
- thread = PluginThread.DecrypterThread(self, job)
-
- def getLimit(self, thread):
- limit = thread.active.plugin.account.getAccountData(thread.active.plugin.user)["options"].get("limitDL",["0"])[0]
- return int(limit)
-
- def cleanup(self):
- """do global cleanup, should be called when finished with pycurl"""
- pycurl.global_cleanup()
diff --git a/module/__init__.py b/module/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/__init__.py
+++ /dev/null
diff --git a/module/cli/AddPackage.py b/module/cli/AddPackage.py
deleted file mode 100644
index a73401586..000000000
--- a/module/cli/AddPackage.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-#Copyright (C) 2011 RaNaN
-#
-#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/>.
-#
-###
-
-from Handler import Handler
-from printer import *
-
-class AddPackage(Handler):
- """ let the user add packages """
-
- def init(self):
- self.name = ""
- self.urls = []
-
- def onEnter(self, inp):
- if inp == "0":
- self.cli.reset()
-
- if not self.name:
- self.name = inp
- self.setInput()
- elif inp == "END":
- #add package
- self.client.addPackage(self.name, self.urls, 1)
- self.cli.reset()
- else:
- if inp.strip():
- self.urls.append(inp)
- self.setInput()
-
- def renderBody(self, line):
- println(line, white(_("Add Package:")))
- println(line + 1, "")
- line += 2
-
- if not self.name:
- println(line, _("Enter a name for the new package"))
- println(line + 1, "")
- line += 2
- else:
- println(line, _("Package: %s") % self.name)
- println(line + 1, _("Parse the links you want to add."))
- println(line + 2, _("Type %s when done.") % mag("END"))
- println(line + 3, _("Links added: ") + mag(len(self.urls)))
- line += 4
-
- println(line, "")
- println(line + 1, mag("0.") + _(" back to main menu"))
-
- return line + 2 \ No newline at end of file
diff --git a/module/cli/Handler.py b/module/cli/Handler.py
deleted file mode 100644
index 476d09386..000000000
--- a/module/cli/Handler.py
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-#Copyright (C) 2011 RaNaN
-#
-#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/>.
-#
-###
-class Handler:
- def __init__(self, cli):
- self.cli = cli
- self.init()
-
- client = property(lambda self: self.cli.client)
- input = property(lambda self: self.cli.input)
-
- def init(self):
- pass
-
- def onChar(self, char):
- pass
-
- def onBackSpace(self):
- pass
-
- def onEnter(self, inp):
- pass
-
- def setInput(self, inp=""):
- self.cli.setInput(inp)
-
- def backspace(self):
- self.cli.setInput(self.input[:-1])
-
- def renderBody(self, line):
- """ gets the line where to render output and should return the line number below its content """
- return line + 1 \ No newline at end of file
diff --git a/module/cli/ManageFiles.py b/module/cli/ManageFiles.py
deleted file mode 100644
index 4d0377d9d..000000000
--- a/module/cli/ManageFiles.py
+++ /dev/null
@@ -1,204 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-#Copyright (C) 2011 RaNaN
-#
-#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/>.
-#
-###
-
-from itertools import islice
-from time import time
-
-from Handler import Handler
-from printer import *
-
-from module.Api import Destination, PackageData
-
-class ManageFiles(Handler):
- """ possibility to manage queue/collector """
-
- def init(self):
- self.target = Destination.Queue
- self.pos = 0 #position in queue
- self.package = -1 #choosen package
- self.mode = "" # move/delete/restart
-
- self.cache = None
- self.links = None
- self.time = 0
-
- def onChar(self, char):
- if char in ("m", "d", "r"):
- self.mode = char
- self.setInput()
- elif char == "p":
- self.pos = max(0, self.pos - 5)
- self.backspace()
- elif char == "n":
- self.pos += 5
- self.backspace()
-
- def onBackSpace(self):
- if not self.input and self.mode:
- self.mode = ""
- if not self.input and self.package > -1:
- self.package = -1
-
- def onEnter(self, input):
- if input == "0":
- self.cli.reset()
- elif self.package < 0 and self.mode:
- #mode select
- packs = self.parseInput(input)
- if self.mode == "m":
- [self.client.movePackage((self.target + 1) % 2, x) for x in packs]
- elif self.mode == "d":
- self.client.deletePackages(packs)
- elif self.mode == "r":
- [self.client.restartPackage(x) for x in packs]
-
- elif self.mode:
- #edit links
- links = self.parseInput(input, False)
-
- if self.mode == "d":
- self.client.deleteFiles(links)
- elif self.mode == "r":
- map(self.client.restartFile, links)
-
- else:
- #look into package
- try:
- self.package = int(input)
- except:
- pass
-
- self.cache = None
- self.links = None
- self.pos = 0
- self.mode = ""
- self.setInput()
-
-
- def renderBody(self, line):
- if self.package < 0:
- println(line, white(_("Manage Packages:")))
- else:
- println(line, white((_("Manage Links:"))))
- line += 1
-
- if self.mode:
- if self.mode == "m":
- println(line, _("What do you want to move?"))
- elif self.mode == "d":
- println(line, _("What do you want to delete?"))
- elif self.mode == "r":
- println(line, _("What do you want to restart?"))
-
- println(line + 1, "Enter single number, comma seperated numbers or ranges. eg. 1,2,3 or 1-3.")
- line += 2
- else:
- println(line, _("Choose what yout want to do or enter package number."))
- println(line + 1, ("%s - %%s, %s - %%s, %s - %%s" % (mag("d"), mag("m"), mag("r"))) % (
- _("delete"), _("move"), _("restart")))
- line += 2
-
- if self.package < 0:
- #print package info
- pack = self.getPackages()
- i = 0
- for value in islice(pack, self.pos, self.pos + 5):
- try:
- println(line, mag(str(value.pid)) + ": " + value.name)
- line += 1
- i += 1
- except Exception, e:
- pass
- for x in range(5 - i):
- println(line, "")
- line += 1
- else:
- #print links info
- pack = self.getLinks()
- i = 0
- for value in islice(pack.links, self.pos, self.pos + 5):
- try:
- println(line, mag(value.fid) + ": %s | %s | %s" % (
- value.name, value.statusmsg, value.plugin))
- line += 1
- i += 1
- except Exception, e:
- pass
- for x in range(5 - i):
- println(line, "")
- line += 1
-
- println(line, mag("p") + _(" - previous") + " | " + mag("n") + _(" - next"))
- println(line + 1, mag("0.") + _(" back to main menu"))
-
- return line + 2
-
-
- def getPackages(self):
- if self.cache and self.time + 2 < time():
- return self.cache
-
- if self.target == Destination.Queue:
- data = self.client.getQueue()
- else:
- data = self.client.getCollector()
-
-
- self.cache = data
- self.time = time()
-
- return data
-
- def getLinks(self):
- if self.links and self.time + 1 < time():
- return self.links
-
- try:
- data = self.client.getPackageData(self.package)
- except:
- data = PackageData(links=[])
-
- self.links = data
- self.time = time()
-
- return data
-
- def parseInput(self, inp, package=True):
- inp = inp.strip()
- if "-" in inp:
- l, n, h = inp.partition("-")
- l = int(l)
- h = int(h)
- r = range(l, h + 1)
-
- ret = []
- if package:
- for p in self.cache:
- if p.pid in r:
- ret.append(p.pid)
- else:
- for l in self.links.links:
- if l.lid in r:
- ret.append(l.lid)
-
- return ret
-
- else:
- return [int(x) for x in inp.split(",")]
diff --git a/module/cli/__init__.py b/module/cli/__init__.py
deleted file mode 100644
index fa8a09291..000000000
--- a/module/cli/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-from AddPackage import AddPackage
-from ManageFiles import ManageFiles \ No newline at end of file
diff --git a/module/cli/printer.py b/module/cli/printer.py
deleted file mode 100644
index c62c1800e..000000000
--- a/module/cli/printer.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-def blue(string):
- return "\033[1;34m" + unicode(string) + "\033[0m"
-
-def green(string):
- return "\033[1;32m" + unicode(string) + "\033[0m"
-
-def yellow(string):
- return "\033[1;33m" + unicode(string) + "\033[0m"
-
-def red(string):
- return "\033[1;31m" + unicode(string) + "\033[0m"
-
-def cyan(string):
- return "\033[1;36m" + unicode(string) + "\033[0m"
-
-def mag(string):
- return "\033[1;35m" + unicode(string) + "\033[0m"
-
-def white(string):
- return "\033[1;37m" + unicode(string) + "\033[0m"
-
-def println(line, content):
- print "\033[" + str(line) + ";0H\033[2K" + content \ No newline at end of file
diff --git a/module/common/APIExerciser.py b/module/common/APIExerciser.py
deleted file mode 100644
index 96f5ce9cf..000000000
--- a/module/common/APIExerciser.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import string
-from threading import Thread
-from random import choice, random, sample, randint
-from time import time, sleep
-from math import floor
-import gc
-
-from traceback import print_exc, format_exc
-
-from module.remote.thriftbackend.ThriftClient import ThriftClient, Destination
-
-def createURLs():
- """ create some urls, some may fail """
- urls = []
- for x in range(0, randint(20, 100)):
- name = "DEBUG_API"
- if randint(0, 5) == 5:
- name = "" #this link will fail
-
- urls.append(name + "".join(sample(string.ascii_letters, randint(10, 20))))
-
- return urls
-
-AVOID = (0,3,8)
-
-idPool = 0
-sumCalled = 0
-
-
-def startApiExerciser(core, n):
- for i in range(n):
- APIExerciser(core).start()
-
-class APIExerciser(Thread):
-
-
- def __init__(self, core, thrift=False, user=None, pw=None):
- global idPool
-
- Thread.__init__(self)
- self.setDaemon(True)
- self.core = core
- self.count = 0 #number of methods
- self.time = time()
-
- if thrift:
- self.api = ThriftClient(user=user, password=pw)
- else:
- self.api = core.api
-
-
- self.id = idPool
-
- idPool += 1
-
- #self.start()
-
- def run(self):
-
- self.core.log.info("API Excerciser started %d" % self.id)
-
- out = open("error.log", "ab")
- #core errors are not logged of course
- out.write("\n" + "Starting\n")
- out.flush()
-
- while True:
- try:
- self.testAPI()
- except Exception:
- self.core.log.error("Excerciser %d throw an execption" % self.id)
- print_exc()
- out.write(format_exc() + 2 * "\n")
- out.flush()
-
- if not self.count % 100:
- self.core.log.info("Exerciser %d tested %d api calls" % (self.id, self.count))
- if not self.count % 1000:
- out.flush()
-
- if not sumCalled % 1000: #not thread safe
- self.core.log.info("Exercisers tested %d api calls" % sumCalled)
- persec = sumCalled / (time() - self.time)
- self.core.log.info("Approx. %.2f calls per second." % persec)
- self.core.log.info("Approx. %.2f ms per call." % (1000 / persec))
- self.core.log.info("Collected garbage: %d" % gc.collect())
-
-
- #sleep(random() / 500)
-
- def testAPI(self):
- global sumCalled
-
- m = ["statusDownloads", "statusServer", "addPackage", "getPackageData", "getFileData", "deleteFiles",
- "deletePackages", "getQueue", "getCollector", "getQueueData", "getCollectorData", "isCaptchaWaiting",
- "getCaptchaTask", "stopAllDownloads", "getAllInfo", "getServices" , "getAccounts", "getAllUserData"]
-
- method = choice(m)
- #print "Testing:", method
-
- if hasattr(self, method):
- res = getattr(self, method)()
- else:
- res = getattr(self.api, method)()
-
- self.count += 1
- sumCalled += 1
-
- #print res
-
- def addPackage(self):
- name = "".join(sample(string.ascii_letters, 10))
- urls = createURLs()
-
- self.api.addPackage(name, urls, choice([Destination.Queue, Destination.Collector]))
-
-
- def deleteFiles(self):
- info = self.api.getQueueData()
- if not info: return
-
- pack = choice(info)
- fids = pack.links
-
- if len(fids):
- fids = [f.fid for f in sample(fids, randint(1, max(len(fids) / 2, 1)))]
- self.api.deleteFiles(fids)
-
-
- def deletePackages(self):
- info = choice([self.api.getQueue(), self.api.getCollector()])
- if not info: return
-
- pids = [p.pid for p in info]
- if len(pids):
- pids = sample(pids, randint(1, max(floor(len(pids) / 2.5), 1)))
- self.api.deletePackages(pids)
-
- def getFileData(self):
- info = self.api.getQueueData()
- if info:
- p = choice(info)
- if p.links:
- self.api.getFileData(choice(p.links).fid)
-
- def getPackageData(self):
- info = self.api.getQueue()
- if info:
- self.api.getPackageData(choice(info).pid)
-
- def getAccounts(self):
- self.api.getAccounts(False)
-
- def getCaptchaTask(self):
- self.api.getCaptchaTask(False) \ No newline at end of file
diff --git a/module/common/ImportDebugger.py b/module/common/ImportDebugger.py
deleted file mode 100644
index a997f7b0c..000000000
--- a/module/common/ImportDebugger.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import sys
-
-class ImportDebugger(object):
-
- def __init__(self):
- self.imported = {}
-
- def find_module(self, name, path=None):
-
- if name not in self.imported:
- self.imported[name] = 0
-
- self.imported[name] += 1
-
- print name, path
-
-sys.meta_path.append(ImportDebugger()) \ No newline at end of file
diff --git a/module/common/JsEngine.py b/module/common/JsEngine.py
deleted file mode 100644
index 576be2a1b..000000000
--- a/module/common/JsEngine.py
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/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
-"""
-
-from imp import find_module
-from os.path import join, exists
-from urllib import quote
-
-
-ENGINE = ""
-
-DEBUG = False
-JS = False
-PYV8 = False
-RHINO = False
-
-
-if not ENGINE:
- try:
- import subprocess
-
- subprocess.Popen(["js", "-v"], bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
- p = subprocess.Popen(["js", "-e", "print(23+19)"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- out, err = p.communicate()
- #integrity check
- if out.strip() == "42":
- ENGINE = "js"
- JS = True
- except:
- pass
-
-if not ENGINE or DEBUG:
- try:
- find_module("PyV8")
- ENGINE = "pyv8"
- PYV8 = True
- except:
- pass
-
-if not ENGINE or DEBUG:
- try:
- path = "" #path where to find rhino
-
- if exists("/usr/share/java/js.jar"):
- path = "/usr/share/java/js.jar"
- elif exists("js.jar"):
- path = "js.jar"
- elif exists(join(pypath, "js.jar")): #may raises an exception, but js.jar wasnt found anyway
- path = join(pypath, "js.jar")
-
- if not path:
- raise Exception
-
- import subprocess
-
- p = subprocess.Popen(["java", "-cp", path, "org.mozilla.javascript.tools.shell.Main", "-e", "print(23+19)"],
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- out, err = p.communicate()
- #integrity check
- if out.strip() == "42":
- ENGINE = "rhino"
- RHINO = True
- except:
- pass
-
-class JsEngine():
- def __init__(self):
- self.engine = ENGINE
- self.init = False
-
- def __nonzero__(self):
- return False if not ENGINE else True
-
- def eval(self, script):
- if not self.init:
- if ENGINE == "pyv8" or (DEBUG and PYV8):
- import PyV8
- global PyV8
-
- self.init = True
-
- if type(script) == unicode:
- script = script.encode("utf8")
-
- if not ENGINE:
- raise Exception("No JS Engine")
-
- if not DEBUG:
- if ENGINE == "pyv8":
- return self.eval_pyv8(script)
- elif ENGINE == "js":
- return self.eval_js(script)
- elif ENGINE == "rhino":
- return self.eval_rhino(script)
- else:
- results = []
- if PYV8:
- res = self.eval_pyv8(script)
- print "PyV8:", res
- results.append(res)
- if JS:
- res = self.eval_js(script)
- print "JS:", res
- results.append(res)
- if RHINO:
- res = self.eval_rhino(script)
- print "Rhino:", res
- results.append(res)
-
- warning = False
- for x in results:
- for y in results:
- if x != y:
- warning = True
-
- if warning: print "### WARNING ###: Different results"
-
- return results[0]
-
- def eval_pyv8(self, script):
- rt = PyV8.JSContext()
- rt.enter()
- return rt.eval(script)
-
- def eval_js(self, script):
- script = "print(eval(unescape('%s')))" % quote(script)
- p = subprocess.Popen(["js", "-e", script], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=-1)
- out, err = p.communicate()
- res = out.strip()
- return res
-
- def eval_rhino(self, script):
- script = "print(eval(unescape('%s')))" % quote(script)
- p = subprocess.Popen(["java", "-cp", path, "org.mozilla.javascript.tools.shell.Main", "-e", script],
- stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=-1)
- out, err = p.communicate()
- res = out.strip()
- return res.decode("utf8").encode("ISO-8859-1")
-
- def error(self):
- return _("No js engine detected, please install either Spidermonkey, ossp-js, pyv8 or rhino")
-
-if __name__ == "__main__":
- js = JsEngine()
-
- test = u'"Ì"+"À"'
- js.eval(test) \ No newline at end of file
diff --git a/module/common/__init__.py b/module/common/__init__.py
deleted file mode 100644
index de6d13128..000000000
--- a/module/common/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-__author__ = 'christian'
- \ No newline at end of file
diff --git a/module/common/json_layer.py b/module/common/json_layer.py
deleted file mode 100644
index 4d57a9f38..000000000
--- a/module/common/json_layer.py
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# abstraction layer for json operations
-
-try: # since python 2.6
- import json
- from json import loads as json_loads
- from json import dumps as json_dumps
-except ImportError: #use system simplejson if available
- import simplejson as json
- from simplejson import loads as json_loads
- from simplejson import dumps as json_dumps
diff --git a/module/common/packagetools.py b/module/common/packagetools.py
deleted file mode 100644
index 5bfbcba95..000000000
--- a/module/common/packagetools.py
+++ /dev/null
@@ -1,155 +0,0 @@
-#!/usr/bin/env python
-
-# JDownloader/src/jd/controlling/LinkGrabberPackager.java
-
-import re
-from urlparse import urlparse
-
-def matchFirst(string, *args):
- """ matches against list of regexp and returns first match"""
- for patternlist in args:
- for pattern in patternlist:
- r = pattern.search(string)
- if r is not None:
- name = r.group(1)
- return name
-
- return string
-
-
-def parseNames(files):
- """ Generates packages names from name, data lists
-
- :param files: list of (name, data)
- :return: packagenames mapt to data lists (eg. urls)
- """
- packs = {}
-
- endings = "\\.(3gp|7zip|7z|abr|ac3|aiff|aifc|aif|ai|au|avi|bin|bz2|cbr|cbz|ccf|cue|cvd|chm|dta|deb|divx|djvu|dlc|dmg|doc|docx|dot|eps|exe|ff|flv|f4v|gsd|gif|gz|iwd|iso|ipsw|java|jar|jpg|jpeg|jdeatme|load|mws|mw|m4v|m4a|mkv|mp2|mp3|mp4|mov|movie|mpeg|mpe|mpg|msi|msu|msp|nfo|npk|oga|ogg|ogv|otrkey|pkg|png|pdf|pptx|ppt|pps|ppz|pot|psd|qt|rmvb|rm|rar|ram|ra|rev|rnd|r\\d+|rpm|run|rsdf|rtf|sh(!?tml)|srt|snd|sfv|swf|tar|tif|tiff|ts|txt|viv|vivo|vob|wav|wmv|xla|xls|xpi|zeno|zip|z\\d+|_[_a-z]{2}|\\d+$)"
-
- rarPats = [re.compile("(.*)(\\.|_|-)pa?r?t?\\.?[0-9]+.(rar|exe)$", re.I),
- re.compile("(.*)(\\.|_|-)part\\.?[0]*[1].(rar|exe)$", re.I),
- re.compile("(.*)\\.rar$", re.I),
- re.compile("(.*)\\.r\\d+$", re.I),
- re.compile("(.*)(\\.|_|-)\\d+$", re.I)]
-
- zipPats = [re.compile("(.*)\\.zip$", re.I),
- re.compile("(.*)\\.z\\d+$", re.I),
- re.compile("(?is).*\\.7z\\.[\\d]+$", re.I),
- re.compile("(.*)\\.a.$", re.I)]
-
- ffsjPats = [re.compile("(.*)\\._((_[a-z])|([a-z]{2}))(\\.|$)"),
- re.compile("(.*)(\\.|_|-)[\\d]+(" + endings + "$)", re.I)]
-
- iszPats = [re.compile("(.*)\\.isz$", re.I),
- re.compile("(.*)\\.i\\d{2}$", re.I)]
-
- pat1 = re.compile("(\\.?CD\\d+)", re.I)
- pat2 = re.compile("(\\.?part\\d+)", re.I)
-
- pat3 = re.compile("(.+)[\\.\\-_]+$")
- pat4 = re.compile("(.+)\\.\\d+\\.xtm$")
-
- for file, url in files:
- patternMatch = False
-
- if file is None:
- continue
-
- # remove trailing /
- name = file.rstrip('/')
-
- # extract last path part .. if there is a path
- split = name.rsplit("/", 1)
- if len(split) > 1:
- name = split.pop(1)
-
- #check if a already existing package may be ok for this file
- # found = False
- # for pack in packs:
- # if pack in file:
- # packs[pack].append(url)
- # found = True
- # break
- #
- # if found: continue
-
- # unrar pattern, 7zip/zip and hjmerge pattern, isz pattern, FFSJ pattern
- before = name
- name = matchFirst(name, rarPats, zipPats, iszPats, ffsjPats)
- if before != name:
- patternMatch = True
-
- # xtremsplit pattern
- r = pat4.search(name)
- if r is not None:
- name = r.group(1)
-
- # remove part and cd pattern
- r = pat1.search(name)
- if r is not None:
- name = name.replace(r.group(0), "")
- patternMatch = True
-
- r = pat2.search(name)
- if r is not None:
- name = name.replace(r.group(0), "")
- patternMatch = True
-
- # additional checks if extension pattern matched
- if patternMatch:
- # remove extension
- index = name.rfind(".")
- if index <= 0:
- index = name.rfind("_")
- if index > 0:
- length = len(name) - index
- if length <= 4:
- name = name[:-length]
-
- # remove endings like . _ -
- r = pat3.search(name)
- if r is not None:
- name = r.group(1)
-
- # replace . and _ with space
- name = name.replace(".", " ")
- name = name.replace("_", " ")
-
- name = name.strip()
- else:
- name = ""
-
- # fallback: package by hoster
- if not name:
- name = urlparse(file).hostname
- if name: name = name.replace("www.", "")
-
- # fallback : default name
- if not name:
- name = "unknown"
-
- # build mapping
- if name in packs:
- packs[name].append(url)
- else:
- packs[name] = [url]
-
- return packs
-
-
-if __name__ == "__main__":
- from os.path import join
- from pprint import pprint
-
- f = open(join("..", "..", "testlinks2.txt"), "rb")
- urls = [(x.strip(), x.strip()) for x in f.readlines() if x.strip()]
- f.close()
-
- print "Having %d urls." % len(urls)
-
- packs = parseNames(urls)
-
- pprint(packs)
-
- print "Got %d urls." % sum([len(x) for x in packs.itervalues()])
diff --git a/module/common/pylgettext.py b/module/common/pylgettext.py
deleted file mode 100644
index fb36fecee..000000000
--- a/module/common/pylgettext.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from gettext import *
-
-_searchdirs = None
-
-origfind = find
-
-def setpaths(pathlist):
- global _searchdirs
- if isinstance(pathlist, list):
- _searchdirs = pathlist
- else:
- _searchdirs = list(pathlist)
-
-
-def addpath(path):
- global _searchdirs
- if _searchdirs is None:
- _searchdirs = list(path)
- else:
- if path not in _searchdirs:
- _searchdirs.append(path)
-
-
-def delpath(path):
- global _searchdirs
- if _searchdirs is not None:
- if path in _searchdirs:
- _searchdirs.remove(path)
-
-
-def clearpath():
- global _searchdirs
- if _searchdirs is not None:
- _searchdirs = None
-
-
-def find(domain, localedir=None, languages=None, all=False):
- if _searchdirs is None:
- return origfind(domain, localedir, languages, all)
- searches = [localedir] + _searchdirs
- results = list()
- for dir in searches:
- res = origfind(domain, dir, languages, all)
- if all is False:
- results.append(res)
- else:
- results.extend(res)
- if all is False:
- results = filter(lambda x: x is not None, results)
- if len(results) == 0:
- return None
- else:
- return results[0]
- else:
- return results
-
-#Is there a smarter/cleaner pythonic way for this?
-translation.func_globals['find'] = find
diff --git a/module/config/default.conf b/module/config/default.conf
deleted file mode 100644
index 2e9152ba2..000000000
--- a/module/config/default.conf
+++ /dev/null
@@ -1,65 +0,0 @@
-version: 1
-
-remote - "Remote":
- int port : "Port" = 7227
- ip listenaddr : "Adress" = 0.0.0.0
- bool nolocalauth : "No authentication on local connections" = True
- bool activated : "Activated" = True
-ssl - "SSL":
- bool activated : "Activated"= False
- file cert : "SSL Certificate" = ssl.crt
- file key : "SSL Key" = ssl.key
-webinterface - "Webinterface":
- bool activated : "Activated" = True
- builtin;threaded;fastcgi;lightweight server : "Server" = builtin
- bool https : "Use HTTPS" = False
- ip host : "IP" = 0.0.0.0
- int port : "Port" = 8001
- str template : "Template" = default
- str prefix: "Path Prefix" =
-log - "Log":
- bool file_log : "File Log" = True
- folder log_folder : "Folder" = Logs
- int log_count : "Count" = 5
- int log_size : "Size in kb" = 100
- bool log_rotate : "Log Rotate" = True
-general - "General":
- en;de;fr;it;es;nl;sv;ru;pl;cs;sr;pt_BR language : "Language" = en
- folder download_folder : "Download Folder" = Downloads
- bool debug_mode : "Debug Mode" = False
- bool checksum : "Use Checksum" = False
- int min_free_space : "Min Free Space (MB)" = 200
- bool folder_per_package : "Create folder for each package" = True
- int renice : "CPU Priority" = 0
-download - "Download":
- int chunks : "Max connections for one download" = 3
- int max_downloads : "Max Parallel Downloads" = 3
- int max_speed : "Max Download Speed in kb/s" = -1
- bool limit_speed : "Limit Download Speed" = False
- str interface : "Download interface to bind (ip or Name)" = None
- bool ipv6 : "Allow IPv6" = False
- bool skip_existing : "Skip already existing files" = False
-permission - "Permissions":
- bool change_user : "Change user of running process" = False
- str user : "Username" = user
- str folder : "Folder Permission mode" = 0755
- bool change_file : "Change file mode of downloads" = False
- str file : "Filemode for Downloads" = 0644
- bool change_group : "Change group of running process" = False
- str group : "Groupname" = users
- bool change_dl : "Change Group and User of Downloads" = False
-reconnect - "Reconnect":
- bool activated : "Use Reconnect" = False
- str method : "Method" = None
- time startTime : "Start" = 0:00
- time endTime : "End" = 0:00
-downloadTime - "Download Time":
- time start : "Start" = 0:00
- time end : "End" = 0:00
-proxy - "Proxy":
- str address : "Address" = "localhost"
- int port : "Port" = 7070
- http;socks4;socks5 type : "Protocol" = http
- str username : "Username" = None
- password password : "Password" = None
- bool proxy : "Use Proxy" = False
diff --git a/module/config/gui_default.xml b/module/config/gui_default.xml
deleted file mode 100644
index 1faed776f..000000000
--- a/module/config/gui_default.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" ?>
-<root>
- <connections>
- <connection default="True" type="local" id="33965310e19b4a869112c43b39a16440">
- <name>Local</name>
- </connection>
- </connections>
- <mainWindow>
- <state></state>
- <geometry></geometry>
- </mainWindow>
- <language>en</language>
-</root>
diff --git a/module/database/DatabaseBackend.py b/module/database/DatabaseBackend.py
deleted file mode 100644
index 9530390c3..000000000
--- a/module/database/DatabaseBackend.py
+++ /dev/null
@@ -1,352 +0,0 @@
-#!/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
-"""
-from threading import Thread
-from threading import Event
-from os import remove
-from os.path import exists
-from shutil import move
-
-from Queue import Queue
-from traceback import print_exc
-
-from module.utils import chmod
-
-try:
- from pysqlite2 import dbapi2 as sqlite3
-except:
- import sqlite3
-
-DB_VERSION = 4
-
-class style():
- db = None
-
- @classmethod
- def setDB(cls, db):
- cls.db = db
-
- @classmethod
- def inner(cls, f):
- @staticmethod
- def x(*args, **kwargs):
- if cls.db:
- return f(cls.db, *args, **kwargs)
- return x
-
- @classmethod
- def queue(cls, f):
- @staticmethod
- def x(*args, **kwargs):
- if cls.db:
- return cls.db.queue(f, *args, **kwargs)
- return x
-
- @classmethod
- def async(cls, f):
- @staticmethod
- def x(*args, **kwargs):
- if cls.db:
- return cls.db.async(f, *args, **kwargs)
- return x
-
-class DatabaseJob():
- def __init__(self, f, *args, **kwargs):
- self.done = Event()
-
- self.f = f
- self.args = args
- self.kwargs = kwargs
-
- self.result = None
- self.exception = False
-
-# import inspect
-# self.frame = inspect.currentframe()
-
- def __repr__(self):
- from os.path import basename
- frame = self.frame.f_back
- output = ""
- for i in range(5):
- output += "\t%s:%s, %s\n" % (basename(frame.f_code.co_filename), frame.f_lineno, frame.f_code.co_name)
- frame = frame.f_back
- del frame
- del self.frame
-
- return "DataBase Job %s:%s\n%sResult: %s" % (self.f.__name__, self.args[1:], output, self.result)
-
- def processJob(self):
- try:
- self.result = self.f(*self.args, **self.kwargs)
- except Exception, e:
- print_exc()
- try:
- print "Database Error @", self.f.__name__, self.args[1:], self.kwargs, e
- except:
- pass
-
- self.exception = e
- finally:
- self.done.set()
-
- def wait(self):
- self.done.wait()
-
-class DatabaseBackend(Thread):
- subs = []
- def __init__(self, core):
- Thread.__init__(self)
- self.setDaemon(True)
- self.core = core
-
- self.jobs = Queue()
-
- self.setuplock = Event()
-
- style.setDB(self)
-
- def setup(self):
- self.start()
- self.setuplock.wait()
-
- def run(self):
- """main loop, which executes commands"""
- convert = self._checkVersion() #returns None or current version
-
- self.conn = sqlite3.connect("files.db")
- chmod("files.db", 0600)
-
- self.c = self.conn.cursor() #compatibility
-
- if convert is not None:
- self._convertDB(convert)
-
- self._createTables()
- self._migrateUser()
-
- self.conn.commit()
-
- self.setuplock.set()
-
- while True:
- j = self.jobs.get()
- if j == "quit":
- self.c.close()
- self.conn.close()
- break
- j.processJob()
-
- @style.queue
- def shutdown(self):
- self.conn.commit()
- self.jobs.put("quit")
-
- def _checkVersion(self):
- """ check db version and delete it if needed"""
- if not exists("files.version"):
- f = open("files.version", "wb")
- f.write(str(DB_VERSION))
- f.close()
- return
-
- f = open("files.version", "rb")
- v = int(f.read().strip())
- f.close()
- if v < DB_VERSION:
- if v < 2:
- try:
- self.manager.core.log.warning(_("Filedatabase was deleted due to incompatible version."))
- except:
- print "Filedatabase was deleted due to incompatible version."
- remove("files.version")
- move("files.db", "files.backup.db")
- f = open("files.version", "wb")
- f.write(str(DB_VERSION))
- f.close()
- return v
-
- def _convertDB(self, v):
- try:
- getattr(self, "_convertV%i" % v)()
- except:
- try:
- self.core.log.error(_("Filedatabase could NOT be converted."))
- except:
- print "Filedatabase could NOT be converted."
-
- #--convert scripts start
-
- def _convertV2(self):
- self.c.execute('CREATE TABLE IF NOT EXISTS "storage" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "identifier" TEXT NOT NULL, "key" TEXT NOT NULL, "value" TEXT DEFAULT "")')
- try:
- self.manager.core.log.info(_("Database was converted from v2 to v3."))
- except:
- print "Database was converted from v2 to v3."
- self._convertV3()
-
- def _convertV3(self):
- self.c.execute('CREATE TABLE IF NOT EXISTS "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "name" TEXT NOT NULL, "email" TEXT DEFAULT "" NOT NULL, "password" TEXT NOT NULL, "role" INTEGER DEFAULT 0 NOT NULL, "permission" INTEGER DEFAULT 0 NOT NULL, "template" TEXT DEFAULT "default" NOT NULL)')
- try:
- self.manager.core.log.info(_("Database was converted from v3 to v4."))
- except:
- print "Database was converted from v3 to v4."
-
- #--convert scripts end
-
- def _createTables(self):
- """create tables for database"""
-
- self.c.execute('CREATE TABLE IF NOT EXISTS "packages" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "name" TEXT NOT NULL, "folder" TEXT, "password" TEXT DEFAULT "", "site" TEXT DEFAULT "", "queue" INTEGER DEFAULT 0 NOT NULL, "packageorder" INTEGER DEFAULT 0 NOT NULL)')
- self.c.execute('CREATE TABLE IF NOT EXISTS "links" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "url" TEXT NOT NULL, "name" TEXT, "size" INTEGER DEFAULT 0 NOT NULL, "status" INTEGER DEFAULT 3 NOT NULL, "plugin" TEXT DEFAULT "BasePlugin" NOT NULL, "error" TEXT DEFAULT "", "linkorder" INTEGER DEFAULT 0 NOT NULL, "package" INTEGER DEFAULT 0 NOT NULL, FOREIGN KEY(package) REFERENCES packages(id))')
- self.c.execute('CREATE INDEX IF NOT EXISTS "pIdIndex" ON links(package)')
- self.c.execute('CREATE TABLE IF NOT EXISTS "storage" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "identifier" TEXT NOT NULL, "key" TEXT NOT NULL, "value" TEXT DEFAULT "")')
- self.c.execute('CREATE TABLE IF NOT EXISTS "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT, "name" TEXT NOT NULL, "email" TEXT DEFAULT "" NOT NULL, "password" TEXT NOT NULL, "role" INTEGER DEFAULT 0 NOT NULL, "permission" INTEGER DEFAULT 0 NOT NULL, "template" TEXT DEFAULT "default" NOT NULL)')
-
- self.c.execute('CREATE VIEW IF NOT EXISTS "pstats" AS \
- SELECT p.id AS id, SUM(l.size) AS sizetotal, COUNT(l.id) AS linkstotal, linksdone, sizedone\
- FROM packages p JOIN links l ON p.id = l.package LEFT OUTER JOIN\
- (SELECT p.id AS id, COUNT(*) AS linksdone, SUM(l.size) AS sizedone \
- FROM packages p JOIN links l ON p.id = l.package AND l.status in (0,4,13) GROUP BY p.id) s ON s.id = p.id \
- GROUP BY p.id')
-
- #try to lower ids
- self.c.execute('SELECT max(id) FROM LINKS')
- fid = self.c.fetchone()[0]
- if fid:
- fid = int(fid)
- else:
- fid = 0
- self.c.execute('UPDATE SQLITE_SEQUENCE SET seq=? WHERE name=?', (fid, "links"))
-
-
- self.c.execute('SELECT max(id) FROM packages')
- pid = self.c.fetchone()[0]
- if pid:
- pid = int(pid)
- else:
- pid = 0
- self.c.execute('UPDATE SQLITE_SEQUENCE SET seq=? WHERE name=?', (pid, "packages"))
-
- self.c.execute('VACUUM')
-
-
- def _migrateUser(self):
- if exists("pyload.db"):
- try:
- self.core.log.info(_("Converting old Django DB"))
- except:
- print "Converting old Django DB"
- conn = sqlite3.connect('pyload.db')
- c = conn.cursor()
- c.execute("SELECT username, password, email from auth_user WHERE is_superuser")
- users = []
- for r in c:
- pw = r[1].split("$")
- users.append((r[0], pw[1] + pw[2], r[2]))
- c.close()
- conn.close()
-
- self.c.executemany("INSERT INTO users(name, password, email) VALUES (?, ?, ?)", users)
- move("pyload.db", "pyload.old.db")
-
- def createCursor(self):
- return self.conn.cursor()
-
- @style.async
- def commit(self):
- self.conn.commit()
-
- @style.queue
- def syncSave(self):
- self.conn.commit()
-
- @style.async
- def rollback(self):
- self.conn.rollback()
-
- def async(self, f, *args, **kwargs):
- args = (self, ) + args
- job = DatabaseJob(f, *args, **kwargs)
- self.jobs.put(job)
-
- def queue(self, f, *args, **kwargs):
- args = (self, ) + args
- job = DatabaseJob(f, *args, **kwargs)
- self.jobs.put(job)
- job.wait()
- return job.result
-
- @classmethod
- def registerSub(cls, klass):
- cls.subs.append(klass)
-
- @classmethod
- def unregisterSub(cls, klass):
- cls.subs.remove(klass)
-
- def __getattr__(self, attr):
- for sub in DatabaseBackend.subs:
- if hasattr(sub, attr):
- return getattr(sub, attr)
-
-if __name__ == "__main__":
- db = DatabaseBackend()
- db.setup()
-
- class Test():
- @style.queue
- def insert(db):
- c = db.createCursor()
- for i in range(1000):
- c.execute("INSERT INTO storage (identifier, key, value) VALUES (?, ?, ?)", ("foo", i, "bar"))
- @style.async
- def insert2(db):
- c = db.createCursor()
- for i in range(1000*1000):
- c.execute("INSERT INTO storage (identifier, key, value) VALUES (?, ?, ?)", ("foo", i, "bar"))
-
- @style.queue
- def select(db):
- c = db.createCursor()
- for i in range(10):
- res = c.execute("SELECT value FROM storage WHERE identifier=? AND key=?", ("foo", i))
- print res.fetchone()
-
- @style.queue
- def error(db):
- c = db.createCursor()
- print "a"
- c.execute("SELECT myerror FROM storage WHERE identifier=? AND key=?", ("foo", i))
- print "e"
-
- db.registerSub(Test)
- from time import time
- start = time()
- for i in range(100):
- db.insert()
- end = time()
- print end-start
-
- start = time()
- db.insert2()
- end = time()
- print end-start
-
- db.error()
-
diff --git a/module/database/FileDatabase.py b/module/database/FileDatabase.py
deleted file mode 100644
index 7e7efb028..000000000
--- a/module/database/FileDatabase.py
+++ /dev/null
@@ -1,944 +0,0 @@
-#!/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
-"""
-
-
-from threading import RLock
-from time import time
-
-from module.utils import formatSize, lock
-from module.PullEvents import InsertEvent, ReloadAllEvent, RemoveEvent, UpdateEvent
-from module.PyPackage import PyPackage
-from module.PyFile import PyFile
-from module.database import style, DatabaseBackend
-
-try:
- from pysqlite2 import dbapi2 as sqlite3
-except:
- import sqlite3
-
-
-class FileHandler:
- """Handles all request made to obtain information,
- modify status or other request for links or packages"""
-
- def __init__(self, core):
- """Constructor"""
- self.core = core
-
- # translations
- self.statusMsg = [_("finished"), _("offline"), _("online"), _("queued"), _("skipped"), _("waiting"), _("temp. offline"), _("starting"), _("failed"), _("aborted"), _("decrypting"), _("custom"), _("downloading"), _("processing"), _("unknown")]
-
- self.cache = {} #holds instances for files
- self.packageCache = {} # same for packages
- #@TODO: purge the cache
-
- self.jobCache = {}
-
- self.lock = RLock() #@TODO should be a Lock w/o R
- #self.lock._Verbose__verbose = True
-
- self.filecount = -1 # if an invalid value is set get current value from db
- self.queuecount = -1 #number of package to be loaded
- self.unchanged = False #determines if any changes was made since last call
-
- self.db = self.core.db
-
- def change(func):
- def new(*args):
- args[0].unchanged = False
- args[0].filecount = -1
- args[0].queuecount = -1
- args[0].jobCache = {}
- return func(*args)
- return new
-
- #----------------------------------------------------------------------
- def save(self):
- """saves all data to backend"""
- self.db.commit()
-
- #----------------------------------------------------------------------
- def syncSave(self):
- """saves all data to backend and waits until all data are written"""
- pyfiles = self.cache.values()
- for pyfile in pyfiles:
- pyfile.sync()
-
- pypacks = self.packageCache.values()
- for pypack in pypacks:
- pypack.sync()
-
- self.db.syncSave()
-
- @lock
- def getCompleteData(self, queue=1):
- """gets a complete data representation"""
-
- data = self.db.getAllLinks(queue)
- packs = self.db.getAllPackages(queue)
-
- data.update([(x.id, x.toDbDict()[x.id]) for x in self.cache.values()])
-
- for x in self.packageCache.itervalues():
- if x.queue != queue or x.id not in packs: continue
- packs[x.id].update(x.toDict()[x.id])
-
- for key, value in data.iteritems():
- if value["package"] in packs:
- packs[value["package"]]["links"][key] = value
-
- return packs
-
- @lock
- def getInfoData(self, queue=1):
- """gets a data representation without links"""
-
- packs = self.db.getAllPackages(queue)
- for x in self.packageCache.itervalues():
- if x.queue != queue or x.id not in packs: continue
- packs[x.id].update(x.toDict()[x.id])
-
- return packs
-
- @lock
- @change
- def addLinks(self, urls, package):
- """adds links"""
-
- self.core.hookManager.dispatchEvent("linksAdded", urls, package)
-
- data = self.core.pluginManager.parseUrls(urls)
-
- self.db.addLinks(data, package)
- self.core.threadManager.createInfoThread(data, package)
-
- #@TODO change from reloadAll event to package update event
- self.core.pullManager.addEvent(ReloadAllEvent("collector"))
-
- #----------------------------------------------------------------------
- @lock
- @change
- def addPackage(self, name, folder, queue=0):
- """adds a package, default to link collector"""
- lastID = self.db.addPackage(name, folder, queue)
- p = self.db.getPackage(lastID)
- e = InsertEvent("pack", lastID, p.order, "collector" if not queue else "queue")
- self.core.pullManager.addEvent(e)
- return lastID
-
- #----------------------------------------------------------------------
- @lock
- @change
- def deletePackage(self, id):
- """delete package and all contained links"""
-
- p = self.getPackage(id)
- if not p:
- if id in self.packageCache: del self.packageCache[id]
- return
-
- oldorder = p.order
- queue = p.queue
-
- e = RemoveEvent("pack", id, "collector" if not p.queue else "queue")
-
- pyfiles = self.cache.values()
-
- for pyfile in pyfiles:
- if pyfile.packageid == id:
- pyfile.abortDownload()
- pyfile.release()
-
- self.db.deletePackage(p)
- self.core.pullManager.addEvent(e)
- self.core.hookManager.dispatchEvent("packageDeleted", id)
-
- if id in self.packageCache:
- del self.packageCache[id]
-
- packs = self.packageCache.values()
- for pack in packs:
- if pack.queue == queue and pack.order > oldorder:
- pack.order -= 1
- pack.notifyChange()
-
- #----------------------------------------------------------------------
- @lock
- @change
- def deleteLink(self, id):
- """deletes links"""
-
- f = self.getFile(id)
- if not f:
- return None
-
- pid = f.packageid
- e = RemoveEvent("file", id, "collector" if not f.package().queue else "queue")
-
- oldorder = f.order
-
- if id in self.core.threadManager.processingIds():
- self.cache[id].abortDownload()
-
- if id in self.cache:
- del self.cache[id]
-
- self.db.deleteLink(f)
-
- self.core.pullManager.addEvent(e)
-
- p = self.getPackage(pid)
- if not len(p.getChildren()):
- p.delete()
-
- pyfiles = self.cache.values()
- for pyfile in pyfiles:
- if pyfile.packageid == pid and pyfile.order > oldorder:
- pyfile.order -= 1
- pyfile.notifyChange()
-
- #----------------------------------------------------------------------
- def releaseLink(self, id):
- """removes pyfile from cache"""
- if id in self.cache:
- del self.cache[id]
-
- #----------------------------------------------------------------------
- def releasePackage(self, id):
- """removes package from cache"""
- if id in self.packageCache:
- del self.packageCache[id]
-
- #----------------------------------------------------------------------
- def updateLink(self, pyfile):
- """updates link"""
- self.db.updateLink(pyfile)
-
- e = UpdateEvent("file", pyfile.id, "collector" if not pyfile.package().queue else "queue")
- self.core.pullManager.addEvent(e)
-
- #----------------------------------------------------------------------
- def updatePackage(self, pypack):
- """updates a package"""
- self.db.updatePackage(pypack)
-
- e = UpdateEvent("pack", pypack.id, "collector" if not pypack.queue else "queue")
- self.core.pullManager.addEvent(e)
-
- #----------------------------------------------------------------------
- def getPackage(self, id):
- """return package instance"""
-
- if id in self.packageCache:
- return self.packageCache[id]
- else:
- return self.db.getPackage(id)
-
- #----------------------------------------------------------------------
- def getPackageData(self, id):
- """returns dict with package information"""
- pack = self.getPackage(id)
-
- if not pack:
- return None
-
- pack = pack.toDict()[id]
-
- data = self.db.getPackageData(id)
-
- tmplist = []
-
- cache = self.cache.values()
- for x in cache:
- if int(x.toDbDict()[x.id]["package"]) == int(id):
- tmplist.append((x.id, x.toDbDict()[x.id]))
- data.update(tmplist)
-
- pack["links"] = data
-
- return pack
-
- #----------------------------------------------------------------------
- def getFileData(self, id):
- """returns dict with file information"""
- if id in self.cache:
- return self.cache[id].toDbDict()
-
- return self.db.getLinkData(id)
-
- #----------------------------------------------------------------------
- def getFile(self, id):
- """returns pyfile instance"""
- if id in self.cache:
- return self.cache[id]
- else:
- return self.db.getFile(id)
-
- #----------------------------------------------------------------------
- @lock
- def getJob(self, occ):
- """get suitable job"""
-
- #@TODO clean mess
- #@TODO improve selection of valid jobs
-
- if occ in self.jobCache:
- if self.jobCache[occ]:
- id = self.jobCache[occ].pop()
- if id == "empty":
- pyfile = None
- self.jobCache[occ].append("empty")
- else:
- pyfile = self.getFile(id)
- else:
- jobs = self.db.getJob(occ)
- jobs.reverse()
- if not jobs:
- self.jobCache[occ].append("empty")
- pyfile = None
- else:
- self.jobCache[occ].extend(jobs)
- pyfile = self.getFile(self.jobCache[occ].pop())
-
- else:
- self.jobCache = {} #better not caching to much
- jobs = self.db.getJob(occ)
- jobs.reverse()
- self.jobCache[occ] = jobs
-
- if not jobs:
- self.jobCache[occ].append("empty")
- pyfile = None
- else:
- pyfile = self.getFile(self.jobCache[occ].pop())
-
- #@TODO: maybe the new job has to be approved...
-
-
- #pyfile = self.getFile(self.jobCache[occ].pop())
- return pyfile
-
- @lock
- def getDecryptJob(self):
- """return job for decrypting"""
- if "decrypt" in self.jobCache:
- return None
-
- plugins = self.core.pluginManager.crypterPlugins.keys() + self.core.pluginManager.containerPlugins.keys()
- plugins = str(tuple(plugins))
-
- jobs = self.db.getPluginJob(plugins)
- if jobs:
- return self.getFile(jobs[0])
- else:
- self.jobCache["decrypt"] = "empty"
- return None
-
- def getFileCount(self):
- """returns number of files"""
-
- if self.filecount == -1:
- self.filecount = self.db.filecount(1)
-
- return self.filecount
-
- def getQueueCount(self, force=False):
- """number of files that have to be processed"""
- if self.queuecount == -1 or force:
- self.queuecount = self.db.queuecount(1)
-
- return self.queuecount
-
- def checkAllLinksFinished(self):
- """checks if all files are finished and dispatch event"""
-
- if not self.getQueueCount(True):
- self.core.hookManager.dispatchEvent("allDownloadsFinished")
- self.core.log.debug("All downloads finished")
- return True
-
- return False
-
- def checkAllLinksProcessed(self, fid):
- """checks if all files was processed and pyload would idle now, needs fid which will be ignored when counting"""
-
- # reset count so statistic will update (this is called when dl was processed)
- self.resetCount()
-
- if not self.db.processcount(1, fid):
- self.core.hookManager.dispatchEvent("allDownloadsProcessed")
- self.core.log.debug("All downloads processed")
- return True
-
- return False
-
- def resetCount(self):
- self.queuecount = -1
-
- @lock
- @change
- def restartPackage(self, id):
- """restart package"""
- pyfiles = self.cache.values()
- for pyfile in pyfiles:
- if pyfile.packageid == id:
- self.restartFile(pyfile.id)
-
- self.db.restartPackage(id)
-
- if id in self.packageCache:
- self.packageCache[id].setFinished = False
-
- e = UpdateEvent("pack", id, "collector" if not self.getPackage(id).queue else "queue")
- self.core.pullManager.addEvent(e)
-
- @lock
- @change
- def restartFile(self, id):
- """ restart file"""
- if id in self.cache:
- self.cache[id].status = 3
- self.cache[id].name = self.cache[id].url
- self.cache[id].error = ""
- self.cache[id].abortDownload()
-
-
- self.db.restartFile(id)
-
- e = UpdateEvent("file", id, "collector" if not self.getFile(id).package().queue else "queue")
- self.core.pullManager.addEvent(e)
-
- @lock
- @change
- def setPackageLocation(self, id, queue):
- """push package to queue"""
-
- p = self.db.getPackage(id)
- oldorder = p.order
-
- e = RemoveEvent("pack", id, "collector" if not p.queue else "queue")
- self.core.pullManager.addEvent(e)
-
- self.db.clearPackageOrder(p)
-
- p = self.db.getPackage(id)
-
- p.queue = queue
- self.db.updatePackage(p)
-
- self.db.reorderPackage(p, -1, True)
-
- packs = self.packageCache.values()
- for pack in packs:
- if pack.queue != queue and pack.order > oldorder:
- pack.order -= 1
- pack.notifyChange()
-
- self.db.commit()
- self.releasePackage(id)
- p = self.getPackage(id)
-
- e = InsertEvent("pack", id, p.order, "collector" if not p.queue else "queue")
- self.core.pullManager.addEvent(e)
-
- @lock
- @change
- def reorderPackage(self, id, position):
- p = self.getPackage(id)
-
- e = RemoveEvent("pack", id, "collector" if not p.queue else "queue")
- self.core.pullManager.addEvent(e)
- self.db.reorderPackage(p, position)
-
- packs = self.packageCache.values()
- for pack in packs:
- if pack.queue != p.queue or pack.order < 0 or pack == p: continue
- if p.order > position:
- if pack.order >= position and pack.order < p.order:
- pack.order += 1
- pack.notifyChange()
- elif p.order < position:
- if pack.order <= position and pack.order > p.order:
- pack.order -= 1
- pack.notifyChange()
-
- p.order = position
- self.db.commit()
-
- e = InsertEvent("pack", id, position, "collector" if not p.queue else "queue")
- self.core.pullManager.addEvent(e)
-
- @lock
- @change
- def reorderFile(self, id, position):
- f = self.getFileData(id)
- f = f[id]
-
- e = RemoveEvent("file", id, "collector" if not self.getPackage(f["package"]).queue else "queue")
- self.core.pullManager.addEvent(e)
-
- self.db.reorderLink(f, position)
-
- pyfiles = self.cache.values()
- for pyfile in pyfiles:
- if pyfile.packageid != f["package"] or pyfile.order < 0: continue
- if f["order"] > position:
- if pyfile.order >= position and pyfile.order < f["order"]:
- pyfile.order += 1
- pyfile.notifyChange()
- elif f["order"] < position:
- if pyfile.order <= position and pyfile.order > f["order"]:
- pyfile.order -= 1
- pyfile.notifyChange()
-
- if id in self.cache:
- self.cache[id].order = position
-
- self.db.commit()
-
- e = InsertEvent("file", id, position, "collector" if not self.getPackage(f["package"]).queue else "queue")
- self.core.pullManager.addEvent(e)
-
- @change
- def updateFileInfo(self, data, pid):
- """ updates file info (name, size, status, url)"""
- ids = self.db.updateLinkInfo(data)
- e = UpdateEvent("pack", pid, "collector" if not self.getPackage(pid).queue else "queue")
- self.core.pullManager.addEvent(e)
-
- def checkPackageFinished(self, pyfile):
- """ checks if package is finished and calls hookmanager """
-
- ids = self.db.getUnfinished(pyfile.packageid)
- if not ids or (pyfile.id in ids and len(ids) == 1):
- if not pyfile.package().setFinished:
- self.core.log.info(_("Package finished: %s") % pyfile.package().name)
- self.core.hookManager.packageFinished(pyfile.package())
- pyfile.package().setFinished = True
-
-
- def reCheckPackage(self, pid):
- """ recheck links in package """
- data = self.db.getPackageData(pid)
-
- urls = []
-
- for pyfile in data.itervalues():
- if pyfile["status"] not in (0, 12, 13):
- urls.append((pyfile["url"], pyfile["plugin"]))
-
- self.core.threadManager.createInfoThread(urls, pid)
-
- @lock
- @change
- def deleteFinishedLinks(self):
- """ deletes finished links and packages, return deleted packages """
-
- old_packs = self.getInfoData(0)
- old_packs.update(self.getInfoData(1))
-
- self.db.deleteFinished()
-
- new_packs = self.db.getAllPackages(0)
- new_packs.update(self.db.getAllPackages(1))
- #get new packages only from db
-
- deleted = []
- for id in old_packs.iterkeys():
- if id not in new_packs:
- deleted.append(id)
- self.deletePackage(int(id))
-
- return deleted
-
- @lock
- @change
- def restartFailed(self):
- """ restart all failed links """
- self.db.restartFailed()
-
-class FileMethods():
- @style.queue
- def filecount(self, queue):
- """returns number of files in queue"""
- self.c.execute("SELECT COUNT(*) FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=?", (queue, ))
- return self.c.fetchone()[0]
-
- @style.queue
- def queuecount(self, queue):
- """ number of files in queue not finished yet"""
- self.c.execute("SELECT COUNT(*) FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=? AND l.status NOT IN (0,4)", (queue, ))
- return self.c.fetchone()[0]
-
- @style.queue
- def processcount(self, queue, fid):
- """ number of files which have to be proccessed """
- self.c.execute("SELECT COUNT(*) FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=? AND l.status IN (2,3,5,7,12) AND l.id != ?", (queue, str(fid)))
- return self.c.fetchone()[0]
-
- @style.inner
- def _nextPackageOrder(self, queue=0):
- self.c.execute('SELECT MAX(packageorder) FROM packages WHERE queue=?', (queue,))
- max = self.c.fetchone()[0]
- if max is not None:
- return max + 1
- else:
- return 0
-
- @style.inner
- def _nextFileOrder(self, package):
- self.c.execute('SELECT MAX(linkorder) FROM links WHERE package=?', (package,))
- max = self.c.fetchone()[0]
- if max is not None:
- return max + 1
- else:
- return 0
-
- @style.queue
- def addLink(self, url, name, plugin, package):
- order = self._nextFileOrder(package)
- self.c.execute('INSERT INTO links(url, name, plugin, package, linkorder) VALUES(?,?,?,?,?)', (url, name, plugin, package, order))
- return self.c.lastrowid
-
- @style.queue
- def addLinks(self, links, package):
- """ links is a list of tupels (url,plugin)"""
- order = self._nextFileOrder(package)
- orders = [order + x for x in range(len(links))]
- links = [(x[0], x[0], x[1], package, o) for x, o in zip(links, orders)]
- self.c.executemany('INSERT INTO links(url, name, plugin, package, linkorder) VALUES(?,?,?,?,?)', links)
-
- @style.queue
- def addPackage(self, name, folder, queue):
- order = self._nextPackageOrder(queue)
- self.c.execute('INSERT INTO packages(name, folder, queue, packageorder) VALUES(?,?,?,?)', (name, folder, queue, order))
- return self.c.lastrowid
-
- @style.queue
- def deletePackage(self, p):
-
- self.c.execute('DELETE FROM links WHERE package=?', (str(p.id),))
- self.c.execute('DELETE FROM packages WHERE id=?', (str(p.id),))
- self.c.execute('UPDATE packages SET packageorder=packageorder-1 WHERE packageorder > ? AND queue=?', (p.order, p.queue))
-
- @style.queue
- def deleteLink(self, f):
-
- self.c.execute('DELETE FROM links WHERE id=?', (str(f.id),))
- self.c.execute('UPDATE links SET linkorder=linkorder-1 WHERE linkorder > ? AND package=?', (f.order, str(f.packageid)))
-
-
- @style.queue
- def getAllLinks(self, q):
- """return information about all links in queue q
-
- q0 queue
- q1 collector
-
- format:
-
- {
- id: {'name': name, ... 'package': id }, ...
- }
-
- """
- self.c.execute('SELECT l.id,l.url,l.name,l.size,l.status,l.error,l.plugin,l.package,l.linkorder FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE p.queue=? ORDER BY l.linkorder', (q,))
- data = {}
- for r in self.c:
- data[r[0]] = {
- 'id': r[0],
- 'url': r[1],
- 'name': r[2],
- 'size': r[3],
- 'format_size': formatSize(r[3]),
- 'status': r[4],
- 'statusmsg': self.manager.statusMsg[r[4]],
- 'error': r[5],
- 'plugin': r[6],
- 'package': r[7],
- 'order': r[8],
- }
-
- return data
-
- @style.queue
- def getAllPackages(self, q):
- """return information about packages in queue q
- (only useful in get all data)
-
- q0 queue
- q1 collector
-
- format:
-
- {
- id: {'name': name ... 'links': {} }, ...
- }
- """
- self.c.execute('SELECT p.id, p.name, p.folder, p.site, p.password, p.queue, p.packageorder, s.sizetotal, s.sizedone, s.linksdone, s.linkstotal \
- FROM packages p JOIN pstats s ON p.id = s.id \
- WHERE p.queue=? ORDER BY p.packageorder', str(q))
-
- data = {}
- for r in self.c:
- data[r[0]] = {
- 'id': r[0],
- 'name': r[1],
- 'folder': r[2],
- 'site': r[3],
- 'password': r[4],
- 'queue': r[5],
- 'order': r[6],
- 'sizetotal': int(r[7]),
- 'sizedone': r[8] if r[8] else 0, #these can be None
- 'linksdone': r[9] if r[9] else 0,
- 'linkstotal': r[10],
- 'links': {}
- }
-
- return data
-
- @style.queue
- def getLinkData(self, id):
- """get link information as dict"""
- self.c.execute('SELECT id,url,name,size,status,error,plugin,package,linkorder FROM links WHERE id=?', (str(id), ))
- data = {}
- r = self.c.fetchone()
- if not r:
- return None
- data[r[0]] = {
- 'id': r[0],
- 'url': r[1],
- 'name': r[2],
- 'size': r[3],
- 'format_size': formatSize(r[3]),
- 'status': r[4],
- 'statusmsg': self.manager.statusMsg[r[4]],
- 'error': r[5],
- 'plugin': r[6],
- 'package': r[7],
- 'order': r[8],
- }
-
- return data
-
- @style.queue
- def getPackageData(self, id):
- """get data about links for a package"""
- self.c.execute('SELECT id,url,name,size,status,error,plugin,package,linkorder FROM links WHERE package=? ORDER BY linkorder', (str(id), ))
-
- data = {}
- for r in self.c:
- data[r[0]] = {
- 'id': r[0],
- 'url': r[1],
- 'name': r[2],
- 'size': r[3],
- 'format_size': formatSize(r[3]),
- 'status': r[4],
- 'statusmsg': self.manager.statusMsg[r[4]],
- 'error': r[5],
- 'plugin': r[6],
- 'package': r[7],
- 'order': r[8],
- }
-
- return data
-
-
- @style.async
- def updateLink(self, f):
- self.c.execute('UPDATE links SET url=?,name=?,size=?,status=?,error=?,package=? WHERE id=?', (f.url, f.name, f.size, f.status, f.error, str(f.packageid), str(f.id)))
-
- @style.queue
- def updatePackage(self, p):
- self.c.execute('UPDATE packages SET name=?,folder=?,site=?,password=?,queue=? WHERE id=?', (p.name, p.folder, p.site, p.password, p.queue, str(p.id)))
-
- @style.queue
- def updateLinkInfo(self, data):
- """ data is list of tupels (name, size, status, url) """
- self.c.executemany('UPDATE links SET name=?, size=?, status=? WHERE url=? AND status IN (1,2,3,14)', data)
- ids = []
- self.c.execute('SELECT id FROM links WHERE url IN (\'%s\')' % "','".join([x[3] for x in data]))
- for r in self.c:
- ids.append(int(r[0]))
- return ids
-
- @style.queue
- def reorderPackage(self, p, position, noMove=False):
- if position == -1:
- position = self._nextPackageOrder(p.queue)
- if not noMove:
- if p.order > position:
- self.c.execute('UPDATE packages SET packageorder=packageorder+1 WHERE packageorder >= ? AND packageorder < ? AND queue=? AND packageorder >= 0', (position, p.order, p.queue))
- elif p.order < position:
- self.c.execute('UPDATE packages SET packageorder=packageorder-1 WHERE packageorder <= ? AND packageorder > ? AND queue=? AND packageorder >= 0', (position, p.order, p.queue))
-
- self.c.execute('UPDATE packages SET packageorder=? WHERE id=?', (position, str(p.id)))
-
- @style.queue
- def reorderLink(self, f, position):
- """ reorder link with f as dict for pyfile """
- if f["order"] > position:
- self.c.execute('UPDATE links SET linkorder=linkorder+1 WHERE linkorder >= ? AND linkorder < ? AND package=?', (position, f["order"], f["package"]))
- elif f["order"] < position:
- self.c.execute('UPDATE links SET linkorder=linkorder-1 WHERE linkorder <= ? AND linkorder > ? AND package=?', (position, f["order"], f["package"]))
-
- self.c.execute('UPDATE links SET linkorder=? WHERE id=?', (position, f["id"]))
-
-
- @style.queue
- def clearPackageOrder(self, p):
- self.c.execute('UPDATE packages SET packageorder=? WHERE id=?', (-1, str(p.id)))
- self.c.execute('UPDATE packages SET packageorder=packageorder-1 WHERE packageorder > ? AND queue=? AND id != ?', (p.order, p.queue, str(p.id)))
-
- @style.async
- def restartFile(self, id):
- self.c.execute('UPDATE links SET status=3,error="" WHERE id=?', (str(id),))
-
- @style.async
- def restartPackage(self, id):
- self.c.execute('UPDATE links SET status=3 WHERE package=?', (str(id),))
-
- @style.queue
- def getPackage(self, id):
- """return package instance from id"""
- self.c.execute("SELECT name,folder,site,password,queue,packageorder FROM packages WHERE id=?", (str(id), ))
- r = self.c.fetchone()
- if not r: return None
- return PyPackage(self.manager, id, * r)
-
- #----------------------------------------------------------------------
- @style.queue
- def getFile(self, id):
- """return link instance from id"""
- self.c.execute("SELECT url, name, size, status, error, plugin, package, linkorder FROM links WHERE id=?", (str(id), ))
- r = self.c.fetchone()
- if not r: return None
- return PyFile(self.manager, id, * r)
-
-
- @style.queue
- def getJob(self, occ):
- """return pyfile ids, which are suitable for download and dont use a occupied plugin"""
-
- #@TODO improve this hardcoded method
- pre = "('DLC', 'LinkList', 'SerienjunkiesOrg', 'CCF', 'RSDF')" #plugins which are processed in collector
-
- cmd = "("
- for i, item in enumerate(occ):
- if i: cmd += ", "
- cmd += "'%s'" % item
-
- cmd += ")"
-
- cmd = "SELECT l.id FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE ((p.queue=1 AND l.plugin NOT IN %s) OR l.plugin IN %s) AND l.status IN (2,3,14) ORDER BY p.packageorder ASC, l.linkorder ASC LIMIT 5" % (cmd, pre)
-
- self.c.execute(cmd) # very bad!
-
- return [x[0] for x in self.c]
-
- @style.queue
- def getPluginJob(self, plugins):
- """returns pyfile ids with suited plugins"""
- cmd = "SELECT l.id FROM links as l INNER JOIN packages as p ON l.package=p.id WHERE l.plugin IN %s AND l.status IN (2,3,14) ORDER BY p.packageorder ASC, l.linkorder ASC LIMIT 5" % plugins
-
- self.c.execute(cmd) # very bad!
-
- return [x[0] for x in self.c]
-
- @style.queue
- def getUnfinished(self, pid):
- """return list of max length 3 ids with pyfiles in package not finished or processed"""
-
- self.c.execute("SELECT id FROM links WHERE package=? AND status NOT IN (0, 4, 13) LIMIT 3", (str(pid),))
- return [r[0] for r in self.c]
-
- @style.queue
- def deleteFinished(self):
- self.c.execute("DELETE FROM links WHERE status IN (0,4)")
- self.c.execute("DELETE FROM packages WHERE NOT EXISTS(SELECT 1 FROM links WHERE packages.id=links.package)")
-
- @style.queue
- def restartFailed(self):
- self.c.execute("UPDATE links SET status=3,error='' WHERE status IN (6, 8, 9)")
-
- @style.queue
- def findDuplicates(self, id, folder, filename):
- """ checks if filename exists with different id and same package """
- self.c.execute("SELECT l.plugin FROM links as l INNER JOIN packages as p ON l.package=p.id AND p.folder=? WHERE l.id!=? AND l.status=0 AND l.name=?", (folder, id, filename))
- return self.c.fetchone()
-
- @style.queue
- def purgeLinks(self):
- self.c.execute("DELETE FROM links;")
- self.c.execute("DELETE FROM packages;")
-
-DatabaseBackend.registerSub(FileMethods)
-
-if __name__ == "__main__":
-
- pypath = "."
- _ = lambda x: x
-
- db = FileHandler(None)
-
- #p = PyFile(db, 5)
- #sleep(0.1)
-
- a = time()
-
- #print db.addPackage("package", "folder" , 1)
-
- pack = db.db.addPackage("package", "folder", 1)
-
- updates = []
-
-
- for x in range(0, 200):
- x = str(x)
- db.db.addLink("http://somehost.com/hoster/file/download?file_id=" + x, x, "BasePlugin", pack)
- updates.append(("new name" + x, 0, 3, "http://somehost.com/hoster/file/download?file_id=" + x))
-
-
- for x in range(0, 100):
- updates.append(("unimportant%s" % x, 0, 3, "a really long non existent url%s" % x))
-
- db.db.commit()
-
- b = time()
- print "adding 200 links, single sql execs, no commit", b-a
-
- print db.getCompleteData(1)
-
- c = time()
-
-
- db.db.updateLinkInfo(updates)
-
- d = time()
-
- print "updates", d-c
-
- print db.getCompleteData(1)
-
-
- e = time()
-
- print "complete data", e-d
diff --git a/module/database/StorageDatabase.py b/module/database/StorageDatabase.py
deleted file mode 100644
index 3ed29625f..000000000
--- a/module/database/StorageDatabase.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from module.database import style
-from module.database import DatabaseBackend
-
-class StorageMethods():
- @style.queue
- def setStorage(db, identifier, key, value):
- db.c.execute("SELECT id FROM storage WHERE identifier=? AND key=?", (identifier, key))
- if db.c.fetchone() is not None:
- db.c.execute("UPDATE storage SET value=? WHERE identifier=? AND key=?", (value, identifier, key))
- else:
- db.c.execute("INSERT INTO storage (identifier, key, value) VALUES (?, ?, ?)", (identifier, key, value))
-
- @style.queue
- def getStorage(db, identifier, key=None):
- if key is not None:
- db.c.execute("SELECT value FROM storage WHERE identifier=? AND key=?", (identifier, key))
- row = db.c.fetchone()
- if row is not None:
- return row[0]
- else:
- db.c.execute("SELECT key, value FROM storage WHERE identifier=?", (identifier, ))
- d = {}
- for row in db.c:
- d[row[0]] = row[1]
- return d
-
- @style.queue
- def delStorage(db, identifier, key):
- db.c.execute("DELETE FROM storage WHERE identifier=? AND key=?", (identifier, key))
-
-DatabaseBackend.registerSub(StorageMethods)
diff --git a/module/database/UserDatabase.py b/module/database/UserDatabase.py
deleted file mode 100644
index 0c781057d..000000000
--- a/module/database/UserDatabase.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from hashlib import sha1
-import random
-
-from DatabaseBackend import DatabaseBackend
-from DatabaseBackend import style
-
-class UserMethods():
- @style.queue
- def checkAuth(db, user, password):
- c = db.c
- c.execute('SELECT id, name, password, role, permission, template, email FROM "users" WHERE name=?', (user, ))
- r = c.fetchone()
- if not r:
- return {}
-
- salt = r[2][:5]
- pw = r[2][5:]
- h = sha1(salt + password)
- if h.hexdigest() == pw:
- return {"id": r[0], "name": r[1], "role": r[3],
- "permission": r[4], "template": r[5], "email": r[6]}
- else:
- return {}
-
- @style.queue
- def addUser(db, user, password):
- salt = reduce(lambda x, y: x + y, [str(random.randint(0, 9)) for i in range(0, 5)])
- h = sha1(salt + password)
- password = salt + h.hexdigest()
-
- c = db.c
- c.execute('SELECT name FROM users WHERE name=?', (user, ))
- if c.fetchone() is not None:
- c.execute('UPDATE users SET password=? WHERE name=?', (password, user))
- else:
- c.execute('INSERT INTO users (name, password) VALUES (?, ?)', (user, password))
-
-
- @style.queue
- def changePassword(db, user, oldpw, newpw):
- db.c.execute('SELECT id, name, password FROM users WHERE name=?', (user, ))
- r = db.c.fetchone()
- if not r:
- return False
-
- salt = r[2][:5]
- pw = r[2][5:]
- h = sha1(salt + oldpw)
- if h.hexdigest() == pw:
- salt = reduce(lambda x, y: x + y, [str(random.randint(0, 9)) for i in range(0, 5)])
- h = sha1(salt + newpw)
- password = salt + h.hexdigest()
-
- db.c.execute("UPDATE users SET password=? WHERE name=?", (password, user))
- return True
-
- return False
-
-
- @style.async
- def setPermission(db, user, perms):
- db.c.execute("UPDATE users SET permission=? WHERE name=?", (perms, user))
-
- @style.async
- def setRole(db, user, role):
- db.c.execute("UPDATE users SET role=? WHERE name=?", (role, user))
-
-
- @style.queue
- def listUsers(db):
- db.c.execute('SELECT name FROM users')
- users = []
- for row in db.c:
- users.append(row[0])
- return users
-
- @style.queue
- def getAllUserData(db):
- db.c.execute("SELECT name, permission, role, template, email FROM users")
- user = {}
- for r in db.c:
- user[r[0]] = {"permission": r[1], "role": r[2], "template": r[3], "email": r[4]}
-
- return user
-
- @style.queue
- def removeUser(db, user):
- db.c.execute('DELETE FROM users WHERE name=?', (user, ))
-
-DatabaseBackend.registerSub(UserMethods)
diff --git a/module/database/__init__.py b/module/database/__init__.py
deleted file mode 100644
index 545789c0c..000000000
--- a/module/database/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from DatabaseBackend import DatabaseBackend
-from DatabaseBackend import style
-
-from FileDatabase import FileHandler
-from UserDatabase import UserMethods
-from StorageDatabase import StorageMethods \ No newline at end of file
diff --git a/module/debug.py b/module/debug.py
deleted file mode 100644
index 8d1ddd3d0..000000000
--- a/module/debug.py
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/usr/bin/env python
-#coding:utf-8
-
-import re
-import InitHomeDir
-from os import listdir
-
-class Wrapper(object):
- pass
-
-def filter_info(line):
- if "object at 0x" in line:
- return False
- elif " at line " in line:
- return False
- elif " <DownloadThread(" in line:
- return False
- elif "<class '" in line:
- return False
- elif "PyFile " in line:
- return False
- elif " <module '" in line:
- return False
-
- else:
- return True
-
-def appendName(lines, name):
- test = re.compile("^[a-zA-z0-9]+ = ")
-
- for i, line in enumerate(lines):
- if test.match(line):
- lines[i] = name+"."+line
-
- return lines
-
-def initReport():
- reports = []
- for f in listdir("."):
- if f.startswith("debug_"):
- reports.append(f)
-
- for i, f in enumerate(reports):
- print "%s. %s" % (i,f)
-
- choice = raw_input("Choose Report: ")
-
- report = reports[int(choice)]
-
- f = open(report, "rb")
-
- content = f.readlines()
- content = [x.strip() for x in content if x.strip()]
-
- frame = Wrapper()
- plugin = Wrapper()
- pyfile = Wrapper()
-
- frame_c = []
- plugin_c = []
- pyfile_c = []
-
- dest = None
-
- for line in content:
- if line == "FRAMESTACK:":
- dest = frame_c
- continue
- elif line == "PLUGIN OBJECT DUMP:":
- dest = plugin_c
- continue
- elif line == "PYFILE OBJECT DUMP:":
- dest = pyfile_c
- continue
- elif line == "CONFIG:":
- dest = None
-
- if dest is not None:
- dest.append(line)
-
-
- frame_c = filter(filter_info, frame_c)
- plugin_c = filter(filter_info, plugin_c)
- pyfile_c = filter(filter_info, pyfile_c)
-
- frame_c = appendName(frame_c, "frame")
- plugin_c = appendName(plugin_c, "plugin")
- pyfile_c = appendName(pyfile_c, "pyfile")
-
- exec("\n".join(frame_c+plugin_c+pyfile_c) )
-
- return frame, plugin, pyfile
-
-if __name__=='__main__':
- print "No main method, use this module with your python shell" \ No newline at end of file
diff --git a/module/forwarder.py b/module/forwarder.py
deleted file mode 100644
index eacb33c2b..000000000
--- a/module/forwarder.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- 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
-"""
-
-from sys import argv
-from sys import exit
-
-import socket
-import thread
-
-from traceback import print_exc
-
-class Forwarder():
-
- def __init__(self, extip,extport=9666):
- print "Start portforwarding to %s:%s" % (extip, extport)
- proxy(extip, extport, 9666)
-
-
-def proxy(*settings):
- while True:
- server(*settings)
-
-def server(*settings):
- try:
- dock_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- dock_socket.bind(("127.0.0.1", settings[2]))
- dock_socket.listen(5)
- while True:
- client_socket = dock_socket.accept()[0]
- server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- server_socket.connect((settings[0], settings[1]))
- thread.start_new_thread(forward, (client_socket, server_socket))
- thread.start_new_thread(forward, (server_socket, client_socket))
- except Exception:
- print_exc()
-
-
-def forward(source, destination):
- string = ' '
- while string:
- string = source.recv(1024)
- if string:
- destination.sendall(string)
- else:
- #source.shutdown(socket.SHUT_RD)
- destination.shutdown(socket.SHUT_WR)
-
-if __name__ == "__main__":
- args = argv[1:]
- if not args:
- print "Usage: forwarder.py <remote ip> <remote port>"
- exit()
- if len(args) == 1:
- args.append(9666)
-
- f = Forwarder(args[0], int(args[1]))
- \ No newline at end of file
diff --git a/module/gui/AccountEdit.py b/module/gui/AccountEdit.py
deleted file mode 100644
index b22cfc49f..000000000
--- a/module/gui/AccountEdit.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from os.path import join
-
-class AccountEdit(QWidget):
- """
- account editor widget
- """
-
- def __init__(self):
- QMainWindow.__init__(self)
-
- self.setWindowTitle(_("Edit account"))
- self.setWindowIcon(QIcon(join(pypath, "icons","logo.png")))
-
- self.setLayout(QGridLayout())
- l = self.layout()
-
- typeLabel = QLabel(_("Type"))
- loginLabel = QLabel(_("Login"))
- passwordLabel = QLabel(_("New password"))
- changePw = QCheckBox()
- changePw.setChecked(False)
- self.changePw = changePw
- password = QLineEdit()
- password.setEnabled(False)
- password.setEchoMode(QLineEdit.Password)
- self.password = password
- login = QLineEdit()
- self.login = login
- acctype = QComboBox()
- self.acctype = acctype
-
- save = QPushButton(_("Save"))
-
- self.connect(changePw, SIGNAL("toggled(bool)"), password, SLOT("setEnabled(bool)"))
-
- l.addWidget(save, 3, 0, 1, 3)
- l.addWidget(acctype, 0, 1, 1, 2)
- l.addWidget(login, 1, 1, 1, 2)
- l.addWidget(password, 2, 2)
- l.addWidget(changePw, 2, 1)
- l.addWidget(passwordLabel, 2, 0)
- l.addWidget(loginLabel, 1, 0)
- l.addWidget(typeLabel, 0, 0)
-
- self.connect(save, SIGNAL("clicked()"), self.slotSave)
-
- def slotSave(self):
- """
- save entered data
- """
- data = {"login": str(self.login.text()), "acctype": str(self.acctype.currentText()), "password": False}
- if self.changePw.isChecked():
- data["password"] = str(self.password.text())
- self.emit(SIGNAL("done"), data)
-
- @staticmethod
- def newAccount(types):
- """
- create empty editor instance
- """
- w = AccountEdit()
- w.setWindowTitle(_("Create account"))
-
- w.changePw.setChecked(True)
- w.password.setEnabled(True)
-
- w.acctype.addItems(types)
-
- return w
-
- @staticmethod
- def editAccount(types, base):
- """
- create editor instance with given data
- """
- w = AccountEdit()
-
- w.acctype.addItems(types)
- w.acctype.setCurrentIndex(types.index(base["type"]))
-
- w.login.setText(base["login"])
-
- return w
diff --git a/module/gui/Accounts.py b/module/gui/Accounts.py
deleted file mode 100644
index 8db04dfa9..000000000
--- a/module/gui/Accounts.py
+++ /dev/null
@@ -1,213 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from time import strftime, gmtime
-
-class AccountModel(QAbstractItemModel):
- """
- model for account view
- """
-
- def __init__(self, view, connector):
- QAbstractItemModel.__init__(self)
- self.connector = connector
- self.view = view
- self._data = []
- self.cols = 4
- self.mutex = QMutex()
-
- def reloadData(self, force=False):
- """
- reload account list
- """
- accounts = self.connector.proxy.getAccounts(False)
-
- if self._data == accounts:
- return
-
- if len(self._data) > 0:
- self.beginRemoveRows(QModelIndex(), 0, len(self._data)-1)
- self._data = []
- self.endRemoveRows()
-
- if len(accounts) > 0:
- self.beginInsertRows(QModelIndex(), 0, len(accounts)-1)
- self._data = accounts
- self.endInsertRows()
-
- def toData(self, index):
- """
- return index pointer
- """
- return index.internalPointer()
-
- def data(self, index, role=Qt.DisplayRole):
- """
- return cell data
- """
- if not index.isValid():
- return QVariant()
- if role == Qt.DisplayRole:
- if index.column() == 0:
- return QVariant(self.toData(index).type)
- elif index.column() == 1:
- return QVariant(self.toData(index).login)
- elif index.column() == 2:
- if not self.toData(index).valid:
- return QVariant(_("not valid"))
- if not self.toData(index).validuntil:
- return QVariant(_("n/a"))
- until = int(self.toData(index).validuntil)
- if until > 0:
- fmtime = strftime(_("%a, %d %b %Y %H:%M"), gmtime(until))
- return QVariant(fmtime)
- else:
- return QVariant(_("unlimited"))
- #elif role == Qt.EditRole:
- # if index.column() == 0:
- # return QVariant(index.internalPointer().data["name"])
- return QVariant()
-
- def index(self, row, column, parent=QModelIndex()):
- """
- create index with data pointer
- """
- if parent == QModelIndex() and len(self._data) > row:
- pointer = self._data[row]
- index = self.createIndex(row, column, pointer)
- elif parent.isValid():
- pointer = parent.internalPointer().children[row]
- index = self.createIndex(row, column, pointer)
- else:
- index = QModelIndex()
- return index
-
- def parent(self, index):
- """
- no parents, everything on top level
- """
- return QModelIndex()
-
- def rowCount(self, parent=QModelIndex()):
- """
- account count
- """
- if parent == QModelIndex():
- return len(self._data)
- return 0
-
- def columnCount(self, parent=QModelIndex()):
- return self.cols
-
- def hasChildren(self, parent=QModelIndex()):
- """
- everything on top level
- """
- if parent == QModelIndex():
- return True
- else:
- return False
-
- def canFetchMore(self, parent):
- return False
-
- def headerData(self, section, orientation, role=Qt.DisplayRole):
- """
- returns column heading
- """
- if orientation == Qt.Horizontal and role == Qt.DisplayRole:
- if section == 0:
- return QVariant(_("Type"))
- elif section == 1:
- return QVariant(_("Login"))
- elif section == 2:
- return QVariant(_("Valid until"))
- elif section == 3:
- return QVariant(_("Traffic left"))
- return QVariant()
-
- def flags(self, index):
- """
- cell flags
- """
- return Qt.ItemIsSelectable | Qt.ItemIsEnabled
-
-class AccountView(QTreeView):
- """
- view component for accounts
- """
-
- def __init__(self, connector):
- QTreeView.__init__(self)
- self.setModel(AccountModel(self, connector))
-
- self.setColumnWidth(0, 150)
- self.setColumnWidth(1, 150)
- self.setColumnWidth(2, 150)
- self.setColumnWidth(3, 150)
-
- self.setEditTriggers(QAbstractItemView.NoEditTriggers)
-
- self.delegate = AccountDelegate(self, self.model())
- self.setItemDelegateForColumn(3, self.delegate)
-
-class AccountDelegate(QItemDelegate):
- """
- used to display a progressbar for the traffic in the traffic cell
- """
-
- def __init__(self, parent, model):
- QItemDelegate.__init__(self, parent)
- self.model = model
-
- def paint(self, painter, option, index):
- """
- paint the progressbar
- """
- if not index.isValid():
- return
- if index.column() == 3:
- data = self.model.toData(index)
- opts = QStyleOptionProgressBarV2()
- opts.minimum = 0
- if data.trafficleft:
- if data.trafficleft == -1 or data.trafficleft is None:
- opts.maximum = opts.progress = 1
- else:
- opts.maximum = opts.progress = data.trafficleft
- if data.maxtraffic:
- opts.maximum = data.maxtraffic
-
- opts.rect = option.rect
- opts.rect.setRight(option.rect.right()-1)
- opts.rect.setHeight(option.rect.height()-1)
- opts.textVisible = True
- opts.textAlignment = Qt.AlignCenter
- if data.trafficleft and data.trafficleft == -1:
- opts.text = QString(_("unlimited"))
- elif data.trafficleft is None:
- opts.text = QString(_("n/a"))
- else:
- opts.text = QString.number(round(float(opts.progress)/1024/1024, 2)) + " GB"
- QApplication.style().drawControl(QStyle.CE_ProgressBar, opts, painter)
- return
- QItemDelegate.paint(self, painter, option, index)
-
diff --git a/module/gui/CNLServer.py b/module/gui/CNLServer.py
deleted file mode 100644
index 5ecac8277..000000000
--- a/module/gui/CNLServer.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# -*- 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
-"""
-import re
-from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
-from threading import Thread
-from cgi import FieldStorage
-from os.path import abspath, dirname, join
-from urllib import unquote
-from base64 import standard_b64decode
-from binascii import unhexlify
-
-try:
- from Crypto.Cipher import AES
-except:
- pass
-
-try:
- from module.common import JsEngine
-except ImportError:
- import sys
- sys.path.append(join(abspath(dirname(__file__)), "..", ".."))
- from module.common import JsEngine
-
-js = JsEngine.JsEngine()
-core = None
-
-class CNLServer(Thread):
- def __init__(self):
- Thread.__init__(self)
- self.setDaemon(True)
-
- self.stop = False
- self.stopped = False
-
- def run(self):
- server_address = ('127.0.0.1', 9666)
- try:
- httpd = HTTPServer(server_address, CNLHandler)
- except:
- self.stopped = True
- return
-
- self.stopped = False
- while self.keep_running():
- httpd.handle_request()
- self.stopped = True
-
- def keep_running(self):
- return not self.stop
-
-
-class CNLHandler(BaseHTTPRequestHandler):
-
- #def log_message(self, *args):
- # pass
-
- def add_package(self, name, urls, queue=0):
- print "name", name
- print "urls", urls
- print "queue", queue
-
- def get_post(self, name, default=""):
- if name in self.post:
- return self.post[name]
- else:
- return default
-
- def start_response(self, string):
-
- self.send_response(200)
-
- self.send_header("Content-Length", len(string))
- self.send_header("Content-Language", "de")
- self.send_header("Vary", "Accept-Language, Cookie")
- self.send_header("Cache-Control", "no-cache, must-revalidate")
- self.send_header("Content-type", "text/html")
- self.end_headers()
-
- def do_GET(self):
- path = self.path.strip("/").lower()
- #self.wfile.write(path+"\n")
-
- self.map = [ (r"add$", self.add),
- (r"addcrypted$", self.addcrypted),
- (r"addcrypted2$", self.addcrypted2),
- (r"flashgot", self.flashgot),
- (r"crossdomain\.xml", self.crossdomain),
- (r"checkSupportForUrl", self.checksupport),
- (r"jdcheck.js", self.jdcheck),
- (r"", self.flash) ]
-
- func = None
- for r, f in self.map:
- if re.match(r"(flash(got)?/?)?"+r, path):
- func = f
- break
-
- if func:
- try:
- resp = func()
- if not resp: resp = "success"
- resp += "\r\n"
- self.start_response(resp)
- self.wfile.write(resp)
- except Exception,e :
- self.send_error(500, str(e))
- else:
- self.send_error(404, "Not Found")
-
- def do_POST(self):
- form = FieldStorage(
- fp=self.rfile,
- headers=self.headers,
- environ={'REQUEST_METHOD':'POST',
- 'CONTENT_TYPE':self.headers['Content-Type'],
- })
-
- self.post = {}
- for name in form.keys():
- self.post[name] = form[name].value
-
- return self.do_GET()
-
- def flash(self):
- return "JDownloader"
-
- def add(self):
- package = self.get_post('referer', 'ClickAndLoad Package')
- urls = filter(lambda x: x != "", self.get_post('urls').split("\n"))
-
- self.add_package(package, urls, 0)
-
- def addcrypted(self):
- package = self.get_post('referer', 'ClickAndLoad Package')
- dlc = self.get_post('crypted').replace(" ", "+")
-
- core.upload_container(package, dlc)
-
- def addcrypted2(self):
- package = self.get_post("source", "ClickAndLoad Package")
- crypted = self.get_post("crypted")
- jk = self.get_post("jk")
-
- crypted = standard_b64decode(unquote(crypted.replace(" ", "+")))
- jk = "%s f()" % jk
- jk = js.eval(jk)
- Key = unhexlify(jk)
- IV = Key
-
- obj = AES.new(Key, AES.MODE_CBC, IV)
- result = obj.decrypt(crypted).replace("\x00", "").replace("\r","").split("\n")
-
- result = filter(lambda x: x != "", result)
-
- self.add_package(package, result, 0)
-
-
- def flashgot(self):
- autostart = int(self.get_post('autostart', 0))
- package = self.get_post('package', "FlashGot")
- urls = filter(lambda x: x != "", self.get_post('urls').split("\n"))
-
- self.add_package(package, urls, autostart)
-
- def crossdomain(self):
- rep = "<?xml version=\"1.0\"?>\n"
- rep += "<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n"
- rep += "<cross-domain-policy>\n"
- rep += "<allow-access-from domain=\"*\" />\n"
- rep += "</cross-domain-policy>"
- return rep
-
- def checksupport(self):
- pass
-
- def jdcheck(self):
- rep = "jdownloader=true;\n"
- rep += "var version='10629';\n"
- return rep
-
-
-if __name__ == "__main__":
- import xmlrpclib
- from module import InitHomeDir
- from module.ConfigParser import ConfigParser
-
- config = ConfigParser()
-
- ssl = ""
- if config.get("ssl", "activated"):
- ssl = "s"
-
- server_url = "http%s://%s:%s@%s:%s/" % (
- ssl,
- config.username,
- config.password,
- config.get("remote", "listenaddr"),
- config.get("remote", "port")
- )
-
- core = xmlrpclib.ServerProxy(server_url, allow_none=True)
-
- s = CNLServer()
- s.start()
- while not s.stopped:
- try:
- s.join(1)
- except KeyboardInterrupt:
- s.stop = True
- s.stopped = True
- print "quiting.."
diff --git a/module/gui/CaptchaDock.py b/module/gui/CaptchaDock.py
deleted file mode 100644
index b88cb53ca..000000000
--- a/module/gui/CaptchaDock.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-class CaptchaDock(QDockWidget):
- """
- dock widget for captcha input
- """
-
- def __init__(self):
- QDockWidget.__init__(self, _("Captcha"))
- self.setObjectName("Captcha Dock")
- self.widget = CaptchaDockWidget(self)
- self.setWidget(self.widget)
- self.setAllowedAreas(Qt.BottomDockWidgetArea)
- self.setFeatures(QDockWidget.NoDockWidgetFeatures)
- self.hide()
- self.processing = False
- self.currentID = None
- self.connect(self, SIGNAL("setTask"), self.setTask)
-
- def isFree(self):
- return not self.processing
-
- def setTask(self, tid, img, imgType):
- self.processing = True
- data = QByteArray(img)
- self.currentID = tid
- self.widget.emit(SIGNAL("setImage"), data)
- self.widget.input.setText("")
- self.show()
-
-class CaptchaDockWidget(QWidget):
- """
- widget for the input widgets
- """
-
- def __init__(self, dock):
- QWidget.__init__(self)
- self.dock = dock
- self.setLayout(QHBoxLayout())
- layout = self.layout()
-
- imgLabel = QLabel()
- captchaInput = QLineEdit()
- okayButton = QPushButton(_("OK"))
- cancelButton = QPushButton(_("Cancel"))
-
- layout.addStretch()
- layout.addWidget(imgLabel)
- layout.addWidget(captchaInput)
- layout.addWidget(okayButton)
- layout.addWidget(cancelButton)
- layout.addStretch()
-
- self.input = captchaInput
-
- self.connect(okayButton, SIGNAL("clicked()"), self.slotSubmit)
- self.connect(captchaInput, SIGNAL("returnPressed()"), self.slotSubmit)
- self.connect(self, SIGNAL("setImage"), self.setImg)
- self.connect(self, SIGNAL("setPixmap(const QPixmap &)"), imgLabel, SLOT("setPixmap(const QPixmap &)"))
-
- def setImg(self, data):
- pixmap = QPixmap()
- pixmap.loadFromData(data)
- self.emit(SIGNAL("setPixmap(const QPixmap &)"), pixmap)
- self.input.setFocus(Qt.OtherFocusReason)
-
- def slotSubmit(self):
- text = self.input.text()
- tid = self.dock.currentID
- self.dock.currentID = None
- self.dock.emit(SIGNAL("done"), tid, str(text))
- self.dock.hide()
- self.dock.processing = False
-
diff --git a/module/gui/Collector.py b/module/gui/Collector.py
deleted file mode 100644
index 3ec4262f1..000000000
--- a/module/gui/Collector.py
+++ /dev/null
@@ -1,407 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from module.PyFile import statusMap
-from module.utils import formatSize
-
-from module.remote.thriftbackend.ThriftClient import Destination, FileDoesNotExists, ElementType
-
-statusMapReverse = dict((v,k) for k, v in statusMap.iteritems())
-
-translatedStatusMap = {} # -> CollectorModel.__init__
-
-class CollectorModel(QAbstractItemModel):
- """
- model for the collector view
- """
-
- def __init__(self, view, connector):
- QAbstractItemModel.__init__(self)
- self.connector = connector
- self.view = view
- self._data = []
- self.cols = 4
- self.interval = 1
- self.mutex = QMutex()
-
- global translatedStatusMap # workaround because i18n is not running at import time
- translatedStatusMap = {
- "finished": _("finished"),
- "offline": _("offline"),
- "online": _("online"),
- "queued": _("queued"),
- "skipped": _("skipped"),
- "waiting": _("waiting"),
- "temp. offline": _("temp. offline"),
- "starting": _("starting"),
- "failed": _("failed"),
- "aborted": _("aborted"),
- "decrypting": _("decrypting"),
- "custom": _("custom"),
- "downloading": _("downloading"),
- "processing": _("processing")
- }
-
- def translateStatus(self, string):
- """
- used to convert to locale specific status
- """
- return translatedStatusMap[string]
-
- def addEvent(self, event):
- """
- called from main loop, pass events to the correct methods
- """
- QMutexLocker(self.mutex)
- if event.eventname == "reload":
- self.fullReload()
- elif event.eventname == "remove":
- self.removeEvent(event)
- elif event.eventname == "insert":
- self.insertEvent(event)
- elif event.eventname == "update":
- self.updateEvent(event)
-
- def fullReload(self):
- """
- reload whole model, used at startup to load initial data
- """
- self._data = []
- order = self.connector.getPackageOrder(Destination.Collector)
- self.beginInsertRows(QModelIndex(), 0, len(order.values()))
- for position, pid in order.iteritems():
- pack = self.connector.getPackageData(pid)
- package = Package(pack)
- self._data.append(package)
- self._data = sorted(self._data, key=lambda p: p.data["order"])
- self.endInsertRows()
-
- def removeEvent(self, event):
- """
- remove an element from model
- """
- if event.type == ElementType.File:
- for p, package in enumerate(self._data):
- for k, child in enumerate(package.children):
- if child.id == event.id:
- self.beginRemoveRows(self.index(p, 0), k, k)
- del package.children[k]
- self.endRemoveRows()
- break
- else:
- for k, package in enumerate(self._data):
- if package.id == event.id:
- self.beginRemoveRows(QModelIndex(), k, k)
- del self._data[k]
- self.endRemoveRows()
- break
-
- def insertEvent(self, event):
- """
- inserts a new element in the model
- """
- if event.type == ElementType.File:
- try:
- info = self.connector.getFileData(event.id)
- except FileDoesNotExists:
- return
-
- for k, package in enumerate(self._data):
- if package.id == info.package:
- if package.getChild(info.fid):
- self.updateEvent(event)
- break
- self.beginInsertRows(self.index(k, 0), info.order, info.order)
- package.addChild(info)
- self.endInsertRows()
- break
- else:
- data = self.connector.getPackageData(event.id)
- package = Package(data)
- self.beginInsertRows(QModelIndex(), data.order, data.order)
- self._data.insert(data.order, package)
- self.endInsertRows()
-
- def updateEvent(self, event):
- """
- update an element in the model
- """
- if event.type == ElementType.File:
- try:
- info = self.connector.proxy.getFileData(event.id)
- except FileDoesNotExists:
- return
- for p, package in enumerate(self._data):
- if package.id == info.packageID:
- for k, child in enumerate(package.children):
- if child.id == event.id:
- child.update(info)
- if not info.status == 12:
- child.data["downloading"] = None
- self.emit(SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"), self.index(k, 0, self.index(p, 0)), self.index(k, self.cols, self.index(p, self.cols)))
- break
- else:
- data = self.connector.getPackageData(event.id)
- if not data:
- return
- for p, package in enumerate(self._data):
- if package.id == event.id:
- package.update(data)
- self.emit(SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"), self.index(p, 0), self.index(p, self.cols))
- break
-
- def data(self, index, role=Qt.DisplayRole):
- """
- return cell data
- """
- if not index.isValid():
- return QVariant()
- if role == Qt.DisplayRole:
- if index.column() == 0:
- return QVariant(index.internalPointer().data["name"])
- elif index.column() == 1:
- item = index.internalPointer()
- plugins = []
- if isinstance(item, Package):
- for child in item.children:
- if not child.data["plugin"] in plugins:
- plugins.append(child.data["plugin"])
- else:
- plugins.append(item.data["plugin"])
- return QVariant(", ".join(plugins))
- elif index.column() == 2:
- item = index.internalPointer()
- status = 0
- if isinstance(item, Package):
- for child in item.children:
- if child.data["status"] > status:
- status = child.data["status"]
- else:
- status = item.data["status"]
- return QVariant(self.translateStatus(statusMapReverse[status]))
- elif index.column() == 3:
- item = index.internalPointer()
- if isinstance(item, Link):
- return QVariant(formatSize(item.data["size"]))
- else:
- ms = 0
- for c in item.children:
- ms += c.data["size"]
- return QVariant(formatSize(ms))
- elif role == Qt.EditRole:
- if index.column() == 0:
- return QVariant(index.internalPointer().data["name"])
- return QVariant()
-
- def index(self, row, column, parent=QModelIndex()):
- """
- creates a cell index with pointer to the data
- """
- if parent == QModelIndex() and len(self._data) > row:
- pointer = self._data[row]
- index = self.createIndex(row, column, pointer)
- elif parent.isValid():
- try:
- pointer = parent.internalPointer().children[row]
- except:
- return QModelIndex()
- index = self.createIndex(row, column, pointer)
- else:
- index = QModelIndex()
- return index
-
- def parent(self, index):
- """
- return index of parent element
- only valid for links
- """
- if index == QModelIndex():
- return QModelIndex()
- if index.isValid():
- link = index.internalPointer()
- if isinstance(link, Link):
- for k, pack in enumerate(self._data):
- if pack == link.package:
- return self.createIndex(k, 0, link.package)
- return QModelIndex()
-
- def rowCount(self, parent=QModelIndex()):
- """
- returns row count for the element
- """
- if parent == QModelIndex():
- #return package count
- return len(self._data)
- else:
- if parent.isValid():
- #index is valid
- pack = parent.internalPointer()
- if isinstance(pack, Package):
- #index points to a package
- #return len of children
- return len(pack.children)
- else:
- #index is invalid
- return False
- #files have no children
- return 0
-
- def columnCount(self, parent=QModelIndex()):
- return self.cols
-
- def hasChildren(self, parent=QModelIndex()):
- if not parent.isValid():
- return True
- return self.rowCount(parent) > 0
-
- def canFetchMore(self, parent):
- return False
-
- def headerData(self, section, orientation, role=Qt.DisplayRole):
- """
- returns column heading
- """
- if orientation == Qt.Horizontal and role == Qt.DisplayRole:
- if section == 0:
- return QVariant(_("Name"))
- elif section == 1:
- return QVariant(_("Plugin"))
- elif section == 2:
- return QVariant(_("Status"))
- elif section == 3:
- return QVariant(_("Size"))
- return QVariant()
-
- def flags(self, index):
- """
- cell flags
- """
- if index.column() == 0 and self.parent(index) == QModelIndex():
- return Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled
- return Qt.ItemIsSelectable | Qt.ItemIsEnabled
-
- def setData(self, index, value, role=Qt.EditRole):
- """
- called if package name editing is finished, sets new name
- """
- if index.column() == 0 and self.parent(index) == QModelIndex() and role == Qt.EditRole:
- self.connector.setPackageName(index.internalPointer().id, str(value.toString()))
- return True
-
-class Package(object):
- """
- package object in the model
- """
-
- def __init__(self, pack):
- self.id = pack.pid
- self.children = []
- for f in pack.links:
- self.addChild(f)
- self.data = {}
- self.update(pack)
-
- def update(self, pack):
- """
- update data dict from thift object
- """
- data = {
- "name": pack.name,
- "folder": pack.folder,
- "site": pack.site,
- "password": pack.password,
- "order": pack.order,
- }
- self.data.update(data)
-
- def addChild(self, f):
- """
- add child (Link) to package
- """
- self.children.insert(f.order, Link(f, self))
- self.children = sorted(self.children, key=lambda l: l.data["order"])
-
- def getChild(self, fid):
- """
- get child from package
- """
- for child in self.children:
- if child.id == int(fid):
- return child
- return None
-
- def getChildKey(self, fid):
- """
- get child index
- """
- for k, child in enumerate(self.children):
- if child.id == int(fid):
- return k
- return None
-
- def removeChild(self, fid):
- """
- remove child
- """
- for k, child in enumerate(self.children):
- if child.id == int(fid):
- del self.children[k]
-
-class Link(object):
- def __init__(self, f, pack):
- self.data = {"downloading": None}
- self.update(f)
- self.id = f.fid
- self.package = pack
-
- def update(self, f):
- """
- update data dict from thift object
- """
- data = {
- "url": f.url,
- "name": f.name,
- "plugin": f.plugin,
- "size": f.size,
- "format_size": f.format_size,
- "status": f.status,
- "statusmsg": f.statusmsg,
- "package": f.packageID,
- "error": f.error,
- "order": f.order,
- }
- self.data.update(data)
-
-class CollectorView(QTreeView):
- """
- view component for collector
- """
-
- def __init__(self, connector):
- QTreeView.__init__(self)
- self.setModel(CollectorModel(self, connector))
- self.setColumnWidth(0, 500)
- self.setColumnWidth(1, 100)
- self.setColumnWidth(2, 200)
- self.setColumnWidth(3, 100)
-
- self.setEditTriggers(QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed)
-
diff --git a/module/gui/ConnectionManager.py b/module/gui/ConnectionManager.py
deleted file mode 100644
index def575abc..000000000
--- a/module/gui/ConnectionManager.py
+++ /dev/null
@@ -1,302 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from os.path import join
-
-from uuid import uuid4 as uuid
-
-class ConnectionManager(QWidget):
-
-
- warningShown = False
-
- def __init__(self):
- QWidget.__init__(self)
-
- if not self.warningShown:
- QMessageBox.warning(self, 'Warning',
- "We are sorry but the GUI is not stable yet. Please use the webinterface for much better experience. \n", QMessageBox.Ok)
- ConnectionManager.warningShown = True
-
- mainLayout = QHBoxLayout()
- buttonLayout = QVBoxLayout()
-
- self.setWindowTitle(_("pyLoad ConnectionManager"))
- self.setWindowIcon(QIcon(join(pypath, "icons","logo.png")))
-
- connList = QListWidget()
-
- new = QPushButton(_("New"))
- edit = QPushButton(_("Edit"))
- remove = QPushButton(_("Remove"))
- connect = QPushButton(_("Connect"))
-
- #box = QFrame()
- boxLayout = QVBoxLayout()
- #box.setLayout(boxLayout)
-
- boxLayout.addWidget(QLabel(_("Connect:")))
- boxLayout.addWidget(connList)
-
- line = QFrame()
- #line.setFixedWidth(100)
- line.setFrameShape(line.HLine)
- line.setFrameShadow(line.Sunken)
- line.setFixedHeight(10)
-
- boxLayout.addWidget(line)
-
- form = QFormLayout()
- form.setMargin(5)
- form.setSpacing(20)
-
- form.setAlignment(Qt.AlignRight)
- checkbox = QCheckBox()
- form.addRow(_("Use internal Core:"), checkbox)
-
- boxLayout.addLayout(form)
-
- mainLayout.addLayout(boxLayout)
- mainLayout.addLayout(buttonLayout)
-
- buttonLayout.addWidget(new)
- buttonLayout.addWidget(edit)
- buttonLayout.addWidget(remove)
- buttonLayout.addStretch()
- buttonLayout.addWidget(connect)
-
- self.setLayout(mainLayout)
-
- self.internal = checkbox
- self.new = new
- self.connectb = connect
- self.remove = remove
- self.editb = edit
- self.connList = connList
- self.edit = self.EditWindow()
- self.connectSignals()
-
- self.defaultStates = {}
-
- def connectSignals(self):
- self.connect(self, SIGNAL("setConnections"), self.setConnections)
- self.connect(self.new, SIGNAL("clicked()"), self.slotNew)
- self.connect(self.editb, SIGNAL("clicked()"), self.slotEdit)
- self.connect(self.remove, SIGNAL("clicked()"), self.slotRemove)
- self.connect(self.connectb, SIGNAL("clicked()"), self.slotConnect)
- self.connect(self.edit, SIGNAL("save"), self.slotSave)
- self.connect(self.connList, SIGNAL("itemDoubleClicked(QListWidgetItem *)"), self.slotItemDoubleClicked)
- self.connect(self.internal, SIGNAL("clicked()"), self.slotInternal)
-
- def setConnections(self, connections):
- self.connList.clear()
- for conn in connections:
- item = QListWidgetItem()
- item.setData(Qt.DisplayRole, QVariant(conn["name"]))
- item.setData(Qt.UserRole, QVariant(conn))
- self.connList.addItem(item)
- if conn["default"]:
- item.setData(Qt.DisplayRole, QVariant(_("%s (Default)") % conn["name"]))
- self.connList.setCurrentItem(item)
-
- def slotNew(self):
- data = {"id":uuid().hex, "type":"remote", "default":False, "name":"", "host":"", "port":"7228", "user":"admin", "password":""}
- self.edit.setData(data)
- self.edit.show()
-
- def slotEdit(self):
- item = self.connList.currentItem()
- data = item.data(Qt.UserRole).toPyObject()
- data = self.cleanDict(data)
- self.edit.setData(data)
- self.edit.show()
-
- def slotRemove(self):
- item = self.connList.currentItem()
- data = item.data(Qt.UserRole).toPyObject()
- data = self.cleanDict(data)
- self.emit(SIGNAL("removeConnection"), data)
-
- def slotConnect(self):
- if self.internal.checkState() == 2:
- data = {"type": "internal"}
- self.emit(SIGNAL("connect"), data)
- else:
- item = self.connList.currentItem()
- data = item.data(Qt.UserRole).toPyObject()
- data = self.cleanDict(data)
- self.emit(SIGNAL("connect"), data)
-
- def cleanDict(self, data):
- tmp = {}
- for k, d in data.items():
- tmp[str(k)] = d
- return tmp
-
- def slotSave(self, data):
- self.emit(SIGNAL("saveConnection"), data)
-
- def slotItemDoubleClicked(self, defaultItem):
- data = defaultItem.data(Qt.UserRole).toPyObject()
- self.setDefault(data, True)
- did = self.cleanDict(data)["id"]
- #allItems = self.connList.findItems("*", Qt.MatchWildcard)
- count = self.connList.count()
- for i in range(count):
- item = self.connList.item(i)
- data = item.data(Qt.UserRole).toPyObject()
- if self.cleanDict(data)["id"] == did:
- continue
- self.setDefault(data, False)
-
- def slotInternal(self):
- if self.internal.checkState() == 2:
- self.connList.clearSelection()
-
- def setDefault(self, data, state):
- data = self.cleanDict(data)
- self.edit.setData(data)
- data = self.edit.getData()
- data["default"] = state
- self.edit.emit(SIGNAL("save"), data)
-
- class EditWindow(QWidget):
- def __init__(self):
- QWidget.__init__(self)
-
- self.setWindowTitle(_("pyLoad ConnectionManager"))
- self.setWindowIcon(QIcon(join(pypath, "icons","logo.png")))
-
- grid = QGridLayout()
-
- nameLabel = QLabel(_("Name:"))
- hostLabel = QLabel(_("Host:"))
- localLabel = QLabel(_("Local:"))
- userLabel = QLabel(_("User:"))
- pwLabel = QLabel(_("Password:"))
- portLabel = QLabel(_("Port:"))
-
- name = QLineEdit()
- host = QLineEdit()
- local = QCheckBox()
- user = QLineEdit()
- password = QLineEdit()
- password.setEchoMode(QLineEdit.Password)
- port = QSpinBox()
- port.setRange(1,10000)
-
- save = QPushButton(_("Save"))
- cancel = QPushButton(_("Cancel"))
-
- grid.addWidget(nameLabel, 0, 0)
- grid.addWidget(name, 0, 1)
- grid.addWidget(localLabel, 1, 0)
- grid.addWidget(local, 1, 1)
- grid.addWidget(hostLabel, 2, 0)
- grid.addWidget(host, 2, 1)
- grid.addWidget(portLabel, 3, 0)
- grid.addWidget(port, 3, 1)
- grid.addWidget(userLabel, 4, 0)
- grid.addWidget(user, 4, 1)
- grid.addWidget(pwLabel, 5, 0)
- grid.addWidget(password, 5, 1)
- grid.addWidget(cancel, 6, 0)
- grid.addWidget(save, 6, 1)
-
- self.setLayout(grid)
- self.controls = {"name": name,
- "host": host,
- "local": local,
- "user": user,
- "password": password,
- "port": port,
- "save": save,
- "cancel": cancel}
-
- self.connect(cancel, SIGNAL("clicked()"), self.hide)
- self.connect(save, SIGNAL("clicked()"), self.slotDone)
- self.connect(local, SIGNAL("stateChanged(int)"), self.slotLocalChanged)
-
- self.id = None
- self.default = None
-
- def setData(self, data):
- if not data: return
-
- self.id = data["id"]
- self.default = data["default"]
- self.controls["name"].setText(data["name"])
- if data["type"] == "local":
- data["local"] = True
- else:
- data["local"] = False
- self.controls["local"].setChecked(data["local"])
- if not data["local"]:
- self.controls["user"].setText(data["user"])
- self.controls["password"].setText(data["password"])
- self.controls["port"].setValue(int(data["port"]))
- self.controls["host"].setText(data["host"])
- self.controls["user"].setDisabled(False)
- self.controls["password"].setDisabled(False)
- self.controls["port"].setDisabled(False)
- self.controls["host"].setDisabled(False)
- else:
- self.controls["user"].setText("")
- self.controls["port"].setValue(1)
- self.controls["host"].setText("")
- self.controls["user"].setDisabled(True)
- self.controls["password"].setDisabled(True)
- self.controls["port"].setDisabled(True)
- self.controls["host"].setDisabled(True)
-
- def slotLocalChanged(self, val):
- if val == 2:
- self.controls["user"].setDisabled(True)
- self.controls["password"].setDisabled(True)
- self.controls["port"].setDisabled(True)
- self.controls["host"].setDisabled(True)
- elif val == 0:
- self.controls["user"].setDisabled(False)
- self.controls["password"].setDisabled(False)
- self.controls["port"].setDisabled(False)
- self.controls["host"].setDisabled(False)
-
- def getData(self):
- d = {}
- d["id"] = self.id
- d["default"] = self.default
- d["name"] = self.controls["name"].text()
- d["local"] = self.controls["local"].isChecked()
- d["user"] = self.controls["user"].text()
- d["password"] = self.controls["password"].text()
- d["host"] = self.controls["host"].text()
- d["port"] = self.controls["port"].value()
- if d["local"]:
- d["type"] = "local"
- else:
- d["type"] = "remote"
- return d
-
- def slotDone(self):
- data = self.getData()
- self.hide()
- self.emit(SIGNAL("save"), data)
-
diff --git a/module/gui/CoreConfigParser.py b/module/gui/CoreConfigParser.py
deleted file mode 100644
index 0d1d298c6..000000000
--- a/module/gui/CoreConfigParser.py
+++ /dev/null
@@ -1,165 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-from os.path import exists
-from os.path import join
-
-
-CONF_VERSION = 1
-
-########################################################################
-class ConfigParser:
-
- #----------------------------------------------------------------------
- def __init__(self, configdir):
- """Constructor"""
- self.configdir = configdir
- self.config = {}
-
- if self.checkVersion():
- self.readConfig()
-
- #----------------------------------------------------------------------
- def checkVersion(self):
-
- if not exists(join(self.configdir, "pyload.conf")):
- return False
- f = open(join(self.configdir, "pyload.conf"), "rb")
- v = f.readline()
- f.close()
- v = v[v.find(":")+1:].strip()
-
- if int(v) < CONF_VERSION:
- return False
-
- return True
-
- #----------------------------------------------------------------------
- def readConfig(self):
- """reads the config file"""
-
- self.config = self.parseConfig(join(self.configdir, "pyload.conf"))
-
-
- #----------------------------------------------------------------------
- def parseConfig(self, config):
- """parses a given configfile"""
-
- f = open(config)
-
- config = f.read()
-
- config = config.split("\n")[1:]
-
- conf = {}
-
- section, option, value, typ, desc = "","","","",""
-
- listmode = False
-
- for line in config:
-
- line = line.rpartition("#") # removes comments
-
- if line[1]:
- line = line[0]
- else:
- line = line[2]
-
- line = line.strip()
-
- try:
-
- if line == "":
- continue
- elif line.endswith(":"):
- section, none, desc = line[:-1].partition('-')
- section = section.strip()
- desc = desc.replace('"', "").strip()
- conf[section] = { "desc" : desc }
- else:
- if listmode:
-
- if line.endswith("]"):
- listmode = False
- line = line.replace("]","")
-
- value += [self.cast(typ, x.strip()) for x in line.split(",") if x]
-
- if not listmode:
- conf[section][option] = { "desc" : desc,
- "type" : typ,
- "value" : value}
-
-
- else:
- content, none, value = line.partition("=")
-
- content, none, desc = content.partition(":")
-
- desc = desc.replace('"', "").strip()
-
- typ, option = content.split()
-
- value = value.strip()
-
- if value.startswith("["):
- if value.endswith("]"):
- listmode = False
- value = value[:-1]
- else:
- listmode = True
-
- value = [self.cast(typ, x.strip()) for x in value[1:].split(",") if x]
- else:
- value = self.cast(typ, value)
-
- if not listmode:
- conf[section][option] = { "desc" : desc,
- "type" : typ,
- "value" : value}
-
- except:
- pass
-
-
- f.close()
- return conf
-
- #----------------------------------------------------------------------
- def cast(self, typ, value):
- """cast value to given format"""
- if type(value) not in (str, unicode):
- return value
-
- if typ == "int":
- return int(value)
- elif typ == "bool":
- return True if value.lower() in ("1","true", "on", "an","yes") else False
- else:
- return value
-
- #----------------------------------------------------------------------
- def get(self, section, option):
- """get value"""
- return self.config[section][option]["value"]
-
- #----------------------------------------------------------------------
- def __getitem__(self, section):
- """provides dictonary like access: c['section']['option']"""
- return Section(self, section)
-
-########################################################################
-class Section:
- """provides dictionary like access for configparser"""
-
- #----------------------------------------------------------------------
- def __init__(self, parser, section):
- """Constructor"""
- self.parser = parser
- self.section = section
-
- #----------------------------------------------------------------------
- def __getitem__(self, item):
- """getitem"""
- return self.parser.get(self.section, item)
diff --git a/module/gui/LinkDock.py b/module/gui/LinkDock.py
deleted file mode 100644
index ac2d4aae5..000000000
--- a/module/gui/LinkDock.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-class NewLinkDock(QDockWidget):
- def __init__(self):
- QDockWidget.__init__(self, "New Links")
- self.setObjectName("New Links Dock")
- self.widget = NewLinkWindow(self)
- self.setWidget(self.widget)
- self.setAllowedAreas(Qt.RightDockWidgetArea|Qt.LeftDockWidgetArea)
- self.hide()
-
- def slotDone(self):
- text = str(self.widget.box.toPlainText())
- lines = text.splitlines()
- self.emit(SIGNAL("done"), lines)
- self.widget.box.clear()
- self.hide()
-
-class NewLinkWindow(QWidget):
- def __init__(self, dock):
- QWidget.__init__(self)
- self.dock = dock
- self.setLayout(QVBoxLayout())
- layout = self.layout()
-
- explanationLabel = QLabel("Select a package and then click Add button.")
- boxLabel = QLabel("Paste URLs here:")
- self.box = QTextEdit()
-
- save = QPushButton("Add")
-
- layout.addWidget(explanationLabel)
- layout.addWidget(boxLabel)
- layout.addWidget(self.box)
- layout.addWidget(save)
-
- self.connect(save, SIGNAL("clicked()"), self.dock.slotDone)
diff --git a/module/gui/MainWindow.py b/module/gui/MainWindow.py
deleted file mode 100644
index c71112e9b..000000000
--- a/module/gui/MainWindow.py
+++ /dev/null
@@ -1,697 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from os.path import join
-
-from module.gui.PackageDock import *
-from module.gui.LinkDock import *
-from module.gui.CaptchaDock import CaptchaDock
-from module.gui.SettingsWidget import SettingsWidget
-
-from module.gui.Collector import CollectorView, Package, Link
-from module.gui.Queue import QueueView
-from module.gui.Overview import OverviewView
-from module.gui.Accounts import AccountView
-from module.gui.AccountEdit import AccountEdit
-
-from module.remote.thriftbackend.ThriftClient import AccountInfo
-
-class MainWindow(QMainWindow):
- def __init__(self, connector):
- """
- set up main window
- """
- QMainWindow.__init__(self)
- #window stuff
- self.setWindowTitle(_("pyLoad Client"))
- self.setWindowIcon(QIcon(join(pypath, "icons","logo.png")))
- self.resize(1000,600)
-
- #layout version
- self.version = 3
-
- #init docks
- self.newPackDock = NewPackageDock()
- self.addDockWidget(Qt.RightDockWidgetArea, self.newPackDock)
- self.connect(self.newPackDock, SIGNAL("done"), self.slotAddPackage)
- self.captchaDock = CaptchaDock()
- self.addDockWidget(Qt.BottomDockWidgetArea, self.captchaDock)
- self.newLinkDock = NewLinkDock()
- self.addDockWidget(Qt.RightDockWidgetArea, self.newLinkDock)
- self.connect(self.newLinkDock, SIGNAL("done"), self.slotAddLinksToPackage)
-
- #central widget, layout
- self.masterlayout = QVBoxLayout()
- lw = QWidget()
- lw.setLayout(self.masterlayout)
- self.setCentralWidget(lw)
-
- #status
- self.statusw = QFrame()
- self.statusw.setFrameStyle(QFrame.StyledPanel | QFrame.Raised)
- self.statusw.setLineWidth(2)
- self.statusw.setLayout(QGridLayout())
- #palette = self.statusw.palette()
- #palette.setColor(QPalette.Window, QColor(255, 255, 255))
- #self.statusw.setPalette(palette)
- #self.statusw.setAutoFillBackground(True)
- l = self.statusw.layout()
-
- class BoldLabel(QLabel):
- def __init__(self, text):
- QLabel.__init__(self, text)
- f = self.font()
- f.setBold(True)
- self.setFont(f)
- self.setAlignment(Qt.AlignRight)
-
- class Seperator(QFrame):
- def __init__(self):
- QFrame.__init__(self)
- self.setFrameShape(QFrame.VLine)
- self.setFrameShadow(QFrame.Sunken)
-
- l.addWidget(BoldLabel(_("Packages:")), 0, 0)
- self.packageCount = QLabel("0")
- l.addWidget(self.packageCount, 0, 1)
-
- l.addWidget(BoldLabel(_("Files:")), 0, 2)
- self.fileCount = QLabel("0")
- l.addWidget(self.fileCount, 0, 3)
-
- l.addWidget(BoldLabel(_("Status:")), 0, 4)
- self.status = QLabel("running")
- l.addWidget(self.status, 0, 5)
-
- l.addWidget(BoldLabel(_("Space:")), 0, 6)
- self.space = QLabel("")
- l.addWidget(self.space, 0, 7)
-
- l.addWidget(BoldLabel(_("Speed:")), 0, 8)
- self.speed = QLabel("")
- l.addWidget(self.speed, 0, 9)
-
- #l.addWidget(BoldLabel(_("Max. downloads:")), 0, 9)
- #l.addWidget(BoldLabel(_("Max. chunks:")), 1, 9)
- #self.maxDownloads = QSpinBox()
- #self.maxDownloads.setEnabled(False)
- #self.maxChunks = QSpinBox()
- #self.maxChunks.setEnabled(False)
- #l.addWidget(self.maxDownloads, 0, 10)
- #l.addWidget(self.maxChunks, 1, 10)
-
- #set menubar and statusbar
- self.menubar = self.menuBar()
- #self.statusbar = self.statusBar()
- #self.connect(self.statusbar, SIGNAL("showMsg"), self.statusbar.showMessage)
- #self.serverStatus = QLabel(_("Status: Not Connected"))
- #self.statusbar.addPermanentWidget(self.serverStatus)
-
- #menu
- self.menus = {"file": self.menubar.addMenu(_("File")),
- "connections": self.menubar.addMenu(_("Connections"))}
-
- #menu actions
- self.mactions = {"exit": QAction(_("Exit"), self.menus["file"]),
- "manager": QAction(_("Connection manager"), self.menus["connections"])}
-
- #add menu actions
- self.menus["file"].addAction(self.mactions["exit"])
- self.menus["connections"].addAction(self.mactions["manager"])
-
- #toolbar
- self.actions = {}
- self.init_toolbar()
-
- #tabs
- self.tabw = QTabWidget()
- self.tabs = {"overview": {"w": QWidget()},
- "queue": {"w": QWidget()},
- "collector": {"w": QWidget()},
- "accounts": {"w": QWidget()},
- "settings": {}}
- #self.tabs["settings"]["s"] = QScrollArea()
- self.tabs["settings"]["w"] = SettingsWidget()
- #self.tabs["settings"]["s"].setWidgetResizable(True)
- #self.tabs["settings"]["s"].setWidget(self.tabs["settings"]["w"])
- self.tabs["log"] = {"w":QWidget()}
- self.tabw.addTab(self.tabs["overview"]["w"], _("Overview"))
- self.tabw.addTab(self.tabs["queue"]["w"], _("Queue"))
- self.tabw.addTab(self.tabs["collector"]["w"], _("Collector"))
- self.tabw.addTab(self.tabs["accounts"]["w"], _("Accounts"))
- self.tabw.addTab(self.tabs["settings"]["w"], _("Settings"))
- self.tabw.addTab(self.tabs["log"]["w"], _("Log"))
-
- #init tabs
- self.init_tabs(connector)
-
- #context menus
- self.init_context()
-
- #layout
- self.masterlayout.addWidget(self.tabw)
- self.masterlayout.addWidget(self.statusw)
-
- #signals..
- self.connect(self.mactions["manager"], SIGNAL("triggered()"), self.slotShowConnector)
-
- self.connect(self.tabs["queue"]["view"], SIGNAL('customContextMenuRequested(const QPoint &)'), self.slotQueueContextMenu)
- self.connect(self.tabs["collector"]["package_view"], SIGNAL('customContextMenuRequested(const QPoint &)'), self.slotCollectorContextMenu)
- self.connect(self.tabs["accounts"]["view"], SIGNAL('customContextMenuRequested(const QPoint &)'), self.slotAccountContextMenu)
-
- self.connect(self.tabw, SIGNAL("currentChanged(int)"), self.slotTabChanged)
-
- self.lastAddedID = None
-
- self.connector = connector
-
- def init_toolbar(self):
- """
- create toolbar
- """
- self.toolbar = self.addToolBar(_("Hide Toolbar"))
- self.toolbar.setObjectName("Main Toolbar")
- self.toolbar.setIconSize(QSize(30,30))
- self.toolbar.setMovable(False)
- self.actions["toggle_status"] = self.toolbar.addAction(_("Toggle Pause/Resume"))
- pricon = QIcon()
- pricon.addFile(join(pypath, "icons","toolbar_start.png"), QSize(), QIcon.Normal, QIcon.Off)
- pricon.addFile(join(pypath, "icons","toolbar_pause.png"), QSize(), QIcon.Normal, QIcon.On)
- self.actions["toggle_status"].setIcon(pricon)
- self.actions["toggle_status"].setCheckable(True)
- self.actions["status_stop"] = self.toolbar.addAction(QIcon(join(pypath, "icons","toolbar_stop.png")), _("Stop"))
- self.toolbar.addSeparator()
- self.actions["add"] = self.toolbar.addAction(QIcon(join(pypath, "icons","toolbar_add.png")), _("Add"))
- self.toolbar.addSeparator()
- self.actions["clipboard"] = self.toolbar.addAction(QIcon(join(pypath, "icons","clipboard.png")), _("Check Clipboard"))
- self.actions["clipboard"].setCheckable(True)
-
- self.connect(self.actions["toggle_status"], SIGNAL("toggled(bool)"), self.slotToggleStatus)
- self.connect(self.actions["clipboard"], SIGNAL("toggled(bool)"), self.slotToggleClipboard)
- self.connect(self.actions["status_stop"], SIGNAL("triggered()"), self.slotStatusStop)
- self.addMenu = QMenu()
- packageAction = self.addMenu.addAction(_("Package"))
- containerAction = self.addMenu.addAction(_("Container"))
- accountAction = self.addMenu.addAction(_("Account"))
- linksAction = self.addMenu.addAction(_("Links"))
- self.connect(self.actions["add"], SIGNAL("triggered()"), self.slotAdd)
- self.connect(packageAction, SIGNAL("triggered()"), self.slotShowAddPackage)
- self.connect(containerAction, SIGNAL("triggered()"), self.slotShowAddContainer)
- self.connect(accountAction, SIGNAL("triggered()"), self.slotNewAccount)
- self.connect(linksAction, SIGNAL("triggered()"), self.slotShowAddLinks)
-
- def init_tabs(self, connector):
- """
- create tabs
- """
- #overview
- self.tabs["overview"]["l"] = QGridLayout()
- self.tabs["overview"]["w"].setLayout(self.tabs["overview"]["l"])
- self.tabs["overview"]["view"] = OverviewView(connector)
- self.tabs["overview"]["l"].addWidget(self.tabs["overview"]["view"])
-
- #queue
- self.tabs["queue"]["l"] = QGridLayout()
- self.tabs["queue"]["w"].setLayout(self.tabs["queue"]["l"])
- self.tabs["queue"]["view"] = QueueView(connector)
- self.tabs["queue"]["l"].addWidget(self.tabs["queue"]["view"])
-
- #collector
- toQueue = QPushButton(_("Push selected packages to queue"))
- self.tabs["collector"]["l"] = QGridLayout()
- self.tabs["collector"]["w"].setLayout(self.tabs["collector"]["l"])
- self.tabs["collector"]["package_view"] = CollectorView(connector)
- self.tabs["collector"]["l"].addWidget(self.tabs["collector"]["package_view"], 0, 0)
- self.tabs["collector"]["l"].addWidget(toQueue, 1, 0)
- self.connect(toQueue, SIGNAL("clicked()"), self.slotPushPackageToQueue)
- self.tabs["collector"]["package_view"].setContextMenuPolicy(Qt.CustomContextMenu)
- self.tabs["queue"]["view"].setContextMenuPolicy(Qt.CustomContextMenu)
-
- #log
- self.tabs["log"]["l"] = QGridLayout()
- self.tabs["log"]["w"].setLayout(self.tabs["log"]["l"])
- self.tabs["log"]["text"] = QTextEdit()
- self.tabs["log"]["text"].logOffset = 0
- self.tabs["log"]["text"].setReadOnly(True)
- self.connect(self.tabs["log"]["text"], SIGNAL("append(QString)"), self.tabs["log"]["text"].append)
- self.tabs["log"]["l"].addWidget(self.tabs["log"]["text"])
-
- #accounts
- self.tabs["accounts"]["view"] = AccountView(connector)
- self.tabs["accounts"]["w"].setLayout(QVBoxLayout())
- self.tabs["accounts"]["w"].layout().addWidget(self.tabs["accounts"]["view"])
- newbutton = QPushButton(_("New Account"))
- self.tabs["accounts"]["w"].layout().addWidget(newbutton)
- self.connect(newbutton, SIGNAL("clicked()"), self.slotNewAccount)
- self.tabs["accounts"]["view"].setContextMenuPolicy(Qt.CustomContextMenu)
-
- def init_context(self):
- """
- create context menus
- """
- self.activeMenu = None
- #queue
- self.queueContext = QMenu()
- self.queueContext.buttons = {}
- self.queueContext.item = (None, None)
- self.queueContext.buttons["remove"] = QAction(QIcon(join(pypath, "icons","remove_small.png")), _("Remove"), self.queueContext)
- self.queueContext.buttons["restart"] = QAction(QIcon(join(pypath, "icons","refresh_small.png")), _("Restart"), self.queueContext)
- self.queueContext.buttons["pull"] = QAction(QIcon(join(pypath, "icons","pull_small.png")), _("Pull out"), self.queueContext)
- self.queueContext.buttons["abort"] = QAction(QIcon(join(pypath, "icons","abort.png")), _("Abort"), self.queueContext)
- self.queueContext.buttons["edit"] = QAction(QIcon(join(pypath, "icons","edit_small.png")), _("Edit Name"), self.queueContext)
- self.queueContext.addAction(self.queueContext.buttons["pull"])
- self.queueContext.addAction(self.queueContext.buttons["edit"])
- self.queueContext.addAction(self.queueContext.buttons["remove"])
- self.queueContext.addAction(self.queueContext.buttons["restart"])
- self.queueContext.addAction(self.queueContext.buttons["abort"])
- self.connect(self.queueContext.buttons["remove"], SIGNAL("triggered()"), self.slotRemoveDownload)
- self.connect(self.queueContext.buttons["restart"], SIGNAL("triggered()"), self.slotRestartDownload)
- self.connect(self.queueContext.buttons["pull"], SIGNAL("triggered()"), self.slotPullOutPackage)
- self.connect(self.queueContext.buttons["abort"], SIGNAL("triggered()"), self.slotAbortDownload)
- self.connect(self.queueContext.buttons["edit"], SIGNAL("triggered()"), self.slotEditPackage)
-
- #collector
- self.collectorContext = QMenu()
- self.collectorContext.buttons = {}
- self.collectorContext.item = (None, None)
- self.collectorContext.buttons["remove"] = QAction(QIcon(join(pypath, "icons","remove_small.png")), _("Remove"), self.collectorContext)
- self.collectorContext.buttons["push"] = QAction(QIcon(join(pypath, "icons","push_small.png")), _("Push to queue"), self.collectorContext)
- self.collectorContext.buttons["edit"] = QAction(QIcon(join(pypath, "icons","edit_small.png")), _("Edit Name"), self.collectorContext)
- self.collectorContext.buttons["restart"] = QAction(QIcon(join(pypath, "icons","refresh_small.png")), _("Restart"), self.collectorContext)
- self.collectorContext.buttons["refresh"] = QAction(QIcon(join(pypath, "icons","refresh1_small.png")),_("Refresh Status"), self.collectorContext)
- self.collectorContext.addAction(self.collectorContext.buttons["push"])
- self.collectorContext.addSeparator()
- self.collectorContext.buttons["add"] = self.collectorContext.addMenu(QIcon(join(pypath, "icons","add_small.png")), _("Add"))
- self.collectorContext.addAction(self.collectorContext.buttons["edit"])
- self.collectorContext.addAction(self.collectorContext.buttons["remove"])
- self.collectorContext.addAction(self.collectorContext.buttons["restart"])
- self.collectorContext.addSeparator()
- self.collectorContext.addAction(self.collectorContext.buttons["refresh"])
- packageAction = self.collectorContext.buttons["add"].addAction(_("Package"))
- containerAction = self.collectorContext.buttons["add"].addAction(_("Container"))
- linkAction = self.collectorContext.buttons["add"].addAction(_("Links"))
- self.connect(self.collectorContext.buttons["remove"], SIGNAL("triggered()"), self.slotRemoveDownload)
- self.connect(self.collectorContext.buttons["push"], SIGNAL("triggered()"), self.slotPushPackageToQueue)
- self.connect(self.collectorContext.buttons["edit"], SIGNAL("triggered()"), self.slotEditPackage)
- self.connect(self.collectorContext.buttons["restart"], SIGNAL("triggered()"), self.slotRestartDownload)
- self.connect(self.collectorContext.buttons["refresh"], SIGNAL("triggered()"), self.slotRefreshPackage)
- self.connect(packageAction, SIGNAL("triggered()"), self.slotShowAddPackage)
- self.connect(containerAction, SIGNAL("triggered()"), self.slotShowAddContainer)
- self.connect(linkAction, SIGNAL("triggered()"), self.slotShowAddLinks)
-
- self.accountContext = QMenu()
- self.accountContext.buttons = {}
- self.accountContext.buttons["add"] = QAction(QIcon(join(pypath, "icons","add_small.png")), _("Add"), self.accountContext)
- self.accountContext.buttons["remove"] = QAction(QIcon(join(pypath, "icons","remove_small.png")), _("Remove"), self.accountContext)
- self.accountContext.buttons["edit"] = QAction(QIcon(join(pypath, "icons","edit_small.png")), _("Edit"), self.accountContext)
- self.accountContext.addAction(self.accountContext.buttons["add"])
- self.accountContext.addAction(self.accountContext.buttons["edit"])
- self.accountContext.addAction(self.accountContext.buttons["remove"])
- self.connect(self.accountContext.buttons["add"], SIGNAL("triggered()"), self.slotNewAccount)
- self.connect(self.accountContext.buttons["edit"], SIGNAL("triggered()"), self.slotEditAccount)
- self.connect(self.accountContext.buttons["remove"], SIGNAL("triggered()"), self.slotRemoveAccount)
-
- def slotToggleStatus(self, status):
- """
- pause/start toggle (toolbar)
- """
- self.emit(SIGNAL("setDownloadStatus"), status)
-
- def slotStatusStop(self):
- """
- stop button (toolbar)
- """
- self.emit(SIGNAL("stopAllDownloads"))
-
- def slotAdd(self):
- """
- add button (toolbar)
- show context menu (choice: links/package)
- """
- self.addMenu.exec_(QCursor.pos())
-
- def slotShowAddPackage(self):
- """
- action from add-menu
- show new-package dock
- """
- self.tabw.setCurrentIndex(1)
- self.newPackDock.show()
-
- def slotShowAddLinks(self):
- """
- action from add-menu
- show new-links dock
- """
- self.tabw.setCurrentIndex(1)
- self.newLinkDock.show()
-
- def slotShowConnector(self):
- """
- connectionmanager action triggered
- let main to the stuff
- """
- self.emit(SIGNAL("connector"))
-
- def slotAddPackage(self, name, links, password=None):
- """
- new package
- let main to the stuff
- """
- self.emit(SIGNAL("addPackage"), name, links, password)
-
- def slotAddLinksToPackage(self, links):
- """
- adds links to currently selected package
- only in collector
- """
- if self.tabw.currentIndex() != 1:
- return
-
- smodel = self.tabs["collector"]["package_view"].selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- if isinstance(item, Package):
- self.connector.proxy.addFiles(item.id, links)
- break
-
- def slotShowAddContainer(self):
- """
- action from add-menu
- show file selector, emit upload
- """
- typeStr = ";;".join([
- _("All Container Types (%s)") % "*.dlc *.ccf *.rsdf *.txt",
- _("DLC (%s)") % "*.dlc",
- _("CCF (%s)") % "*.ccf",
- _("RSDF (%s)") % "*.rsdf",
- _("Text Files (%s)") % "*.txt"
- ])
- fileNames = QFileDialog.getOpenFileNames(self, _("Open container"), "", typeStr)
- for name in fileNames:
- self.emit(SIGNAL("addContainer"), str(name))
-
- def slotPushPackageToQueue(self):
- """
- push collector pack to queue
- get child ids
- let main to the rest
- """
- smodel = self.tabs["collector"]["package_view"].selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- if isinstance(item, Package):
- self.emit(SIGNAL("pushPackageToQueue"), item.id)
- else:
- self.emit(SIGNAL("pushPackageToQueue"), item.package.id)
-
- def saveWindow(self):
- """
- get window state/geometry
- pass data to main
- """
- state_raw = self.saveState(self.version)
- geo_raw = self.saveGeometry()
-
- state = str(state_raw.toBase64())
- geo = str(geo_raw.toBase64())
-
- self.emit(SIGNAL("saveMainWindow"), state, geo)
-
- def closeEvent(self, event):
- """
- somebody wants to close me!
- let me first save my state
- """
- self.saveWindow()
- event.ignore()
- self.hide()
- self.emit(SIGNAL("hidden"))
-
- # quit when no tray is available
- if not QSystemTrayIcon.isSystemTrayAvailable():
- self.emit(SIGNAL("Quit"))
-
- def restoreWindow(self, state, geo):
- """
- restore window state/geometry
- """
- state = QByteArray(state)
- geo = QByteArray(geo)
-
- state_raw = QByteArray.fromBase64(state)
- geo_raw = QByteArray.fromBase64(geo)
-
- self.restoreState(state_raw, self.version)
- self.restoreGeometry(geo_raw)
-
- def slotQueueContextMenu(self, pos):
- """
- custom context menu in queue view requested
- """
- globalPos = self.tabs["queue"]["view"].mapToGlobal(pos)
- i = self.tabs["queue"]["view"].indexAt(pos)
- if not i:
- return
- item = i.internalPointer()
- menuPos = QCursor.pos()
- menuPos.setX(menuPos.x()+2)
- self.activeMenu = self.queueContext
- showAbort = False
- if isinstance(item, Link) and item.data["downloading"]:
- showAbort = True
- elif isinstance(item, Package):
- for child in item.children:
- if child.data["downloading"]:
- showAbort = True
- break
- if showAbort:
- self.queueContext.buttons["abort"].setEnabled(True)
- else:
- self.queueContext.buttons["abort"].setEnabled(False)
- if isinstance(item, Package):
- self.queueContext.index = i
- #self.queueContext.buttons["remove"].setEnabled(True)
- #self.queueContext.buttons["restart"].setEnabled(True)
- self.queueContext.buttons["pull"].setEnabled(True)
- self.queueContext.buttons["edit"].setEnabled(True)
- elif isinstance(item, Link):
- self.collectorContext.index = i
- self.collectorContext.buttons["edit"].setEnabled(False)
- self.collectorContext.buttons["remove"].setEnabled(True)
- self.collectorContext.buttons["push"].setEnabled(False)
- self.collectorContext.buttons["restart"].setEnabled(True)
- else:
- self.queueContext.index = None
- #self.queueContext.buttons["remove"].setEnabled(False)
- #self.queueContext.buttons["restart"].setEnabled(False)
- self.queueContext.buttons["pull"].setEnabled(False)
- self.queueContext.buttons["edit"].setEnabled(False)
- self.queueContext.exec_(menuPos)
-
- def slotCollectorContextMenu(self, pos):
- """
- custom context menu in package collector view requested
- """
- globalPos = self.tabs["collector"]["package_view"].mapToGlobal(pos)
- i = self.tabs["collector"]["package_view"].indexAt(pos)
- if not i:
- return
- item = i.internalPointer()
- menuPos = QCursor.pos()
- menuPos.setX(menuPos.x()+2)
- self.activeMenu = self.collectorContext
- if isinstance(item, Package):
- self.collectorContext.index = i
- self.collectorContext.buttons["edit"].setEnabled(True)
- self.collectorContext.buttons["remove"].setEnabled(True)
- self.collectorContext.buttons["push"].setEnabled(True)
- self.collectorContext.buttons["restart"].setEnabled(True)
- elif isinstance(item, Link):
- self.collectorContext.index = i
- self.collectorContext.buttons["edit"].setEnabled(False)
- self.collectorContext.buttons["remove"].setEnabled(True)
- self.collectorContext.buttons["push"].setEnabled(False)
- self.collectorContext.buttons["restart"].setEnabled(True)
- else:
- self.collectorContext.index = None
- self.collectorContext.buttons["edit"].setEnabled(False)
- self.collectorContext.buttons["remove"].setEnabled(False)
- self.collectorContext.buttons["push"].setEnabled(False)
- self.collectorContext.buttons["restart"].setEnabled(False)
- self.collectorContext.exec_(menuPos)
-
- def slotLinkCollectorContextMenu(self, pos):
- """
- custom context menu in link collector view requested
- """
- pass
-
- def slotRestartDownload(self):
- """
- restart download action is triggered
- """
- smodel = self.tabs["queue"]["view"].selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- self.emit(SIGNAL("restartDownload"), item.id, isinstance(item, Package))
-
- def slotRemoveDownload(self):
- """
- remove download action is triggered
- """
- if self.activeMenu == self.queueContext:
- view = self.tabs["queue"]["view"]
- else:
- view = self.tabs["collector"]["package_view"]
- smodel = view.selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- self.emit(SIGNAL("removeDownload"), item.id, isinstance(item, Package))
-
- def slotToggleClipboard(self, status):
- """
- check clipboard (toolbar)
- """
- self.emit(SIGNAL("setClipboardStatus"), status)
-
- def slotEditPackage(self):
- # in Queue, only edit name
- if self.activeMenu == self.queueContext:
- view = self.tabs["queue"]["view"]
- else:
- view = self.tabs["collector"]["package_view"]
- view.edit(self.activeMenu.index)
-
- def slotEditCommit(self, editor):
- self.emit(SIGNAL("changePackageName"), self.activeMenu.index.internalPointer().id, editor.text())
-
- def slotPullOutPackage(self):
- """
- pull package out of the queue
- """
- smodel = self.tabs["queue"]["view"].selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- if isinstance(item, Package):
- self.emit(SIGNAL("pullOutPackage"), item.id)
- else:
- self.emit(SIGNAL("pullOutPackage"), item.package.id)
-
- def slotAbortDownload(self):
- view = self.tabs["queue"]["view"]
- smodel = view.selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- self.emit(SIGNAL("abortDownload"), item.id, isinstance(item, Package))
-
- # TODO disabled because changing desktop on linux, main window disappears
- #def changeEvent(self, e):
- # if e.type() == QEvent.WindowStateChange and self.isMinimized():
- # e.ignore()
- # self.hide()
- # self.emit(SIGNAL("hidden"))
- # else:
- # super(MainWindow, self).changeEvent(e)
-
- def slotTabChanged(self, index):
- if index == 2:
- self.emit(SIGNAL("reloadAccounts"))
- elif index == 3:
- self.tabs["settings"]["w"].loadConfig()
-
- def slotRefreshPackage(self):
- smodel = self.tabs["collector"]["package_view"].selectionModel()
- for index in smodel.selectedRows(0):
- item = index.internalPointer()
- pid = item.id
- if isinstance(item, Link):
- pid = item.package.id
- self.emit(SIGNAL("refreshStatus"), pid)
-
- def slotNewAccount(self):
- types = self.connector.proxy.getAccountTypes()
- self.accountEdit = AccountEdit.newAccount(types)
-
- #TODO make more easy n1, n2, n3
- def save(data):
- if data["password"]:
- self.accountEdit.close()
- n1 = data["acctype"]
- n2 = data["login"]
- n3 = data["password"]
- self.connector.updateAccount(n1, n2, n3, None)
-
- self.accountEdit.connect(self.accountEdit, SIGNAL("done"), save)
- self.accountEdit.show()
-
- def slotEditAccount(self):
- types = self.connector.getAccountTypes()
-
- data = self.tabs["accounts"]["view"].selectedIndexes()
- if len(data) < 1:
- return
-
- data = data[0].internalPointer()
-
- self.accountEdit = AccountEdit.editAccount(types, data)
-
- #TODO make more easy n1, n2, n3
- #TODO reload accounts tab after insert of edit account
- #TODO if account does not exist give error
- def save(data):
- self.accountEdit.close()
- n1 = data["acctype"]
- n2 = data["login"]
- if data["password"]:
- n3 = data["password"]
- self.connector.updateAccount(n1, n2, n3, None)
-
- self.accountEdit.connect(self.accountEdit, SIGNAL("done"), save)
- self.accountEdit.show()
-
- def slotRemoveAccount(self):
- data = self.tabs["accounts"]["view"].selectedIndexes()
- if len(data) < 1:
- return
-
- data = data[0].internalPointer()
-
- self.connector.removeAccount(data.type, data.login)
-
- def slotAccountContextMenu(self, pos):
- globalPos = self.tabs["accounts"]["view"].mapToGlobal(pos)
- i = self.tabs["accounts"]["view"].indexAt(pos)
- if not i:
- return
-
- data = i.internalPointer()
-
- if data is None:
- self.accountContext.buttons["edit"].setEnabled(False)
- self.accountContext.buttons["remove"].setEnabled(False)
- else:
- self.accountContext.buttons["edit"].setEnabled(True)
- self.accountContext.buttons["remove"].setEnabled(True)
-
- menuPos = QCursor.pos()
- menuPos.setX(menuPos.x()+2)
- self.accountContext.exec_(menuPos)
diff --git a/module/gui/Overview.py b/module/gui/Overview.py
deleted file mode 100644
index 183383b5e..000000000
--- a/module/gui/Overview.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from module.utils import formatSpeed, formatSize
-
-class OverviewModel(QAbstractListModel):
- PackageName = 10
- Progress = 11
- PartsFinished = 12
- Parts = 13
- ETA = 14
- Speed = 15
- CurrentSize = 16
- MaxSize = 17
- Status = 18
-
- def __init__(self, view, connector):
- QAbstractListModel.__init__(self)
-
- self.packages = []
-
- def queueChanged(self): #dirty..
- self.beginResetModel()
-
- self.packages = []
-
- def partsFinished(p):
- f = 0
- for c in p.children:
- if c.data["status"] == 0:
- f += 1
- return f
-
- def maxSize(p):
- ms = 0
- cs = 0
- for c in p.children:
- try:
- s = c.data["downloading"]["size"]
- except:
- s = c.data["size"]
- if c.data["downloading"]:
- cs += s - c.data["downloading"]["bleft"]
- elif self.queue.getProgress(c, False) == 100:
- cs += s
- ms += s
- return ms, cs
-
- def getProgress(p):
- for c in p.children:
- if c.data["status"] == 13:
- pass # TODO return _("Unpacking"), int(c.data["progress"])
- return _("Downloading"), self.queue.getProgress(p)
-
- d = self.queue._data
- for p in d:
- status, progress = getProgress(p)
- maxsize, currentsize = maxSize(p)
- speed = self.queue.getSpeed(p)
- if speed:
- eta = (maxsize - (maxsize * (progress/100.0)))/speed
- else:
- eta = 0
- if not speed and not progress:
- status = _("Queued")
- info = {
- OverviewModel.PackageName: p.data["name"],
- OverviewModel.Progress: progress,
- OverviewModel.PartsFinished: partsFinished(p),
- OverviewModel.Parts: len(p.children),
- OverviewModel.ETA: int(eta),
- OverviewModel.Speed: speed,
- OverviewModel.CurrentSize: currentsize,
- OverviewModel.MaxSize: maxsize,
- OverviewModel.Status: status,
- }
-
- self.packages.append(info)
-
- self.endResetModel()
-
- def headerData(self, section, orientation, role=Qt.DisplayRole):
- return QVariant(_("Package"))
-
- def rowCount(self, parent=QModelIndex()):
- return len(self.packages)
-
- def data(self, index, role=Qt.DisplayRole):
- if role in [OverviewModel.PackageName, OverviewModel.Progress, OverviewModel.PartsFinished, OverviewModel.Parts, OverviewModel.ETA, OverviewModel.Speed, OverviewModel.CurrentSize, OverviewModel.MaxSize, OverviewModel.Status]:
- return QVariant(self.packages[index.row()][role])
- return QVariant()
-
-class OverviewView(QListView):
- def __init__(self, connector):
- QListView.__init__(self)
- self.setModel(OverviewModel(self, connector))
-
- self.setAlternatingRowColors(True)
- self.delegate = OverviewDelegate(self)
- self.setItemDelegate(self.delegate)
-
-class OverviewDelegate(QItemDelegate):
- def __init__(self, parent):
- QItemDelegate.__init__(self, parent)
- self.parent = parent
- self.model = parent.model()
-
- def paint(self, painter, option, index):
- option.rect.setHeight(59+16)
- option.rect.setWidth(self.parent.width()-20)
-
- #if option.state & QStyle.State_Selected:
- # painter.fillRect(option.rect, option.palette.color(QPalette.Highlight))
-
- packagename = index.data(OverviewModel.PackageName).toString()
- partsf = index.data(OverviewModel.PartsFinished).toString()
- parts = index.data(OverviewModel.Parts).toString()
- eta = int(index.data(OverviewModel.ETA).toString())
- speed = index.data(OverviewModel.Speed).toString() or 0
- progress = int(index.data(OverviewModel.Progress).toString())
- currentSize = int(index.data(OverviewModel.CurrentSize).toString())
- maxSize = int(index.data(OverviewModel.MaxSize).toString())
- status = index.data(OverviewModel.Status).toString()
-
- def formatEta(seconds): #TODO add to utils
- if seconds <= 0: return ""
- hours, seconds = divmod(seconds, 3600)
- minutes, seconds = divmod(seconds, 60)
- return _("ETA: ") + "%.2i:%.2i:%.2i" % (hours, minutes, seconds)
-
- statusline = QString(_("Parts: ") + "%s/%s" % (partsf, parts))
- if partsf == parts:
- speedline = _("Finished")
- elif not status == _("Downloading"):
- speedline = QString(status)
- else:
- speedline = QString(formatEta(eta) + " " + _("Speed: %s") % formatSpeed(speed))
-
- if progress in (0,100):
- sizeline = QString(_("Size:") + "%s" % formatSize(maxSize))
- else:
- sizeline = QString(_("Size:") + "%s / %s" % (formatSize(currentSize), formatSize(maxSize)))
-
- f = painter.font()
- f.setPointSize(12)
- f.setBold(True)
- painter.setFont(f)
-
- r = option.rect.adjusted(4, 4, -4, -4)
- painter.drawText(r.left(), r.top(), r.width(), r.height(), Qt.AlignTop | Qt.AlignLeft, packagename)
- newr = painter.boundingRect(r.left(), r.top(), r.width(), r.height(), Qt.AlignTop | Qt.AlignLeft, packagename)
-
- f.setPointSize(10)
- f.setBold(False)
- painter.setFont(f)
-
- painter.drawText(r.left(), newr.bottom()+5, r.width(), r.height(), Qt.AlignTop | Qt.AlignLeft, statusline)
- painter.drawText(r.left(), newr.bottom()+5, r.width(), r.height(), Qt.AlignTop | Qt.AlignHCenter, sizeline)
- painter.drawText(r.left(), newr.bottom()+5, r.width(), r.height(), Qt.AlignTop | Qt.AlignRight, speedline)
- newr = painter.boundingRect(r.left(), newr.bottom()+2, r.width(), r.height(), Qt.AlignTop | Qt.AlignLeft, statusline)
- newr.setTop(newr.bottom()+8)
- newr.setBottom(newr.top()+20)
- newr.setRight(self.parent.width()-25)
-
- f.setPointSize(10)
- painter.setFont(f)
-
- opts = QStyleOptionProgressBarV2()
- opts.maximum = 100
- opts.minimum = 0
- opts.progress = progress
- opts.rect = newr
- opts.textVisible = True
- opts.textAlignment = Qt.AlignCenter
- opts.text = QString.number(opts.progress) + "%"
- QApplication.style().drawControl(QStyle.CE_ProgressBar, opts, painter)
-
- def sizeHint(self, option, index):
- return QSize(self.parent.width()-22, 59+16)
diff --git a/module/gui/PackageDock.py b/module/gui/PackageDock.py
deleted file mode 100644
index 73db8f177..000000000
--- a/module/gui/PackageDock.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# -*- 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: mkaay
-"""
-import re
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-class NewPackageDock(QDockWidget):
- def __init__(self):
- QDockWidget.__init__(self, _("New Package"))
- self.setObjectName("New Package Dock")
- self.widget = NewPackageWindow(self)
- self.setWidget(self.widget)
- self.setAllowedAreas(Qt.RightDockWidgetArea|Qt.LeftDockWidgetArea)
- self.hide()
-
- def slotDone(self):
- text = str(self.widget.box.toPlainText())
- pw = str(self.widget.passwordInput.text())
- if not pw:
- pw = None
- lines = []
- for line in text.splitlines():
- line = line.strip()
- if not line:
- continue
- lines.append(line)
- self.emit(SIGNAL("done"), str(self.widget.nameInput.text()), lines, pw)
- self.widget.nameInput.setText("")
- self.widget.passwordInput.setText("")
- self.widget.box.clear()
- self.hide()
-
- def parseUri(self):
-
- text=str(self.widget.box.toPlainText())
- self.widget.box.setText("")
- result = re.findall(r"(?:ht|f)tps?:\/\/[a-zA-Z0-9\-\.\/\?=_&%#]+[<| |\"|\'|\r|\n|\t]{1}", text)
- for url in result:
- if "\n" or "\t" or "\r" or "\"" or "<" or "'" in url:
- url = url[:-1]
- self.widget.box.append("%s " % url)
-
-class NewPackageWindow(QWidget):
- def __init__(self, dock):
- QWidget.__init__(self)
- self.dock = dock
- self.setLayout(QGridLayout())
- layout = self.layout()
-
- nameLabel = QLabel(_("Name"))
- nameInput = QLineEdit()
- passwordLabel = QLabel(_("Password"))
- passwordInput = QLineEdit()
-
- linksLabel = QLabel(_("Links in this Package"))
-
- self.box = QTextEdit()
- self.nameInput = nameInput
- self.passwordInput = passwordInput
-
- save = QPushButton(_("Create"))
- parseUri = QPushButton(_("Filter URLs"))
-
- layout.addWidget(nameLabel, 0, 0)
- layout.addWidget(nameInput, 0, 1)
- layout.addWidget(passwordLabel, 1, 0)
- layout.addWidget(passwordInput, 1, 1)
- layout.addWidget(linksLabel, 2, 0, 1, 2)
- layout.addWidget(self.box, 3, 0, 1, 2)
- layout.addWidget(parseUri, 4, 0, 1, 2)
- layout.addWidget(save, 5, 0, 1, 2)
-
- self.connect(save, SIGNAL("clicked()"), self.dock.slotDone)
- self.connect(parseUri, SIGNAL("clicked()"), self.dock.parseUri) \ No newline at end of file
diff --git a/module/gui/Queue.py b/module/gui/Queue.py
deleted file mode 100644
index 0a0cbb810..000000000
--- a/module/gui/Queue.py
+++ /dev/null
@@ -1,390 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-from time import time
-
-from module.remote.thriftbackend.ThriftClient import Destination
-from module.gui.Collector import CollectorModel, Package, Link, CollectorView, statusMapReverse
-from module.utils import formatSize, formatSpeed
-
-class QueueModel(CollectorModel):
- """
- model for the queue view, inherits from CollectorModel
- """
-
- def __init__(self, view, connector):
- CollectorModel.__init__(self, view, connector)
- self.cols = 6
- self.wait_dict = {}
-
- self.updater = self.QueueUpdater(self.interval)
- self.connect(self.updater, SIGNAL("update()"), self.update)
-
- class QueueUpdater(QObject):
- """
- timer which emits signal for a download status reload
- @TODO: make intervall configurable
- """
-
- def __init__(self, interval):
- QObject.__init__(self)
-
- self.interval = interval
- self.timer = QTimer()
- self.timer.connect(self.timer, SIGNAL("timeout()"), self, SIGNAL("update()"))
-
- def start(self):
- self.timer.start(1000)
-
- def stop(self):
- self.timer.stop()
-
- def start(self):
- self.updater.start()
-
- def stop(self):
- self.updater.stop()
-
- def fullReload(self):
- """
- reimplements CollectorModel.fullReload, because we want the Queue data
- """
- self._data = []
- order = self.connector.getPackageOrder(Destination.Queue)
- self.beginInsertRows(QModelIndex(), 0, len(order.values()))
- for position, pid in order.iteritems():
- pack = self.connector.getPackageData(pid)
- package = Package(pack)
- self._data.append(package)
- self._data = sorted(self._data, key=lambda p: p.data["order"])
- self.endInsertRows()
- self.updateCount()
-
- def insertEvent(self, event):
- """
- wrap CollectorModel.insertEvent to update the element count
- """
- CollectorModel.insertEvent(self, event)
- self.updateCount()
-
- def removeEvent(self, event):
- """
- wrap CollectorModel.removeEvent to update the element count
- """
- CollectorModel.removeEvent(self, event)
- self.updateCount()
-
- def updateEvent(self, event):
- """
- wrap CollectorModel.updateEvent to update the element count
- """
- CollectorModel.updateEvent(self, event)
- self.updateCount()
-
- def updateCount(self):
- """
- calculate package- and filecount for statusbar,
- ugly?: Overview connects to this signal for updating
- """
- packageCount = len(self._data)
- fileCount = 0
- for p in self._data:
- fileCount += len(p.children)
- self.mutex.unlock()
- self.emit(SIGNAL("updateCount"), packageCount, fileCount)
- self.mutex.lock()
-
- def update(self):
- """
- update slot for download status updating
- """
- locker = QMutexLocker(self.mutex)
- downloading = self.connector.statusDownloads()
- if not downloading:
- return
- for p, pack in enumerate(self._data):
- for d in downloading:
- child = pack.getChild(d.fid)
- if child:
- dd = {
- "name": d.name,
- "speed": d.speed,
- "eta": d.eta,
- "format_eta": d.format_eta,
- "bleft": d.bleft,
- "size": d.size,
- "format_size": d.format_size,
- "percent": d.percent,
- "status": d.status,
- "statusmsg": d.statusmsg,
- "format_wait": d.format_wait,
- "wait_until": d.wait_until
- }
- child.data["downloading"] = dd
- k = pack.getChildKey(d.fid)
- self.emit(SIGNAL("dataChanged(const QModelIndex &, const QModelIndex &)"), self.index(k, 0, self.index(p, 0)), self.index(k, self.cols, self.index(p, self.cols)))
- self.updateCount()
-
- def headerData(self, section, orientation, role=Qt.DisplayRole):
- """
- returns column heading
- """
- if orientation == Qt.Horizontal and role == Qt.DisplayRole:
- if section == 0:
- return QVariant(_("Name"))
- elif section == 2:
- return QVariant(_("Status"))
- elif section == 1:
- return QVariant(_("Plugin"))
- elif section == 3:
- return QVariant(_("Size"))
- elif section == 4:
- return QVariant(_("ETA"))
- elif section == 5:
- return QVariant(_("Progress"))
- return QVariant()
-
- def getWaitingProgress(self, item):
- """
- returns time to wait, caches startingtime to provide progress
- """
- locker = QMutexLocker(self.mutex)
- if isinstance(item, Link):
- if item.data["status"] == 5 and item.data["downloading"]:
- until = float(item.data["downloading"]["wait_until"])
- try:
- since, until_old = self.wait_dict[item.id]
- if not until == until_old:
- raise Exception
- except:
- since = time()
- self.wait_dict[item.id] = since, until
- since = float(since)
- max_wait = float(until-since)
- rest = int(until-time())
- if rest < 0:
- return 0, None
- res = 100/max_wait
- perc = rest*res
- return perc, rest
- return None
-
- def getProgress(self, item, locked=True):
- """
- return download progress, locks by default
- since it's used in already locked calls,
- it provides an option to not lock
- """
- if locked:
- locker = QMutexLocker(self.mutex)
- if isinstance(item, Link):
- try:
- if item.data["status"] == 0:
- return 100
- return int(item.data["downloading"]["percent"])
- except:
- return 0
- elif isinstance(item, Package):
- count = len(item.children)
- perc_sum = 0
- for child in item.children:
- try:
- if child.data["status"] == 0: #completed
- perc_sum += 100
- perc_sum += int(child.data["downloading"]["percent"])
- except:
- pass
- if count == 0:
- return 0
- return perc_sum/count
- return 0
-
- def getSpeed(self, item):
- """
- calculate download speed
- """
- if isinstance(item, Link):
- if item.data["downloading"]:
- return int(item.data["downloading"]["speed"])
- elif isinstance(item, Package):
- count = len(item.children)
- speed_sum = 0
- all_waiting = True
- running = False
- for child in item.children:
- val = 0
- if child.data["downloading"]:
- if not child.data["statusmsg"] == "waiting":
- all_waiting = False
- val = int(child.data["downloading"]["speed"])
- running = True
- speed_sum += val
- if count == 0 or not running or all_waiting:
- return None
- return speed_sum
- return None
-
- def data(self, index, role=Qt.DisplayRole):
- """
- return cell data
- """
- if not index.isValid():
- return QVariant()
- if role == Qt.DisplayRole:
- if index.column() == 0:
- return QVariant(index.internalPointer().data["name"])
- elif index.column() == 1:
- item = index.internalPointer()
- plugins = []
- if isinstance(item, Package):
- for child in item.children:
- if not child.data["plugin"] in plugins:
- plugins.append(child.data["plugin"])
- else:
- plugins.append(item.data["plugin"])
- return QVariant(", ".join(plugins))
- elif index.column() == 2:
- item = index.internalPointer()
- status = 0
- speed = self.getSpeed(item)
- if isinstance(item, Package):
- for child in item.children:
- if child.data["status"] > status:
- status = child.data["status"]
- else:
- status = item.data["status"]
-
- if speed is None or status == 7 or status == 10 or status == 5:
- return QVariant(self.translateStatus(statusMapReverse[status]))
- else:
- return QVariant("%s (%s)" % (self.translateStatus(statusMapReverse[status]), formatSpeed(speed)))
- elif index.column() == 3:
- item = index.internalPointer()
- if isinstance(item, Link):
- if item.data["status"] == 0: #TODO needs change??
- #self.getProgress(item, False) == 100:
- return QVariant(formatSize(item.data["size"]))
- elif self.getProgress(item, False) == 0:
- try:
- return QVariant("%s / %s" % (formatSize(item.data["size"]-item.data["downloading"]["bleft"]), formatSize(item.data["size"])))
- except:
- return QVariant("0 B / %s" % formatSize(item.data["size"]))
- else:
- try:
- return QVariant("%s / %s" % (formatSize(item.data["size"]-item.data["downloading"]["bleft"]), formatSize(item.data["size"])))
- except:
- return QVariant("? / %s" % formatSize(item.data["size"]))
- else:
- ms = 0
- cs = 0
- for c in item.children:
- try:
- s = c.data["downloading"]["size"]
- except:
- s = c.data["size"]
- if c.data["downloading"]:
- cs += s - c.data["downloading"]["bleft"]
- elif self.getProgress(c, False) == 100:
- cs += s
- ms += s
- if cs == 0 or cs == ms:
- return QVariant(formatSize(ms))
- else:
- return QVariant("%s / %s" % (formatSize(cs), formatSize(ms)))
- elif index.column() == 4:
- item = index.internalPointer()
- if isinstance(item, Link):
- if item.data["downloading"]:
- return QVariant(item.data["downloading"]["format_eta"])
- elif role == Qt.EditRole:
- if index.column() == 0:
- return QVariant(index.internalPointer().data["name"])
- return QVariant()
-
- def flags(self, index):
- """
- cell flags
- """
- if index.column() == 0 and self.parent(index) == QModelIndex():
- return Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled
- return Qt.ItemIsSelectable | Qt.ItemIsEnabled
-
-class QueueView(CollectorView):
- """
- view component for queue
- """
-
- def __init__(self, connector):
- CollectorView.__init__(self, connector)
- self.setModel(QueueModel(self, connector))
-
- self.setColumnWidth(0, 300)
- self.setColumnWidth(1, 100)
- self.setColumnWidth(2, 140)
- self.setColumnWidth(3, 180)
- self.setColumnWidth(4, 70)
-
- self.setEditTriggers(QAbstractItemView.NoEditTriggers)
-
- self.delegate = QueueProgressBarDelegate(self, self.model())
- self.setItemDelegateForColumn(5, self.delegate)
-
-class QueueProgressBarDelegate(QItemDelegate):
- """
- used to display a progressbar in the progress cell
- """
-
- def __init__(self, parent, queue):
- QItemDelegate.__init__(self, parent)
- self.queue = queue
-
- def paint(self, painter, option, index):
- """
- paint the progressbar
- """
- if not index.isValid():
- return
- if index.column() == 5:
- item = index.internalPointer()
- w = self.queue.getWaitingProgress(item)
- wait = None
- if w:
- progress = w[0]
- wait = w[1]
- else:
- progress = self.queue.getProgress(item)
- opts = QStyleOptionProgressBarV2()
- opts.maximum = 100
- opts.minimum = 0
- opts.progress = progress
- opts.rect = option.rect
- opts.rect.setRight(option.rect.right()-1)
- opts.rect.setHeight(option.rect.height()-1)
- opts.textVisible = True
- opts.textAlignment = Qt.AlignCenter
- if not wait is None:
- opts.text = QString(_("waiting %d seconds") % (wait,))
- else:
- opts.text = QString.number(opts.progress) + "%"
- QApplication.style().drawControl(QStyle.CE_ProgressBar, opts, painter)
- return
- QItemDelegate.paint(self, painter, option, index)
-
diff --git a/module/gui/SettingsWidget.py b/module/gui/SettingsWidget.py
deleted file mode 100644
index cd22a7b9e..000000000
--- a/module/gui/SettingsWidget.py
+++ /dev/null
@@ -1,202 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-from sip import delete
-
-
-class SettingsWidget(QWidget):
- def __init__(self):
- QWidget.__init__(self)
- self.connector = None
- self.sections = {}
- self.psections = {}
- self.data = None
- self.pdata = None
- self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
-
- def setConnector(self, connector):
- self.connector = connector
-
- def loadConfig(self):
- if self.sections and self.psections:
- self.data = self.connector.getConfig()
- self.pdata = self.connector.getPluginConfig()
-
- self.reloadSection(self.sections, self.data)
- self.reloadSection(self.psections, self.pdata)
-
- return
-
- if self.layout():
- delete(self.layout())
-
- for s in self.sections.values()+self.psections.values():
- delete(s)
-
- self.sections = {}
- self.setLayout(QVBoxLayout())
- self.clearConfig()
- layout = self.layout()
- layout.setSizeConstraint(QLayout.SetMinAndMaxSize)
-
- general = QTabWidget()
- self.general = general
-
- plugins = QTabWidget()
- self.plugins = plugins
-
- tab = QTabWidget()
- self.tab = tab
-
- gw = QWidget()
- gw.setLayout(QVBoxLayout())
- gw.layout().addWidget(self.general)
- pw = QWidget()
- pw.setLayout(QVBoxLayout())
- pw.layout().addWidget(self.plugins)
- tab.addTab(gw, _("General"))
- tab.addTab(pw, _("Plugins"))
-
- layout.addWidget(tab)
-
- self.data = self.connector.getConfig()
- self.pdata = self.connector.getPluginConfig()
- for k, section in self.data.iteritems():
- s = Section(section, general)
- self.sections[k] = s
-
- for k, section in self.pdata.iteritems():
- s = Section(section, plugins, "plugin")
- self.psections[k] = s
-
- rel = QPushButton(_("Reload"))
- save = QPushButton(_("Save"))
-
- layout.addWidget(save)
-
- cont = QHBoxLayout()
- cont.addWidget(rel)
- cont.addWidget(save)
-
- layout.addLayout(cont)
-
- self.connect(save, SIGNAL("clicked()"), self.saveConfig)
- self.connect(rel, SIGNAL("clicked()"), self.loadConfig)
-
- def clearConfig(self):
- self.sections = {}
-
- def reloadSection(self, sections, pdata):
-
- for k, section in enumerate(pdata):
- if k in sections:
- widget = sections[k]
- for item in section.items:
- if item.name in widget.inputs:
- i = widget.inputs[item.name]
-
- if item.type == "int":
- i.setValue(int(item.value))
- elif not item.type.find(";") == -1:
- i.setCurrentIndex(i.findText(item.value))
- elif item.type == "bool":
- if True if item.value.lower() in ("1","true", "on", "an","yes") else False:
- i.setCurrentIndex(0)
- else:
- i.setCurrentIndex(1)
- else:
- i.setText(item.value)
-
-
- def saveConfig(self):
- self.data = self.connector.getConfig()
- self.pdata = self.connector.getPluginConfig()
-
- self.saveSection(self.sections, self.data)
- self.saveSection(self.psections, self.pdata, "plugin")
-
-
- def saveSection(self, sections, pdata, sec="core"):
- for k, section in enumerate(pdata):
- if k in sections:
- widget = sections[k]
- for item in section.items:
- if item.name in widget.inputs:
- i = widget.inputs[item.name]
-
- #TODO : unresolved reference: option
-
- if item.type == "int":
- if i.value() != int(item.value):
- self.connector.setConfigValue(k, option, i.value(), sec)
- elif not item.type.find(";") == -1:
- if i.currentText() != item.value:
- self.connector.setConfigValue(k, option, i.currentText(), sec)
- elif item.type == "bool":
- if (True if item.value.lower() in ("1","true", "on", "an","yes") else False) ^ (not i.currentIndex()):
- self.connector.setConfigValue(k, option, not i.currentIndex(), sec)
- else:
- if i.text() != item.value:
- self.connector.setConfigValue(k, option, str(i.text()), sec)
-
-class Section(QGroupBox):
- def __init__(self, data, parent, ctype="core"):
- self.data = data
- QGroupBox.__init__(self, data.description, parent)
- self.labels = {}
- self.inputs = {}
- self.ctype = ctype
- layout = QFormLayout(self)
- self.setLayout(layout)
-
- sw = QWidget()
- sw.setLayout(QVBoxLayout())
- sw.layout().addWidget(self)
-
- sa = QScrollArea()
- sa.setWidgetResizable(True)
- sa.setWidget(sw)
- sa.setFrameShape(sa.NoFrame)
-
- parent.addTab(sa, data.description)
-
- for option in self.data.items:
- if option.type == "int":
- i = QSpinBox(self)
- i.setMaximum(999999)
- i.setValue(int(option.value))
- elif not option.type.find(";") == -1:
- choices = option.type.split(";")
- i = QComboBox(self)
- i.addItems(choices)
- i.setCurrentIndex(i.findText(option.value))
- elif option.type == "bool":
- i = QComboBox(self)
- i.addItem(_("Yes"), QVariant(True))
- i.addItem(_("No"), QVariant(False))
- if True if option.value.lower() in ("1","true", "on", "an","yes") else False:
- i.setCurrentIndex(0)
- else:
- i.setCurrentIndex(1)
- else:
- i = QLineEdit(self)
- i.setText(option.value)
- layout.addRow(option.description, i)
- layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow)
diff --git a/module/gui/XMLParser.py b/module/gui/XMLParser.py
deleted file mode 100644
index 5e3b7bf65..000000000
--- a/module/gui/XMLParser.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# -*- 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: mkaay
-"""
-from __future__ import with_statement
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-from PyQt4.QtXml import *
-
-import os
-
-class XMLParser():
- def __init__(self, data, dfile=""):
- self.mutex = QMutex()
- self.mutex.lock()
- self.xml = QDomDocument()
- self.file = data
- self.dfile = dfile
- self.mutex.unlock()
- self.loadData()
- self.root = self.xml.documentElement()
-
- def loadData(self):
- self.mutex.lock()
- f = self.file
- if not os.path.exists(f):
- f = self.dfile
- with open(f, 'r') as fh:
- content = fh.read()
- self.xml.setContent(content)
- self.mutex.unlock()
-
- def saveData(self):
- self.mutex.lock()
- content = self.xml.toString()
- with open(self.file, 'w') as fh:
- fh.write(content)
- self.mutex.unlock()
- return content
-
- def parseNode(self, node, ret_type="list"):
- if ret_type == "dict":
- childNodes = {}
- else:
- childNodes = []
- child = node.firstChild()
- while True:
- n = child.toElement()
- if n.isNull():
- break
- else:
- if ret_type == "dict":
- childNodes[str(n.tagName())] = n
- else:
- childNodes.append(n)
- child = child.nextSibling()
- return childNodes
diff --git a/module/gui/__init__.py b/module/gui/__init__.py
deleted file mode 100644
index 8d1c8b69c..000000000
--- a/module/gui/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/module/gui/connector.py b/module/gui/connector.py
deleted file mode 100644
index c16ccd08e..000000000
--- a/module/gui/connector.py
+++ /dev/null
@@ -1,165 +0,0 @@
-# -*- 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: mkaay
-"""
-
-SERVER_VERSION = "0.4.9"
-
-from time import sleep
-from uuid import uuid4 as uuid
-
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-
-import socket
-
-from module.remote.thriftbackend.ThriftClient import ThriftClient, WrongLogin, NoSSL, NoConnection
-from thrift.Thrift import TException
-
-class Connector(QObject):
- """
- manages the connection to the pyload core via thrift
- """
-
- firstAttempt = True
-
- def __init__(self):
- QObject.__init__(self)
- self.mutex = QMutex()
- self.connectionID = None
- self.host = None
- self.port = None
- self.user = None
- self.password = None
- self.ssl = None
- self.running = True
- self.internal = False
- self.proxy = self.Dummy()
-
- def setConnectionData(self, host, port, user, password, ssl=False):
- """
- set connection data for connection attempt, called from slotConnect
- """
- self.host = host
- self.port = port
- self.user = user
- self.password = password
- self.ssl = ssl
-
- def connectProxy(self):
- """
- initialize thrift rpc client,
- check for ssl, check auth,
- setup dispatcher,
- connect error signals,
- check server version
- """
- if self.internal: return True
-
- err = None
- try:
- client = ThriftClient(self.host, self.port, self.user, self.password)
- except WrongLogin:
- err = _("bad login credentials")
- except NoSSL:
- err = _("no ssl support")
- except NoConnection:
- err = _("can't connect to host")
- if err:
- if not Connector.firstAttempt:
- self.emit(SIGNAL("errorBox"), err)
- Connector.firstAttempt = False
- return False
-
- self.proxy = DispatchRPC(self.mutex, client)
- self.connect(self.proxy, SIGNAL("connectionLost"), self, SIGNAL("connectionLost"))
-
- server_version = self.proxy.getServerVersion()
- self.connectionID = uuid().hex
-
- if not server_version == SERVER_VERSION:
- self.emit(SIGNAL("errorBox"), _("server is version %(new)s client accepts version %(current)s") % { "new": server_version, "current": SERVER_VERSION})
- return False
-
- return True
-
- def __getattr__(self, attr):
- """
- redirect rpc calls to dispatcher
- """
- return getattr(self.proxy, attr)
-
- class Dummy(object):
- """
- dummy rpc proxy, to prevent errors
- """
- def __nonzero__(self):
- return False
-
- def __getattr__(self, attr):
- def dummy(*args, **kwargs):
- return None
- return dummy
-
-class DispatchRPC(QObject):
- """
- wraps the thrift client, to catch critical exceptions (connection lost)
- adds thread safety
- """
-
- def __init__(self, mutex, server):
- QObject.__init__(self)
- self.mutex = mutex
- self.server = server
-
- def __getattr__(self, attr):
- """
- redirect and wrap call in Wrapper instance, locks dispatcher
- """
- self.mutex.lock()
- self.fname = attr
- f = self.Wrapper(getattr(self.server, attr), self.mutex, self)
- return f
-
- class Wrapper(object):
- """
- represents a rpc call
- """
-
- def __init__(self, f, mutex, dispatcher):
- self.f = f
- self.mutex = mutex
- self.dispatcher = dispatcher
-
- def __call__(self, *args, **kwargs):
- """
- instance is called, rpc is executed
- exceptions are processed
- finally dispatcher is unlocked
- """
- lost = False
- try:
- return self.f(*args, **kwargs)
- except socket.error: #necessary?
- lost = True
- except TException:
- lost = True
- finally:
- self.mutex.unlock()
- if lost:
- from traceback import print_exc
- print_exc()
- self.dispatcher.emit(SIGNAL("connectionLost"))
diff --git a/module/lib/BeautifulSoup.py b/module/lib/BeautifulSoup.py
deleted file mode 100644
index 55567f588..000000000
--- a/module/lib/BeautifulSoup.py
+++ /dev/null
@@ -1,2012 +0,0 @@
-"""Beautiful Soup
-Elixir and Tonic
-"The Screen-Scraper's Friend"
-http://www.crummy.com/software/BeautifulSoup/
-
-Beautiful Soup parses a (possibly invalid) XML or HTML document into a
-tree representation. It provides methods and Pythonic idioms that make
-it easy to navigate, search, and modify the tree.
-
-A well-formed XML/HTML document yields a well-formed data
-structure. An ill-formed XML/HTML document yields a correspondingly
-ill-formed data structure. If your document is only locally
-well-formed, you can use this library to find and process the
-well-formed part of it.
-
-Beautiful Soup works with Python 2.2 and up. It has no external
-dependencies, but you'll have more success at converting data to UTF-8
-if you also install these three packages:
-
-* chardet, for auto-detecting character encodings
- http://chardet.feedparser.org/
-* cjkcodecs and iconv_codec, which add more encodings to the ones supported
- by stock Python.
- http://cjkpython.i18n.org/
-
-Beautiful Soup defines classes for two main parsing strategies:
-
- * BeautifulStoneSoup, for parsing XML, SGML, or your domain-specific
- language that kind of looks like XML.
-
- * BeautifulSoup, for parsing run-of-the-mill HTML code, be it valid
- or invalid. This class has web browser-like heuristics for
- obtaining a sensible parse tree in the face of common HTML errors.
-
-Beautiful Soup also defines a class (UnicodeDammit) for autodetecting
-the encoding of an HTML or XML document, and converting it to
-Unicode. Much of this code is taken from Mark Pilgrim's Universal Feed Parser.
-
-For more than you ever wanted to know about Beautiful Soup, see the
-documentation:
-http://www.crummy.com/software/BeautifulSoup/documentation.html
-
-Here, have some legalese:
-
-Copyright (c) 2004-2010, Leonard Richardson
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials provided
- with the distribution.
-
- * Neither the name of the the Beautiful Soup Consortium and All
- Night Kosher Bakery nor the names of its contributors may be
- used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
-CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE, DAMMIT.
-
-"""
-from __future__ import generators
-
-__author__ = "Leonard Richardson (leonardr@segfault.org)"
-__version__ = "3.0.8.1"
-__copyright__ = "Copyright (c) 2004-2010 Leonard Richardson"
-__license__ = "New-style BSD"
-
-from sgmllib import SGMLParser, SGMLParseError
-import codecs
-import markupbase
-import types
-import re
-import sgmllib
-try:
- from htmlentitydefs import name2codepoint
-except ImportError:
- name2codepoint = {}
-try:
- set
-except NameError:
- from sets import Set as set
-
-#These hacks make Beautiful Soup able to parse XML with namespaces
-sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*')
-markupbase._declname_match = re.compile(r'[a-zA-Z][-_.:a-zA-Z0-9]*\s*').match
-
-DEFAULT_OUTPUT_ENCODING = "utf-8"
-
-def _match_css_class(str):
- """Build a RE to match the given CSS class."""
- return re.compile(r"(^|.*\s)%s($|\s)" % str)
-
-# First, the classes that represent markup elements.
-
-class PageElement(object):
- """Contains the navigational information for some part of the page
- (either a tag or a piece of text)"""
-
- def setup(self, parent=None, previous=None):
- """Sets up the initial relations between this element and
- other elements."""
- self.parent = parent
- self.previous = previous
- self.next = None
- self.previousSibling = None
- self.nextSibling = None
- if self.parent and self.parent.contents:
- self.previousSibling = self.parent.contents[-1]
- self.previousSibling.nextSibling = self
-
- def replaceWith(self, replaceWith):
- oldParent = self.parent
- myIndex = self.parent.index(self)
- if hasattr(replaceWith, "parent")\
- and replaceWith.parent is self.parent:
- # We're replacing this element with one of its siblings.
- index = replaceWith.parent.index(replaceWith)
- if index and index < myIndex:
- # Furthermore, it comes before this element. That
- # means that when we extract it, the index of this
- # element will change.
- myIndex = myIndex - 1
- self.extract()
- oldParent.insert(myIndex, replaceWith)
-
- def replaceWithChildren(self):
- myParent = self.parent
- myIndex = self.parent.index(self)
- self.extract()
- reversedChildren = list(self.contents)
- reversedChildren.reverse()
- for child in reversedChildren:
- myParent.insert(myIndex, child)
-
- def extract(self):
- """Destructively rips this element out of the tree."""
- if self.parent:
- try:
- del self.parent.contents[self.parent.index(self)]
- except ValueError:
- pass
-
- #Find the two elements that would be next to each other if
- #this element (and any children) hadn't been parsed. Connect
- #the two.
- lastChild = self._lastRecursiveChild()
- nextElement = lastChild.next
-
- if self.previous:
- self.previous.next = nextElement
- if nextElement:
- nextElement.previous = self.previous
- self.previous = None
- lastChild.next = None
-
- self.parent = None
- if self.previousSibling:
- self.previousSibling.nextSibling = self.nextSibling
- if self.nextSibling:
- self.nextSibling.previousSibling = self.previousSibling
- self.previousSibling = self.nextSibling = None
- return self
-
- def _lastRecursiveChild(self):
- "Finds the last element beneath this object to be parsed."
- lastChild = self
- while hasattr(lastChild, 'contents') and lastChild.contents:
- lastChild = lastChild.contents[-1]
- return lastChild
-
- def insert(self, position, newChild):
- if isinstance(newChild, basestring) \
- and not isinstance(newChild, NavigableString):
- newChild = NavigableString(newChild)
-
- position = min(position, len(self.contents))
- if hasattr(newChild, 'parent') and newChild.parent is not None:
- # We're 'inserting' an element that's already one
- # of this object's children.
- if newChild.parent is self:
- index = self.index(newChild)
- if index > position:
- # Furthermore we're moving it further down the
- # list of this object's children. That means that
- # when we extract this element, our target index
- # will jump down one.
- position = position - 1
- newChild.extract()
-
- newChild.parent = self
- previousChild = None
- if position == 0:
- newChild.previousSibling = None
- newChild.previous = self
- else:
- previousChild = self.contents[position-1]
- newChild.previousSibling = previousChild
- newChild.previousSibling.nextSibling = newChild
- newChild.previous = previousChild._lastRecursiveChild()
- if newChild.previous:
- newChild.previous.next = newChild
-
- newChildsLastElement = newChild._lastRecursiveChild()
-
- if position >= len(self.contents):
- newChild.nextSibling = None
-
- parent = self
- parentsNextSibling = None
- while not parentsNextSibling:
- parentsNextSibling = parent.nextSibling
- parent = parent.parent
- if not parent: # This is the last element in the document.
- break
- if parentsNextSibling:
- newChildsLastElement.next = parentsNextSibling
- else:
- newChildsLastElement.next = None
- else:
- nextChild = self.contents[position]
- newChild.nextSibling = nextChild
- if newChild.nextSibling:
- newChild.nextSibling.previousSibling = newChild
- newChildsLastElement.next = nextChild
-
- if newChildsLastElement.next:
- newChildsLastElement.next.previous = newChildsLastElement
- self.contents.insert(position, newChild)
-
- def append(self, tag):
- """Appends the given tag to the contents of this tag."""
- self.insert(len(self.contents), tag)
-
- def findNext(self, name=None, attrs={}, text=None, **kwargs):
- """Returns the first item that matches the given criteria and
- appears after this Tag in the document."""
- return self._findOne(self.findAllNext, name, attrs, text, **kwargs)
-
- def findAllNext(self, name=None, attrs={}, text=None, limit=None,
- **kwargs):
- """Returns all items that match the given criteria and appear
- after this Tag in the document."""
- return self._findAll(name, attrs, text, limit, self.nextGenerator,
- **kwargs)
-
- def findNextSibling(self, name=None, attrs={}, text=None, **kwargs):
- """Returns the closest sibling to this Tag that matches the
- given criteria and appears after this Tag in the document."""
- return self._findOne(self.findNextSiblings, name, attrs, text,
- **kwargs)
-
- def findNextSiblings(self, name=None, attrs={}, text=None, limit=None,
- **kwargs):
- """Returns the siblings of this Tag that match the given
- criteria and appear after this Tag in the document."""
- return self._findAll(name, attrs, text, limit,
- self.nextSiblingGenerator, **kwargs)
- fetchNextSiblings = findNextSiblings # Compatibility with pre-3.x
-
- def findPrevious(self, name=None, attrs={}, text=None, **kwargs):
- """Returns the first item that matches the given criteria and
- appears before this Tag in the document."""
- return self._findOne(self.findAllPrevious, name, attrs, text, **kwargs)
-
- def findAllPrevious(self, name=None, attrs={}, text=None, limit=None,
- **kwargs):
- """Returns all items that match the given criteria and appear
- before this Tag in the document."""
- return self._findAll(name, attrs, text, limit, self.previousGenerator,
- **kwargs)
- fetchPrevious = findAllPrevious # Compatibility with pre-3.x
-
- def findPreviousSibling(self, name=None, attrs={}, text=None, **kwargs):
- """Returns the closest sibling to this Tag that matches the
- given criteria and appears before this Tag in the document."""
- return self._findOne(self.findPreviousSiblings, name, attrs, text,
- **kwargs)
-
- def findPreviousSiblings(self, name=None, attrs={}, text=None,
- limit=None, **kwargs):
- """Returns the siblings of this Tag that match the given
- criteria and appear before this Tag in the document."""
- return self._findAll(name, attrs, text, limit,
- self.previousSiblingGenerator, **kwargs)
- fetchPreviousSiblings = findPreviousSiblings # Compatibility with pre-3.x
-
- def findParent(self, name=None, attrs={}, **kwargs):
- """Returns the closest parent of this Tag that matches the given
- criteria."""
- # NOTE: We can't use _findOne because findParents takes a different
- # set of arguments.
- r = None
- l = self.findParents(name, attrs, 1)
- if l:
- r = l[0]
- return r
-
- def findParents(self, name=None, attrs={}, limit=None, **kwargs):
- """Returns the parents of this Tag that match the given
- criteria."""
-
- return self._findAll(name, attrs, None, limit, self.parentGenerator,
- **kwargs)
- fetchParents = findParents # Compatibility with pre-3.x
-
- #These methods do the real heavy lifting.
-
- def _findOne(self, method, name, attrs, text, **kwargs):
- r = None
- l = method(name, attrs, text, 1, **kwargs)
- if l:
- r = l[0]
- return r
-
- def _findAll(self, name, attrs, text, limit, generator, **kwargs):
- "Iterates over a generator looking for things that match."
-
- if isinstance(name, SoupStrainer):
- strainer = name
- # (Possibly) special case some findAll*(...) searches
- elif text is None and not limit and not attrs and not kwargs:
- # findAll*(True)
- if name is True:
- return [element for element in generator()
- if isinstance(element, Tag)]
- # findAll*('tag-name')
- elif isinstance(name, basestring):
- return [element for element in generator()
- if isinstance(element, Tag) and
- element.name == name]
- else:
- strainer = SoupStrainer(name, attrs, text, **kwargs)
- # Build a SoupStrainer
- else:
- strainer = SoupStrainer(name, attrs, text, **kwargs)
- results = ResultSet(strainer)
- g = generator()
- while True:
- try:
- i = g.next()
- except StopIteration:
- break
- if i:
- found = strainer.search(i)
- if found:
- results.append(found)
- if limit and len(results) >= limit:
- break
- return results
-
- #These Generators can be used to navigate starting from both
- #NavigableStrings and Tags.
- def nextGenerator(self):
- i = self
- while i is not None:
- i = i.next
- yield i
-
- def nextSiblingGenerator(self):
- i = self
- while i is not None:
- i = i.nextSibling
- yield i
-
- def previousGenerator(self):
- i = self
- while i is not None:
- i = i.previous
- yield i
-
- def previousSiblingGenerator(self):
- i = self
- while i is not None:
- i = i.previousSibling
- yield i
-
- def parentGenerator(self):
- i = self
- while i is not None:
- i = i.parent
- yield i
-
- # Utility methods
- def substituteEncoding(self, str, encoding=None):
- encoding = encoding or "utf-8"
- return str.replace("%SOUP-ENCODING%", encoding)
-
- def toEncoding(self, s, encoding=None):
- """Encodes an object to a string in some encoding, or to Unicode.
- ."""
- if isinstance(s, unicode):
- if encoding:
- s = s.encode(encoding)
- elif isinstance(s, str):
- if encoding:
- s = s.encode(encoding)
- else:
- s = unicode(s)
- else:
- if encoding:
- s = self.toEncoding(str(s), encoding)
- else:
- s = unicode(s)
- return s
-
-class NavigableString(unicode, PageElement):
-
- def __new__(cls, value):
- """Create a new NavigableString.
-
- When unpickling a NavigableString, this method is called with
- the string in DEFAULT_OUTPUT_ENCODING. That encoding needs to be
- passed in to the superclass's __new__ or the superclass won't know
- how to handle non-ASCII characters.
- """
- if isinstance(value, unicode):
- return unicode.__new__(cls, value)
- return unicode.__new__(cls, value, DEFAULT_OUTPUT_ENCODING)
-
- def __getnewargs__(self):
- return (NavigableString.__str__(self),)
-
- def __getattr__(self, attr):
- """text.string gives you text. This is for backwards
- compatibility for Navigable*String, but for CData* it lets you
- get the string without the CData wrapper."""
- if attr == 'string':
- return self
- else:
- raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr)
-
- def __unicode__(self):
- return str(self).decode(DEFAULT_OUTPUT_ENCODING)
-
- def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING):
- if encoding:
- return self.encode(encoding)
- else:
- return self
-
-class CData(NavigableString):
-
- def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING):
- return "<![CDATA[%s]]>" % NavigableString.__str__(self, encoding)
-
-class ProcessingInstruction(NavigableString):
- def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING):
- output = self
- if "%SOUP-ENCODING%" in output:
- output = self.substituteEncoding(output, encoding)
- return "<?%s?>" % self.toEncoding(output, encoding)
-
-class Comment(NavigableString):
- def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING):
- return "<!--%s-->" % NavigableString.__str__(self, encoding)
-
-class Declaration(NavigableString):
- def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING):
- return "<!%s>" % NavigableString.__str__(self, encoding)
-
-class Tag(PageElement):
-
- """Represents a found HTML tag with its attributes and contents."""
-
- def _invert(h):
- "Cheap function to invert a hash."
- i = {}
- for k,v in h.items():
- i[v] = k
- return i
-
- XML_ENTITIES_TO_SPECIAL_CHARS = { "apos" : "'",
- "quot" : '"',
- "amp" : "&",
- "lt" : "<",
- "gt" : ">" }
-
- XML_SPECIAL_CHARS_TO_ENTITIES = _invert(XML_ENTITIES_TO_SPECIAL_CHARS)
-
- def _convertEntities(self, match):
- """Used in a call to re.sub to replace HTML, XML, and numeric
- entities with the appropriate Unicode characters. If HTML
- entities are being converted, any unrecognized entities are
- escaped."""
- x = match.group(1)
- if self.convertHTMLEntities and x in name2codepoint:
- return unichr(name2codepoint[x])
- elif x in self.XML_ENTITIES_TO_SPECIAL_CHARS:
- if self.convertXMLEntities:
- return self.XML_ENTITIES_TO_SPECIAL_CHARS[x]
- else:
- return u'&%s;' % x
- elif len(x) > 0 and x[0] == '#':
- # Handle numeric entities
- if len(x) > 1 and x[1] == 'x':
- return unichr(int(x[2:], 16))
- else:
- return unichr(int(x[1:]))
-
- elif self.escapeUnrecognizedEntities:
- return u'&amp;%s;' % x
- else:
- return u'&%s;' % x
-
- def __init__(self, parser, name, attrs=None, parent=None,
- previous=None):
- "Basic constructor."
-
- # We don't actually store the parser object: that lets extracted
- # chunks be garbage-collected
- self.parserClass = parser.__class__
- self.isSelfClosing = parser.isSelfClosingTag(name)
- self.name = name
- if attrs is None:
- attrs = []
- self.attrs = attrs
- self.contents = []
- self.setup(parent, previous)
- self.hidden = False
- self.containsSubstitutions = False
- self.convertHTMLEntities = parser.convertHTMLEntities
- self.convertXMLEntities = parser.convertXMLEntities
- self.escapeUnrecognizedEntities = parser.escapeUnrecognizedEntities
-
- # Convert any HTML, XML, or numeric entities in the attribute values.
- convert = lambda(k, val): (k,
- re.sub("&(#\d+|#x[0-9a-fA-F]+|\w+);",
- self._convertEntities,
- val))
- self.attrs = map(convert, self.attrs)
-
- def getString(self):
- if (len(self.contents) == 1
- and isinstance(self.contents[0], NavigableString)):
- return self.contents[0]
-
- def setString(self, string):
- """Replace the contents of the tag with a string"""
- self.clear()
- self.append(string)
-
- string = property(getString, setString)
-
- def getText(self, separator=u""):
- if not len(self.contents):
- return u""
- stopNode = self._lastRecursiveChild().next
- strings = []
- current = self.contents[0]
- while current is not stopNode:
- if isinstance(current, NavigableString):
- strings.append(current.strip())
- current = current.next
- return separator.join(strings)
-
- text = property(getText)
-
- def get(self, key, default=None):
- """Returns the value of the 'key' attribute for the tag, or
- the value given for 'default' if it doesn't have that
- attribute."""
- return self._getAttrMap().get(key, default)
-
- def clear(self):
- """Extract all children."""
- for child in self.contents[:]:
- child.extract()
-
- def index(self, element):
- for i, child in enumerate(self.contents):
- if child is element:
- return i
- raise ValueError("Tag.index: element not in tag")
-
- def has_key(self, key):
- return self._getAttrMap().has_key(key)
-
- def __getitem__(self, key):
- """tag[key] returns the value of the 'key' attribute for the tag,
- and throws an exception if it's not there."""
- return self._getAttrMap()[key]
-
- def __iter__(self):
- "Iterating over a tag iterates over its contents."
- return iter(self.contents)
-
- def __len__(self):
- "The length of a tag is the length of its list of contents."
- return len(self.contents)
-
- def __contains__(self, x):
- return x in self.contents
-
- def __nonzero__(self):
- "A tag is non-None even if it has no contents."
- return True
-
- def __setitem__(self, key, value):
- """Setting tag[key] sets the value of the 'key' attribute for the
- tag."""
- self._getAttrMap()
- self.attrMap[key] = value
- found = False
- for i in range(0, len(self.attrs)):
- if self.attrs[i][0] == key:
- self.attrs[i] = (key, value)
- found = True
- if not found:
- self.attrs.append((key, value))
- self._getAttrMap()[key] = value
-
- def __delitem__(self, key):
- "Deleting tag[key] deletes all 'key' attributes for the tag."
- for item in self.attrs:
- if item[0] == key:
- self.attrs.remove(item)
- #We don't break because bad HTML can define the same
- #attribute multiple times.
- self._getAttrMap()
- if self.attrMap.has_key(key):
- del self.attrMap[key]
-
- def __call__(self, *args, **kwargs):
- """Calling a tag like a function is the same as calling its
- findAll() method. Eg. tag('a') returns a list of all the A tags
- found within this tag."""
- return apply(self.findAll, args, kwargs)
-
- def __getattr__(self, tag):
- #print "Getattr %s.%s" % (self.__class__, tag)
- if len(tag) > 3 and tag.rfind('Tag') == len(tag)-3:
- return self.find(tag[:-3])
- elif tag.find('__') != 0:
- return self.find(tag)
- raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__, tag)
-
- def __eq__(self, other):
- """Returns true iff this tag has the same name, the same attributes,
- and the same contents (recursively) as the given tag.
-
- NOTE: right now this will return false if two tags have the
- same attributes in a different order. Should this be fixed?"""
- if other is self:
- return True
- if not hasattr(other, 'name') or not hasattr(other, 'attrs') or not hasattr(other, 'contents') or self.name != other.name or self.attrs != other.attrs or len(self) != len(other):
- return False
- for i in range(0, len(self.contents)):
- if self.contents[i] != other.contents[i]:
- return False
- return True
-
- def __ne__(self, other):
- """Returns true iff this tag is not identical to the other tag,
- as defined in __eq__."""
- return not self == other
-
- def __repr__(self, encoding=DEFAULT_OUTPUT_ENCODING):
- """Renders this tag as a string."""
- return self.__str__(encoding)
-
- def __unicode__(self):
- return self.__str__(None)
-
- BARE_AMPERSAND_OR_BRACKET = re.compile("([<>]|"
- + "&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;)"
- + ")")
-
- def _sub_entity(self, x):
- """Used with a regular expression to substitute the
- appropriate XML entity for an XML special character."""
- return "&" + self.XML_SPECIAL_CHARS_TO_ENTITIES[x.group(0)[0]] + ";"
-
- def __str__(self, encoding=DEFAULT_OUTPUT_ENCODING,
- prettyPrint=False, indentLevel=0):
- """Returns a string or Unicode representation of this tag and
- its contents. To get Unicode, pass None for encoding.
-
- NOTE: since Python's HTML parser consumes whitespace, this
- method is not certain to reproduce the whitespace present in
- the original string."""
-
- encodedName = self.toEncoding(self.name, encoding)
-
- attrs = []
- if self.attrs:
- for key, val in self.attrs:
- fmt = '%s="%s"'
- if isinstance(val, basestring):
- if self.containsSubstitutions and '%SOUP-ENCODING%' in val:
- val = self.substituteEncoding(val, encoding)
-
- # The attribute value either:
- #
- # * Contains no embedded double quotes or single quotes.
- # No problem: we enclose it in double quotes.
- # * Contains embedded single quotes. No problem:
- # double quotes work here too.
- # * Contains embedded double quotes. No problem:
- # we enclose it in single quotes.
- # * Embeds both single _and_ double quotes. This
- # can't happen naturally, but it can happen if
- # you modify an attribute value after parsing
- # the document. Now we have a bit of a
- # problem. We solve it by enclosing the
- # attribute in single quotes, and escaping any
- # embedded single quotes to XML entities.
- if '"' in val:
- fmt = "%s='%s'"
- if "'" in val:
- # TODO: replace with apos when
- # appropriate.
- val = val.replace("'", "&squot;")
-
- # Now we're okay w/r/t quotes. But the attribute
- # value might also contain angle brackets, or
- # ampersands that aren't part of entities. We need
- # to escape those to XML entities too.
- val = self.BARE_AMPERSAND_OR_BRACKET.sub(self._sub_entity, val)
-
- attrs.append(fmt % (self.toEncoding(key, encoding),
- self.toEncoding(val, encoding)))
- close = ''
- closeTag = ''
- if self.isSelfClosing:
- close = ' /'
- else:
- closeTag = '</%s>' % encodedName
-
- indentTag, indentContents = 0, 0
- if prettyPrint:
- indentTag = indentLevel
- space = (' ' * (indentTag-1))
- indentContents = indentTag + 1
- contents = self.renderContents(encoding, prettyPrint, indentContents)
- if self.hidden:
- s = contents
- else:
- s = []
- attributeString = ''
- if attrs:
- attributeString = ' ' + ' '.join(attrs)
- if prettyPrint:
- s.append(space)
- s.append('<%s%s%s>' % (encodedName, attributeString, close))
- if prettyPrint:
- s.append("\n")
- s.append(contents)
- if prettyPrint and contents and contents[-1] != "\n":
- s.append("\n")
- if prettyPrint and closeTag:
- s.append(space)
- s.append(closeTag)
- if prettyPrint and closeTag and self.nextSibling:
- s.append("\n")
- s = ''.join(s)
- return s
-
- def decompose(self):
- """Recursively destroys the contents of this tree."""
- self.extract()
- if len(self.contents) == 0:
- return
- current = self.contents[0]
- while current is not None:
- next = current.next
- if isinstance(current, Tag):
- del current.contents[:]
- current.parent = None
- current.previous = None
- current.previousSibling = None
- current.next = None
- current.nextSibling = None
- current = next
-
- def prettify(self, encoding=DEFAULT_OUTPUT_ENCODING):
- return self.__str__(encoding, True)
-
- def renderContents(self, encoding=DEFAULT_OUTPUT_ENCODING,
- prettyPrint=False, indentLevel=0):
- """Renders the contents of this tag as a string in the given
- encoding. If encoding is None, returns a Unicode string.."""
- s=[]
- for c in self:
- text = None
- if isinstance(c, NavigableString):
- text = c.__str__(encoding)
- elif isinstance(c, Tag):
- s.append(c.__str__(encoding, prettyPrint, indentLevel))
- if text and prettyPrint:
- text = text.strip()
- if text:
- if prettyPrint:
- s.append(" " * (indentLevel-1))
- s.append(text)
- if prettyPrint:
- s.append("\n")
- return ''.join(s)
-
- #Soup methods
-
- def find(self, name=None, attrs={}, recursive=True, text=None,
- **kwargs):
- """Return only the first child of this Tag matching the given
- criteria."""
- r = None
- l = self.findAll(name, attrs, recursive, text, 1, **kwargs)
- if l:
- r = l[0]
- return r
- findChild = find
-
- def findAll(self, name=None, attrs={}, recursive=True, text=None,
- limit=None, **kwargs):
- """Extracts a list of Tag objects that match the given
- criteria. You can specify the name of the Tag and any
- attributes you want the Tag to have.
-
- The value of a key-value pair in the 'attrs' map can be a
- string, a list of strings, a regular expression object, or a
- callable that takes a string and returns whether or not the
- string matches for some custom definition of 'matches'. The
- same is true of the tag name."""
- generator = self.recursiveChildGenerator
- if not recursive:
- generator = self.childGenerator
- return self._findAll(name, attrs, text, limit, generator, **kwargs)
- findChildren = findAll
-
- # Pre-3.x compatibility methods
- first = find
- fetch = findAll
-
- def fetchText(self, text=None, recursive=True, limit=None):
- return self.findAll(text=text, recursive=recursive, limit=limit)
-
- def firstText(self, text=None, recursive=True):
- return self.find(text=text, recursive=recursive)
-
- #Private methods
-
- def _getAttrMap(self):
- """Initializes a map representation of this tag's attributes,
- if not already initialized."""
- if not getattr(self, 'attrMap'):
- self.attrMap = {}
- for (key, value) in self.attrs:
- self.attrMap[key] = value
- return self.attrMap
-
- #Generator methods
- def childGenerator(self):
- # Just use the iterator from the contents
- return iter(self.contents)
-
- def recursiveChildGenerator(self):
- if not len(self.contents):
- raise StopIteration
- stopNode = self._lastRecursiveChild().next
- current = self.contents[0]
- while current is not stopNode:
- yield current
- current = current.next
-
-
-# Next, a couple classes to represent queries and their results.
-class SoupStrainer:
- """Encapsulates a number of ways of matching a markup element (tag or
- text)."""
-
- def __init__(self, name=None, attrs={}, text=None, **kwargs):
- self.name = name
- if isinstance(attrs, basestring):
- kwargs['class'] = _match_css_class(attrs)
- attrs = None
- if kwargs:
- if attrs:
- attrs = attrs.copy()
- attrs.update(kwargs)
- else:
- attrs = kwargs
- self.attrs = attrs
- self.text = text
-
- def __str__(self):
- if self.text:
- return self.text
- else:
- return "%s|%s" % (self.name, self.attrs)
-
- def searchTag(self, markupName=None, markupAttrs={}):
- found = None
- markup = None
- if isinstance(markupName, Tag):
- markup = markupName
- markupAttrs = markup
- callFunctionWithTagData = callable(self.name) \
- and not isinstance(markupName, Tag)
-
- if (not self.name) \
- or callFunctionWithTagData \
- or (markup and self._matches(markup, self.name)) \
- or (not markup and self._matches(markupName, self.name)):
- if callFunctionWithTagData:
- match = self.name(markupName, markupAttrs)
- else:
- match = True
- markupAttrMap = None
- for attr, matchAgainst in self.attrs.items():
- if not markupAttrMap:
- if hasattr(markupAttrs, 'get'):
- markupAttrMap = markupAttrs
- else:
- markupAttrMap = {}
- for k,v in markupAttrs:
- markupAttrMap[k] = v
- attrValue = markupAttrMap.get(attr)
- if not self._matches(attrValue, matchAgainst):
- match = False
- break
- if match:
- if markup:
- found = markup
- else:
- found = markupName
- return found
-
- def search(self, markup):
- #print 'looking for %s in %s' % (self, markup)
- found = None
- # If given a list of items, scan it for a text element that
- # matches.
- if hasattr(markup, "__iter__") \
- and not isinstance(markup, Tag):
- for element in markup:
- if isinstance(element, NavigableString) \
- and self.search(element):
- found = element
- break
- # If it's a Tag, make sure its name or attributes match.
- # Don't bother with Tags if we're searching for text.
- elif isinstance(markup, Tag):
- if not self.text:
- found = self.searchTag(markup)
- # If it's text, make sure the text matches.
- elif isinstance(markup, NavigableString) or \
- isinstance(markup, basestring):
- if self._matches(markup, self.text):
- found = markup
- else:
- raise Exception, "I don't know how to match against a %s" \
- % markup.__class__
- return found
-
- def _matches(self, markup, matchAgainst):
- #print "Matching %s against %s" % (markup, matchAgainst)
- result = False
- if matchAgainst is True:
- result = markup is not None
- elif callable(matchAgainst):
- result = matchAgainst(markup)
- else:
- #Custom match methods take the tag as an argument, but all
- #other ways of matching match the tag name as a string.
- if isinstance(markup, Tag):
- markup = markup.name
- if markup and not isinstance(markup, basestring):
- markup = unicode(markup)
- #Now we know that chunk is either a string, or None.
- if hasattr(matchAgainst, 'match'):
- # It's a regexp object.
- result = markup and matchAgainst.search(markup)
- elif hasattr(matchAgainst, '__iter__'): # list-like
- result = markup in matchAgainst
- elif hasattr(matchAgainst, 'items'):
- result = markup.has_key(matchAgainst)
- elif matchAgainst and isinstance(markup, basestring):
- if isinstance(markup, unicode):
- matchAgainst = unicode(matchAgainst)
- else:
- matchAgainst = str(matchAgainst)
-
- if not result:
- result = matchAgainst == markup
- return result
-
-class ResultSet(list):
- """A ResultSet is just a list that keeps track of the SoupStrainer
- that created it."""
- def __init__(self, source):
- list.__init__([])
- self.source = source
-
-# Now, some helper functions.
-
-def buildTagMap(default, *args):
- """Turns a list of maps, lists, or scalars into a single map.
- Used to build the SELF_CLOSING_TAGS, NESTABLE_TAGS, and
- NESTING_RESET_TAGS maps out of lists and partial maps."""
- built = {}
- for portion in args:
- if hasattr(portion, 'items'):
- #It's a map. Merge it.
- for k,v in portion.items():
- built[k] = v
- elif hasattr(portion, '__iter__'): # is a list
- #It's a list. Map each item to the default.
- for k in portion:
- built[k] = default
- else:
- #It's a scalar. Map it to the default.
- built[portion] = default
- return built
-
-# Now, the parser classes.
-
-class BeautifulStoneSoup(Tag, SGMLParser):
-
- """This class contains the basic parser and search code. It defines
- a parser that knows nothing about tag behavior except for the
- following:
-
- You can't close a tag without closing all the tags it encloses.
- That is, "<foo><bar></foo>" actually means
- "<foo><bar></bar></foo>".
-
- [Another possible explanation is "<foo><bar /></foo>", but since
- this class defines no SELF_CLOSING_TAGS, it will never use that
- explanation.]
-
- This class is useful for parsing XML or made-up markup languages,
- or when BeautifulSoup makes an assumption counter to what you were
- expecting."""
-
- SELF_CLOSING_TAGS = {}
- NESTABLE_TAGS = {}
- RESET_NESTING_TAGS = {}
- QUOTE_TAGS = {}
- PRESERVE_WHITESPACE_TAGS = []
-
- MARKUP_MASSAGE = [(re.compile('(<[^<>]*)/>'),
- lambda x: x.group(1) + ' />'),
- (re.compile('<!\s+([^<>]*)>'),
- lambda x: '<!' + x.group(1) + '>')
- ]
-
- ROOT_TAG_NAME = u'[document]'
-
- HTML_ENTITIES = "html"
- XML_ENTITIES = "xml"
- XHTML_ENTITIES = "xhtml"
- # TODO: This only exists for backwards-compatibility
- ALL_ENTITIES = XHTML_ENTITIES
-
- # Used when determining whether a text node is all whitespace and
- # can be replaced with a single space. A text node that contains
- # fancy Unicode spaces (usually non-breaking) should be left
- # alone.
- STRIP_ASCII_SPACES = { 9: None, 10: None, 12: None, 13: None, 32: None, }
-
- def __init__(self, markup="", parseOnlyThese=None, fromEncoding=None,
- markupMassage=True, smartQuotesTo=XML_ENTITIES,
- convertEntities=None, selfClosingTags=None, isHTML=False):
- """The Soup object is initialized as the 'root tag', and the
- provided markup (which can be a string or a file-like object)
- is fed into the underlying parser.
-
- sgmllib will process most bad HTML, and the BeautifulSoup
- class has some tricks for dealing with some HTML that kills
- sgmllib, but Beautiful Soup can nonetheless choke or lose data
- if your data uses self-closing tags or declarations
- incorrectly.
-
- By default, Beautiful Soup uses regexes to sanitize input,
- avoiding the vast majority of these problems. If the problems
- don't apply to you, pass in False for markupMassage, and
- you'll get better performance.
-
- The default parser massage techniques fix the two most common
- instances of invalid HTML that choke sgmllib:
-
- <br/> (No space between name of closing tag and tag close)
- <! --Comment--> (Extraneous whitespace in declaration)
-
- You can pass in a custom list of (RE object, replace method)
- tuples to get Beautiful Soup to scrub your input the way you
- want."""
-
- self.parseOnlyThese = parseOnlyThese
- self.fromEncoding = fromEncoding
- self.smartQuotesTo = smartQuotesTo
- self.convertEntities = convertEntities
- # Set the rules for how we'll deal with the entities we
- # encounter
- if self.convertEntities:
- # It doesn't make sense to convert encoded characters to
- # entities even while you're converting entities to Unicode.
- # Just convert it all to Unicode.
- self.smartQuotesTo = None
- if convertEntities == self.HTML_ENTITIES:
- self.convertXMLEntities = False
- self.convertHTMLEntities = True
- self.escapeUnrecognizedEntities = True
- elif convertEntities == self.XHTML_ENTITIES:
- self.convertXMLEntities = True
- self.convertHTMLEntities = True
- self.escapeUnrecognizedEntities = False
- elif convertEntities == self.XML_ENTITIES:
- self.convertXMLEntities = True
- self.convertHTMLEntities = False
- self.escapeUnrecognizedEntities = False
- else:
- self.convertXMLEntities = False
- self.convertHTMLEntities = False
- self.escapeUnrecognizedEntities = False
-
- self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags)
- SGMLParser.__init__(self)
-
- if hasattr(markup, 'read'): # It's a file-type object.
- markup = markup.read()
- self.markup = markup
- self.markupMassage = markupMassage
- try:
- self._feed(isHTML=isHTML)
- except StopParsing:
- pass
- self.markup = None # The markup can now be GCed
-
- def convert_charref(self, name):
- """This method fixes a bug in Python's SGMLParser."""
- try:
- n = int(name)
- except ValueError:
- return
- if not 0 <= n <= 127 : # ASCII ends at 127, not 255
- return
- return self.convert_codepoint(n)
-
- def _feed(self, inDocumentEncoding=None, isHTML=False):
- # Convert the document to Unicode.
- markup = self.markup
- if isinstance(markup, unicode):
- if not hasattr(self, 'originalEncoding'):
- self.originalEncoding = None
- else:
- dammit = UnicodeDammit\
- (markup, [self.fromEncoding, inDocumentEncoding],
- smartQuotesTo=self.smartQuotesTo, isHTML=isHTML)
- markup = dammit.unicode
- self.originalEncoding = dammit.originalEncoding
- self.declaredHTMLEncoding = dammit.declaredHTMLEncoding
- if markup:
- if self.markupMassage:
- if not hasattr(self.markupMassage, "__iter__"):
- self.markupMassage = self.MARKUP_MASSAGE
- for fix, m in self.markupMassage:
- markup = fix.sub(m, markup)
- # TODO: We get rid of markupMassage so that the
- # soup object can be deepcopied later on. Some
- # Python installations can't copy regexes. If anyone
- # was relying on the existence of markupMassage, this
- # might cause problems.
- del(self.markupMassage)
- self.reset()
-
- SGMLParser.feed(self, markup)
- # Close out any unfinished strings and close all the open tags.
- self.endData()
- while self.currentTag.name != self.ROOT_TAG_NAME:
- self.popTag()
-
- def __getattr__(self, methodName):
- """This method routes method call requests to either the SGMLParser
- superclass or the Tag superclass, depending on the method name."""
- #print "__getattr__ called on %s.%s" % (self.__class__, methodName)
-
- if methodName.startswith('start_') or methodName.startswith('end_') \
- or methodName.startswith('do_'):
- return SGMLParser.__getattr__(self, methodName)
- elif not methodName.startswith('__'):
- return Tag.__getattr__(self, methodName)
- else:
- raise AttributeError
-
- def isSelfClosingTag(self, name):
- """Returns true iff the given string is the name of a
- self-closing tag according to this parser."""
- return self.SELF_CLOSING_TAGS.has_key(name) \
- or self.instanceSelfClosingTags.has_key(name)
-
- def reset(self):
- Tag.__init__(self, self, self.ROOT_TAG_NAME)
- self.hidden = 1
- SGMLParser.reset(self)
- self.currentData = []
- self.currentTag = None
- self.tagStack = []
- self.quoteStack = []
- self.pushTag(self)
-
- def popTag(self):
- tag = self.tagStack.pop()
-
- #print "Pop", tag.name
- if self.tagStack:
- self.currentTag = self.tagStack[-1]
- return self.currentTag
-
- def pushTag(self, tag):
- #print "Push", tag.name
- if self.currentTag:
- self.currentTag.contents.append(tag)
- self.tagStack.append(tag)
- self.currentTag = self.tagStack[-1]
-
- def endData(self, containerClass=NavigableString):
- if self.currentData:
- currentData = u''.join(self.currentData)
- if (currentData.translate(self.STRIP_ASCII_SPACES) == '' and
- not set([tag.name for tag in self.tagStack]).intersection(
- self.PRESERVE_WHITESPACE_TAGS)):
- if '\n' in currentData:
- currentData = '\n'
- else:
- currentData = ' '
- self.currentData = []
- if self.parseOnlyThese and len(self.tagStack) <= 1 and \
- (not self.parseOnlyThese.text or \
- not self.parseOnlyThese.search(currentData)):
- return
- o = containerClass(currentData)
- o.setup(self.currentTag, self.previous)
- if self.previous:
- self.previous.next = o
- self.previous = o
- self.currentTag.contents.append(o)
-
-
- def _popToTag(self, name, inclusivePop=True):
- """Pops the tag stack up to and including the most recent
- instance of the given tag. If inclusivePop is false, pops the tag
- stack up to but *not* including the most recent instqance of
- the given tag."""
- #print "Popping to %s" % name
- if name == self.ROOT_TAG_NAME:
- return
-
- numPops = 0
- mostRecentTag = None
- for i in range(len(self.tagStack)-1, 0, -1):
- if name == self.tagStack[i].name:
- numPops = len(self.tagStack)-i
- break
- if not inclusivePop:
- numPops = numPops - 1
-
- for i in range(0, numPops):
- mostRecentTag = self.popTag()
- return mostRecentTag
-
- def _smartPop(self, name):
-
- """We need to pop up to the previous tag of this type, unless
- one of this tag's nesting reset triggers comes between this
- tag and the previous tag of this type, OR unless this tag is a
- generic nesting trigger and another generic nesting trigger
- comes between this tag and the previous tag of this type.
-
- Examples:
- <p>Foo<b>Bar *<p>* should pop to 'p', not 'b'.
- <p>Foo<table>Bar *<p>* should pop to 'table', not 'p'.
- <p>Foo<table><tr>Bar *<p>* should pop to 'tr', not 'p'.
-
- <li><ul><li> *<li>* should pop to 'ul', not the first 'li'.
- <tr><table><tr> *<tr>* should pop to 'table', not the first 'tr'
- <td><tr><td> *<td>* should pop to 'tr', not the first 'td'
- """
-
- nestingResetTriggers = self.NESTABLE_TAGS.get(name)
- isNestable = nestingResetTriggers is not None
- isResetNesting = self.RESET_NESTING_TAGS.has_key(name)
- popTo = None
- inclusive = True
- for i in range(len(self.tagStack)-1, 0, -1):
- p = self.tagStack[i]
- if (not p or p.name == name) and not isNestable:
- #Non-nestable tags get popped to the top or to their
- #last occurance.
- popTo = name
- break
- if (nestingResetTriggers is not None
- and p.name in nestingResetTriggers) \
- or (nestingResetTriggers is None and isResetNesting
- and self.RESET_NESTING_TAGS.has_key(p.name)):
-
- #If we encounter one of the nesting reset triggers
- #peculiar to this tag, or we encounter another tag
- #that causes nesting to reset, pop up to but not
- #including that tag.
- popTo = p.name
- inclusive = False
- break
- p = p.parent
- if popTo:
- self._popToTag(popTo, inclusive)
-
- def unknown_starttag(self, name, attrs, selfClosing=0):
- #print "Start tag %s: %s" % (name, attrs)
- if self.quoteStack:
- #This is not a real tag.
- #print "<%s> is not real!" % name
- attrs = ''.join([' %s="%s"' % (x, y) for x, y in attrs])
- self.handle_data('<%s%s>' % (name, attrs))
- return
- self.endData()
-
- if not self.isSelfClosingTag(name) and not selfClosing:
- self._smartPop(name)
-
- if self.parseOnlyThese and len(self.tagStack) <= 1 \
- and (self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs)):
- return
-
- tag = Tag(self, name, attrs, self.currentTag, self.previous)
- if self.previous:
- self.previous.next = tag
- self.previous = tag
- self.pushTag(tag)
- if selfClosing or self.isSelfClosingTag(name):
- self.popTag()
- if name in self.QUOTE_TAGS:
- #print "Beginning quote (%s)" % name
- self.quoteStack.append(name)
- self.literal = 1
- return tag
-
- def unknown_endtag(self, name):
- #print "End tag %s" % name
- if self.quoteStack and self.quoteStack[-1] != name:
- #This is not a real end tag.
- #print "</%s> is not real!" % name
- self.handle_data('</%s>' % name)
- return
- self.endData()
- self._popToTag(name)
- if self.quoteStack and self.quoteStack[-1] == name:
- self.quoteStack.pop()
- self.literal = (len(self.quoteStack) > 0)
-
- def handle_data(self, data):
- self.currentData.append(data)
-
- def _toStringSubclass(self, text, subclass):
- """Adds a certain piece of text to the tree as a NavigableString
- subclass."""
- self.endData()
- self.handle_data(text)
- self.endData(subclass)
-
- def handle_pi(self, text):
- """Handle a processing instruction as a ProcessingInstruction
- object, possibly one with a %SOUP-ENCODING% slot into which an
- encoding will be plugged later."""
- if text[:3] == "xml":
- text = u"xml version='1.0' encoding='%SOUP-ENCODING%'"
- self._toStringSubclass(text, ProcessingInstruction)
-
- def handle_comment(self, text):
- "Handle comments as Comment objects."
- self._toStringSubclass(text, Comment)
-
- def handle_charref(self, ref):
- "Handle character references as data."
- if self.convertEntities:
- data = unichr(int(ref))
- else:
- data = '&#%s;' % ref
- self.handle_data(data)
-
- def handle_entityref(self, ref):
- """Handle entity references as data, possibly converting known
- HTML and/or XML entity references to the corresponding Unicode
- characters."""
- data = None
- if self.convertHTMLEntities:
- try:
- data = unichr(name2codepoint[ref])
- except KeyError:
- pass
-
- if not data and self.convertXMLEntities:
- data = self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref)
-
- if not data and self.convertHTMLEntities and \
- not self.XML_ENTITIES_TO_SPECIAL_CHARS.get(ref):
- # TODO: We've got a problem here. We're told this is
- # an entity reference, but it's not an XML entity
- # reference or an HTML entity reference. Nonetheless,
- # the logical thing to do is to pass it through as an
- # unrecognized entity reference.
- #
- # Except: when the input is "&carol;" this function
- # will be called with input "carol". When the input is
- # "AT&T", this function will be called with input
- # "T". We have no way of knowing whether a semicolon
- # was present originally, so we don't know whether
- # this is an unknown entity or just a misplaced
- # ampersand.
- #
- # The more common case is a misplaced ampersand, so I
- # escape the ampersand and omit the trailing semicolon.
- data = "&amp;%s" % ref
- if not data:
- # This case is different from the one above, because we
- # haven't already gone through a supposedly comprehensive
- # mapping of entities to Unicode characters. We might not
- # have gone through any mapping at all. So the chances are
- # very high that this is a real entity, and not a
- # misplaced ampersand.
- data = "&%s;" % ref
- self.handle_data(data)
-
- def handle_decl(self, data):
- "Handle DOCTYPEs and the like as Declaration objects."
- self._toStringSubclass(data, Declaration)
-
- def parse_declaration(self, i):
- """Treat a bogus SGML declaration as raw data. Treat a CDATA
- declaration as a CData object."""
- j = None
- if self.rawdata[i:i+9] == '<![CDATA[':
- k = self.rawdata.find(']]>', i)
- if k == -1:
- k = len(self.rawdata)
- data = self.rawdata[i+9:k]
- j = k+3
- self._toStringSubclass(data, CData)
- else:
- try:
- j = SGMLParser.parse_declaration(self, i)
- except SGMLParseError:
- toHandle = self.rawdata[i:]
- self.handle_data(toHandle)
- j = i + len(toHandle)
- return j
-
-class BeautifulSoup(BeautifulStoneSoup):
-
- """This parser knows the following facts about HTML:
-
- * Some tags have no closing tag and should be interpreted as being
- closed as soon as they are encountered.
-
- * The text inside some tags (ie. 'script') may contain tags which
- are not really part of the document and which should be parsed
- as text, not tags. If you want to parse the text as tags, you can
- always fetch it and parse it explicitly.
-
- * Tag nesting rules:
-
- Most tags can't be nested at all. For instance, the occurance of
- a <p> tag should implicitly close the previous <p> tag.
-
- <p>Para1<p>Para2
- should be transformed into:
- <p>Para1</p><p>Para2
-
- Some tags can be nested arbitrarily. For instance, the occurance
- of a <blockquote> tag should _not_ implicitly close the previous
- <blockquote> tag.
-
- Alice said: <blockquote>Bob said: <blockquote>Blah
- should NOT be transformed into:
- Alice said: <blockquote>Bob said: </blockquote><blockquote>Blah
-
- Some tags can be nested, but the nesting is reset by the
- interposition of other tags. For instance, a <tr> tag should
- implicitly close the previous <tr> tag within the same <table>,
- but not close a <tr> tag in another table.
-
- <table><tr>Blah<tr>Blah
- should be transformed into:
- <table><tr>Blah</tr><tr>Blah
- but,
- <tr>Blah<table><tr>Blah
- should NOT be transformed into
- <tr>Blah<table></tr><tr>Blah
-
- Differing assumptions about tag nesting rules are a major source
- of problems with the BeautifulSoup class. If BeautifulSoup is not
- treating as nestable a tag your page author treats as nestable,
- try ICantBelieveItsBeautifulSoup, MinimalSoup, or
- BeautifulStoneSoup before writing your own subclass."""
-
- def __init__(self, *args, **kwargs):
- if not kwargs.has_key('smartQuotesTo'):
- kwargs['smartQuotesTo'] = self.HTML_ENTITIES
- kwargs['isHTML'] = True
- BeautifulStoneSoup.__init__(self, *args, **kwargs)
-
- SELF_CLOSING_TAGS = buildTagMap(None,
- ('br' , 'hr', 'input', 'img', 'meta',
- 'spacer', 'link', 'frame', 'base', 'col'))
-
- PRESERVE_WHITESPACE_TAGS = set(['pre', 'textarea'])
-
- QUOTE_TAGS = {'script' : None, 'textarea' : None}
-
- #According to the HTML standard, each of these inline tags can
- #contain another tag of the same type. Furthermore, it's common
- #to actually use these tags this way.
- NESTABLE_INLINE_TAGS = ('span', 'font', 'q', 'object', 'bdo', 'sub', 'sup',
- 'center')
-
- #According to the HTML standard, these block tags can contain
- #another tag of the same type. Furthermore, it's common
- #to actually use these tags this way.
- NESTABLE_BLOCK_TAGS = ('blockquote', 'div', 'fieldset', 'ins', 'del')
-
- #Lists can contain other lists, but there are restrictions.
- NESTABLE_LIST_TAGS = { 'ol' : [],
- 'ul' : [],
- 'li' : ['ul', 'ol'],
- 'dl' : [],
- 'dd' : ['dl'],
- 'dt' : ['dl'] }
-
- #Tables can contain other tables, but there are restrictions.
- NESTABLE_TABLE_TAGS = {'table' : [],
- 'tr' : ['table', 'tbody', 'tfoot', 'thead'],
- 'td' : ['tr'],
- 'th' : ['tr'],
- 'thead' : ['table'],
- 'tbody' : ['table'],
- 'tfoot' : ['table'],
- }
-
- NON_NESTABLE_BLOCK_TAGS = ('address', 'form', 'p', 'pre')
-
- #If one of these tags is encountered, all tags up to the next tag of
- #this type are popped.
- RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript',
- NON_NESTABLE_BLOCK_TAGS,
- NESTABLE_LIST_TAGS,
- NESTABLE_TABLE_TAGS)
-
- NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS,
- NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
-
- # Used to detect the charset in a META tag; see start_meta
- CHARSET_RE = re.compile("((^|;)\s*charset=)([^;]*)", re.M)
-
- def start_meta(self, attrs):
- """Beautiful Soup can detect a charset included in a META tag,
- try to convert the document to that charset, and re-parse the
- document from the beginning."""
- httpEquiv = None
- contentType = None
- contentTypeIndex = None
- tagNeedsEncodingSubstitution = False
-
- for i in range(0, len(attrs)):
- key, value = attrs[i]
- key = key.lower()
- if key == 'http-equiv':
- httpEquiv = value
- elif key == 'content':
- contentType = value
- contentTypeIndex = i
-
- if httpEquiv and contentType: # It's an interesting meta tag.
- match = self.CHARSET_RE.search(contentType)
- if match:
- if (self.declaredHTMLEncoding is not None or
- self.originalEncoding == self.fromEncoding):
- # An HTML encoding was sniffed while converting
- # the document to Unicode, or an HTML encoding was
- # sniffed during a previous pass through the
- # document, or an encoding was specified
- # explicitly and it worked. Rewrite the meta tag.
- def rewrite(match):
- return match.group(1) + "%SOUP-ENCODING%"
- newAttr = self.CHARSET_RE.sub(rewrite, contentType)
- attrs[contentTypeIndex] = (attrs[contentTypeIndex][0],
- newAttr)
- tagNeedsEncodingSubstitution = True
- else:
- # This is our first pass through the document.
- # Go through it again with the encoding information.
- newCharset = match.group(3)
- if newCharset and newCharset != self.originalEncoding:
- self.declaredHTMLEncoding = newCharset
- self._feed(self.declaredHTMLEncoding)
- raise StopParsing
- pass
- tag = self.unknown_starttag("meta", attrs)
- if tag and tagNeedsEncodingSubstitution:
- tag.containsSubstitutions = True
-
-class StopParsing(Exception):
- pass
-
-class ICantBelieveItsBeautifulSoup(BeautifulSoup):
-
- """The BeautifulSoup class is oriented towards skipping over
- common HTML errors like unclosed tags. However, sometimes it makes
- errors of its own. For instance, consider this fragment:
-
- <b>Foo<b>Bar</b></b>
-
- This is perfectly valid (if bizarre) HTML. However, the
- BeautifulSoup class will implicitly close the first b tag when it
- encounters the second 'b'. It will think the author wrote
- "<b>Foo<b>Bar", and didn't close the first 'b' tag, because
- there's no real-world reason to bold something that's already
- bold. When it encounters '</b></b>' it will close two more 'b'
- tags, for a grand total of three tags closed instead of two. This
- can throw off the rest of your document structure. The same is
- true of a number of other tags, listed below.
-
- It's much more common for someone to forget to close a 'b' tag
- than to actually use nested 'b' tags, and the BeautifulSoup class
- handles the common case. This class handles the not-co-common
- case: where you can't believe someone wrote what they did, but
- it's valid HTML and BeautifulSoup screwed up by assuming it
- wouldn't be."""
-
- I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = \
- ('em', 'big', 'i', 'small', 'tt', 'abbr', 'acronym', 'strong',
- 'cite', 'code', 'dfn', 'kbd', 'samp', 'strong', 'var', 'b',
- 'big')
-
- I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = ('noscript',)
-
- NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS,
- I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS,
- I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS)
-
-class MinimalSoup(BeautifulSoup):
- """The MinimalSoup class is for parsing HTML that contains
- pathologically bad markup. It makes no assumptions about tag
- nesting, but it does know which tags are self-closing, that
- <script> tags contain Javascript and should not be parsed, that
- META tags may contain encoding information, and so on.
-
- This also makes it better for subclassing than BeautifulStoneSoup
- or BeautifulSoup."""
-
- RESET_NESTING_TAGS = buildTagMap('noscript')
- NESTABLE_TAGS = {}
-
-class BeautifulSOAP(BeautifulStoneSoup):
- """This class will push a tag with only a single string child into
- the tag's parent as an attribute. The attribute's name is the tag
- name, and the value is the string child. An example should give
- the flavor of the change:
-
- <foo><bar>baz</bar></foo>
- =>
- <foo bar="baz"><bar>baz</bar></foo>
-
- You can then access fooTag['bar'] instead of fooTag.barTag.string.
-
- This is, of course, useful for scraping structures that tend to
- use subelements instead of attributes, such as SOAP messages. Note
- that it modifies its input, so don't print the modified version
- out.
-
- I'm not sure how many people really want to use this class; let me
- know if you do. Mainly I like the name."""
-
- def popTag(self):
- if len(self.tagStack) > 1:
- tag = self.tagStack[-1]
- parent = self.tagStack[-2]
- parent._getAttrMap()
- if (isinstance(tag, Tag) and len(tag.contents) == 1 and
- isinstance(tag.contents[0], NavigableString) and
- not parent.attrMap.has_key(tag.name)):
- parent[tag.name] = tag.contents[0]
- BeautifulStoneSoup.popTag(self)
-
-#Enterprise class names! It has come to our attention that some people
-#think the names of the Beautiful Soup parser classes are too silly
-#and "unprofessional" for use in enterprise screen-scraping. We feel
-#your pain! For such-minded folk, the Beautiful Soup Consortium And
-#All-Night Kosher Bakery recommends renaming this file to
-#"RobustParser.py" (or, in cases of extreme enterprisiness,
-#"RobustParserBeanInterface.class") and using the following
-#enterprise-friendly class aliases:
-class RobustXMLParser(BeautifulStoneSoup):
- pass
-class RobustHTMLParser(BeautifulSoup):
- pass
-class RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup):
- pass
-class RobustInsanelyWackAssHTMLParser(MinimalSoup):
- pass
-class SimplifyingSOAPParser(BeautifulSOAP):
- pass
-
-######################################################
-#
-# Bonus library: Unicode, Dammit
-#
-# This class forces XML data into a standard format (usually to UTF-8
-# or Unicode). It is heavily based on code from Mark Pilgrim's
-# Universal Feed Parser. It does not rewrite the XML or HTML to
-# reflect a new encoding: that happens in BeautifulStoneSoup.handle_pi
-# (XML) and BeautifulSoup.start_meta (HTML).
-
-# Autodetects character encodings.
-# Download from http://chardet.feedparser.org/
-try:
- import chardet
-# import chardet.constants
-# chardet.constants._debug = 1
-except ImportError:
- chardet = None
-
-# cjkcodecs and iconv_codec make Python know about more character encodings.
-# Both are available from http://cjkpython.i18n.org/
-# They're built in if you use Python 2.4.
-try:
- import cjkcodecs.aliases
-except ImportError:
- pass
-try:
- import iconv_codec
-except ImportError:
- pass
-
-class UnicodeDammit:
- """A class for detecting the encoding of a *ML document and
- converting it to a Unicode string. If the source encoding is
- windows-1252, can replace MS smart quotes with their HTML or XML
- equivalents."""
-
- # This dictionary maps commonly seen values for "charset" in HTML
- # meta tags to the corresponding Python codec names. It only covers
- # values that aren't in Python's aliases and can't be determined
- # by the heuristics in find_codec.
- CHARSET_ALIASES = { "macintosh" : "mac-roman",
- "x-sjis" : "shift-jis" }
-
- def __init__(self, markup, overrideEncodings=[],
- smartQuotesTo='xml', isHTML=False):
- self.declaredHTMLEncoding = None
- self.markup, documentEncoding, sniffedEncoding = \
- self._detectEncoding(markup, isHTML)
- self.smartQuotesTo = smartQuotesTo
- self.triedEncodings = []
- if markup == '' or isinstance(markup, unicode):
- self.originalEncoding = None
- self.unicode = unicode(markup)
- return
-
- u = None
- for proposedEncoding in overrideEncodings:
- u = self._convertFrom(proposedEncoding)
- if u: break
- if not u:
- for proposedEncoding in (documentEncoding, sniffedEncoding):
- u = self._convertFrom(proposedEncoding)
- if u: break
-
- # If no luck and we have auto-detection library, try that:
- if not u and chardet and not isinstance(self.markup, unicode):
- u = self._convertFrom(chardet.detect(self.markup)['encoding'])
-
- # As a last resort, try utf-8 and windows-1252:
- if not u:
- for proposed_encoding in ("utf-8", "windows-1252"):
- u = self._convertFrom(proposed_encoding)
- if u: break
-
- self.unicode = u
- if not u: self.originalEncoding = None
-
- def _subMSChar(self, orig):
- """Changes a MS smart quote character to an XML or HTML
- entity."""
- sub = self.MS_CHARS.get(orig)
- if isinstance(sub, tuple):
- if self.smartQuotesTo == 'xml':
- sub = '&#x%s;' % sub[1]
- else:
- sub = '&%s;' % sub[0]
- return sub
-
- def _convertFrom(self, proposed):
- proposed = self.find_codec(proposed)
- if not proposed or proposed in self.triedEncodings:
- return None
- self.triedEncodings.append(proposed)
- markup = self.markup
-
- # Convert smart quotes to HTML if coming from an encoding
- # that might have them.
- if self.smartQuotesTo and proposed.lower() in("windows-1252",
- "iso-8859-1",
- "iso-8859-2"):
- markup = re.compile("([\x80-\x9f])").sub \
- (lambda(x): self._subMSChar(x.group(1)),
- markup)
-
- try:
- # print "Trying to convert document to %s" % proposed
- u = self._toUnicode(markup, proposed)
- self.markup = u
- self.originalEncoding = proposed
- except Exception, e:
- # print "That didn't work!"
- # print e
- return None
- #print "Correct encoding: %s" % proposed
- return self.markup
-
- def _toUnicode(self, data, encoding):
- '''Given a string and its encoding, decodes the string into Unicode.
- %encoding is a string recognized by encodings.aliases'''
-
- # strip Byte Order Mark (if present)
- if (len(data) >= 4) and (data[:2] == '\xfe\xff') \
- and (data[2:4] != '\x00\x00'):
- encoding = 'utf-16be'
- data = data[2:]
- elif (len(data) >= 4) and (data[:2] == '\xff\xfe') \
- and (data[2:4] != '\x00\x00'):
- encoding = 'utf-16le'
- data = data[2:]
- elif data[:3] == '\xef\xbb\xbf':
- encoding = 'utf-8'
- data = data[3:]
- elif data[:4] == '\x00\x00\xfe\xff':
- encoding = 'utf-32be'
- data = data[4:]
- elif data[:4] == '\xff\xfe\x00\x00':
- encoding = 'utf-32le'
- data = data[4:]
- newdata = unicode(data, encoding)
- return newdata
-
- def _detectEncoding(self, xml_data, isHTML=False):
- """Given a document, tries to detect its XML encoding."""
- xml_encoding = sniffed_xml_encoding = None
- try:
- if xml_data[:4] == '\x4c\x6f\xa7\x94':
- # EBCDIC
- xml_data = self._ebcdic_to_ascii(xml_data)
- elif xml_data[:4] == '\x00\x3c\x00\x3f':
- # UTF-16BE
- sniffed_xml_encoding = 'utf-16be'
- xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
- elif (len(xml_data) >= 4) and (xml_data[:2] == '\xfe\xff') \
- and (xml_data[2:4] != '\x00\x00'):
- # UTF-16BE with BOM
- sniffed_xml_encoding = 'utf-16be'
- xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
- elif xml_data[:4] == '\x3c\x00\x3f\x00':
- # UTF-16LE
- sniffed_xml_encoding = 'utf-16le'
- xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
- elif (len(xml_data) >= 4) and (xml_data[:2] == '\xff\xfe') and \
- (xml_data[2:4] != '\x00\x00'):
- # UTF-16LE with BOM
- sniffed_xml_encoding = 'utf-16le'
- xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
- elif xml_data[:4] == '\x00\x00\x00\x3c':
- # UTF-32BE
- sniffed_xml_encoding = 'utf-32be'
- xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
- elif xml_data[:4] == '\x3c\x00\x00\x00':
- # UTF-32LE
- sniffed_xml_encoding = 'utf-32le'
- xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
- elif xml_data[:4] == '\x00\x00\xfe\xff':
- # UTF-32BE with BOM
- sniffed_xml_encoding = 'utf-32be'
- xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
- elif xml_data[:4] == '\xff\xfe\x00\x00':
- # UTF-32LE with BOM
- sniffed_xml_encoding = 'utf-32le'
- xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
- elif xml_data[:3] == '\xef\xbb\xbf':
- # UTF-8 with BOM
- sniffed_xml_encoding = 'utf-8'
- xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
- else:
- sniffed_xml_encoding = 'ascii'
- pass
- except:
- xml_encoding_match = None
- xml_encoding_match = re.compile(
- '^<\?.*encoding=[\'"](.*?)[\'"].*\?>').match(xml_data)
- if not xml_encoding_match and isHTML:
- regexp = re.compile('<\s*meta[^>]+charset=([^>]*?)[;\'">]', re.I)
- xml_encoding_match = regexp.search(xml_data)
- if xml_encoding_match is not None:
- xml_encoding = xml_encoding_match.groups()[0].lower()
- if isHTML:
- self.declaredHTMLEncoding = xml_encoding
- if sniffed_xml_encoding and \
- (xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode',
- 'iso-10646-ucs-4', 'ucs-4', 'csucs4',
- 'utf-16', 'utf-32', 'utf_16', 'utf_32',
- 'utf16', 'u16')):
- xml_encoding = sniffed_xml_encoding
- return xml_data, xml_encoding, sniffed_xml_encoding
-
-
- def find_codec(self, charset):
- return self._codec(self.CHARSET_ALIASES.get(charset, charset)) \
- or (charset and self._codec(charset.replace("-", ""))) \
- or (charset and self._codec(charset.replace("-", "_"))) \
- or charset
-
- def _codec(self, charset):
- if not charset: return charset
- codec = None
- try:
- codecs.lookup(charset)
- codec = charset
- except (LookupError, ValueError):
- pass
- return codec
-
- EBCDIC_TO_ASCII_MAP = None
- def _ebcdic_to_ascii(self, s):
- c = self.__class__
- if not c.EBCDIC_TO_ASCII_MAP:
- emap = (0,1,2,3,156,9,134,127,151,141,142,11,12,13,14,15,
- 16,17,18,19,157,133,8,135,24,25,146,143,28,29,30,31,
- 128,129,130,131,132,10,23,27,136,137,138,139,140,5,6,7,
- 144,145,22,147,148,149,150,4,152,153,154,155,20,21,158,26,
- 32,160,161,162,163,164,165,166,167,168,91,46,60,40,43,33,
- 38,169,170,171,172,173,174,175,176,177,93,36,42,41,59,94,
- 45,47,178,179,180,181,182,183,184,185,124,44,37,95,62,63,
- 186,187,188,189,190,191,192,193,194,96,58,35,64,39,61,34,
- 195,97,98,99,100,101,102,103,104,105,196,197,198,199,200,
- 201,202,106,107,108,109,110,111,112,113,114,203,204,205,
- 206,207,208,209,126,115,116,117,118,119,120,121,122,210,
- 211,212,213,214,215,216,217,218,219,220,221,222,223,224,
- 225,226,227,228,229,230,231,123,65,66,67,68,69,70,71,72,
- 73,232,233,234,235,236,237,125,74,75,76,77,78,79,80,81,
- 82,238,239,240,241,242,243,92,159,83,84,85,86,87,88,89,
- 90,244,245,246,247,248,249,48,49,50,51,52,53,54,55,56,57,
- 250,251,252,253,254,255)
- import string
- c.EBCDIC_TO_ASCII_MAP = string.maketrans( \
- ''.join(map(chr, range(256))), ''.join(map(chr, emap)))
- return s.translate(c.EBCDIC_TO_ASCII_MAP)
-
- MS_CHARS = { '\x80' : ('euro', '20AC'),
- '\x81' : ' ',
- '\x82' : ('sbquo', '201A'),
- '\x83' : ('fnof', '192'),
- '\x84' : ('bdquo', '201E'),
- '\x85' : ('hellip', '2026'),
- '\x86' : ('dagger', '2020'),
- '\x87' : ('Dagger', '2021'),
- '\x88' : ('circ', '2C6'),
- '\x89' : ('permil', '2030'),
- '\x8A' : ('Scaron', '160'),
- '\x8B' : ('lsaquo', '2039'),
- '\x8C' : ('OElig', '152'),
- '\x8D' : '?',
- '\x8E' : ('#x17D', '17D'),
- '\x8F' : '?',
- '\x90' : '?',
- '\x91' : ('lsquo', '2018'),
- '\x92' : ('rsquo', '2019'),
- '\x93' : ('ldquo', '201C'),
- '\x94' : ('rdquo', '201D'),
- '\x95' : ('bull', '2022'),
- '\x96' : ('ndash', '2013'),
- '\x97' : ('mdash', '2014'),
- '\x98' : ('tilde', '2DC'),
- '\x99' : ('trade', '2122'),
- '\x9a' : ('scaron', '161'),
- '\x9b' : ('rsaquo', '203A'),
- '\x9c' : ('oelig', '153'),
- '\x9d' : '?',
- '\x9e' : ('#x17E', '17E'),
- '\x9f' : ('Yuml', ''),}
-
-#######################################################################
-
-
-#By default, act as an HTML pretty-printer.
-if __name__ == '__main__':
- import sys
- soup = BeautifulSoup(sys.stdin)
- print soup.prettify()
diff --git a/module/lib/Getch.py b/module/lib/Getch.py
deleted file mode 100644
index 22b7ea7f8..000000000
--- a/module/lib/Getch.py
+++ /dev/null
@@ -1,76 +0,0 @@
-class Getch:
- """
- Gets a single character from standard input. Does not echo to
- the screen.
- """
-
- def __init__(self):
- try:
- self.impl = _GetchWindows()
- except ImportError:
- try:
- self.impl = _GetchMacCarbon()
- except(AttributeError, ImportError):
- self.impl = _GetchUnix()
-
- def __call__(self): return self.impl()
-
-
-class _GetchUnix:
- def __init__(self):
- import tty
- import sys
-
- def __call__(self):
- import sys
- import tty
- import termios
-
- fd = sys.stdin.fileno()
- old_settings = termios.tcgetattr(fd)
- try:
- tty.setraw(sys.stdin.fileno())
- ch = sys.stdin.read(1)
- finally:
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
- return ch
-
-
-class _GetchWindows:
- def __init__(self):
- import msvcrt
-
- def __call__(self):
- import msvcrt
-
- return msvcrt.getch()
-
-class _GetchMacCarbon:
- """
- A function which returns the current ASCII key that is down;
- if no ASCII key is down, the null string is returned. The
- page http://www.mactech.com/macintosh-c/chap02-1.html was
- very helpful in figuring out how to do this.
- """
-
- def __init__(self):
- import Carbon
- Carbon.Evt #see if it has this (in Unix, it doesn't)
-
- def __call__(self):
- import Carbon
-
- if Carbon.Evt.EventAvail(0x0008)[0] == 0: # 0x0008 is the keyDownMask
- return ''
- else:
- #
- # The event contains the following info:
- # (what,msg,when,where,mod)=Carbon.Evt.GetNextEvent(0x0008)[1]
- #
- # The message (msg) contains the ASCII char which is
- # extracted with the 0x000000FF charCodeMask; this
- # number is converted to an ASCII character with chr() and
- # returned
- #
- (what, msg, when, where, mod) = Carbon.Evt.GetNextEvent(0x0008)[1]
- return chr(msg) \ No newline at end of file
diff --git a/module/lib/MultipartPostHandler.py b/module/lib/MultipartPostHandler.py
deleted file mode 100644
index 94aee0193..000000000
--- a/module/lib/MultipartPostHandler.py
+++ /dev/null
@@ -1,139 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-####
-# 02/2006 Will Holcomb <wholcomb@gmail.com>
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2.1 of the License, or (at your option) any later version.
-#
-# This library 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
-# Lesser General Public License for more details.
-#
-# 7/26/07 Slightly modified by Brian Schneider
-# in order to support unicode files ( multipart_encode function )
-"""
-Usage:
- Enables the use of multipart/form-data for posting forms
-
-Inspirations:
- Upload files in python:
- http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
- urllib2_file:
- Fabien Seisen: <fabien@seisen.org>
-
-Example:
- import MultipartPostHandler, urllib2, cookielib
-
- cookies = cookielib.CookieJar()
- opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies),
- MultipartPostHandler.MultipartPostHandler)
- params = { "username" : "bob", "password" : "riviera",
- "file" : open("filename", "rb") }
- opener.open("http://wwww.bobsite.com/upload/", params)
-
-Further Example:
- The main function of this file is a sample which downloads a page and
- then uploads it to the W3C validator.
-"""
-
-from urllib import urlencode
-from urllib2 import BaseHandler, HTTPHandler, build_opener
-import mimetools, mimetypes
-from os import write, remove
-from cStringIO import StringIO
-
-class Callable:
- def __init__(self, anycallable):
- self.__call__ = anycallable
-
-# Controls how sequences are uncoded. If true, elements may be given multiple values by
-# assigning a sequence.
-doseq = 1
-
-class MultipartPostHandler(BaseHandler):
- handler_order = HTTPHandler.handler_order - 10 # needs to run first
-
- def http_request(self, request):
- data = request.get_data()
- if data is not None and type(data) != str:
- v_files = []
- v_vars = []
- try:
- for(key, value) in data.items():
- if type(value) == file:
- v_files.append((key, value))
- else:
- v_vars.append((key, value))
- except TypeError:
- systype, value, traceback = sys.exc_info()
- raise TypeError, "not a valid non-string sequence or mapping object", traceback
-
- if len(v_files) == 0:
- data = urlencode(v_vars, doseq)
- else:
- boundary, data = self.multipart_encode(v_vars, v_files)
-
- contenttype = 'multipart/form-data; boundary=%s' % boundary
- if(request.has_header('Content-Type')
- and request.get_header('Content-Type').find('multipart/form-data') != 0):
- print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data')
- request.add_unredirected_header('Content-Type', contenttype)
-
- request.add_data(data)
-
- return request
-
- def multipart_encode(vars, files, boundary = None, buf = None):
- if boundary is None:
- boundary = mimetools.choose_boundary()
- if buf is None:
- buf = StringIO()
- for(key, value) in vars:
- buf.write('--%s\r\n' % boundary)
- buf.write('Content-Disposition: form-data; name="%s"' % key)
- buf.write('\r\n\r\n' + value + '\r\n')
- for(key, fd) in files:
- #file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
- filename = fd.name.split('/')[-1]
- contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
- buf.write('--%s\r\n' % boundary)
- buf.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename))
- buf.write('Content-Type: %s\r\n' % contenttype)
- # buffer += 'Content-Length: %s\r\n' % file_size
- fd.seek(0)
- buf.write('\r\n' + fd.read() + '\r\n')
- buf.write('--' + boundary + '--\r\n\r\n')
- buf = buf.getvalue()
- return boundary, buf
- multipart_encode = Callable(multipart_encode)
-
- https_request = http_request
-
-def main():
- import tempfile, sys
-
- validatorURL = "http://validator.w3.org/check"
- opener = build_opener(MultipartPostHandler)
-
- def validateFile(url):
- temp = tempfile.mkstemp(suffix=".html")
- write(temp[0], opener.open(url).read())
- params = { "ss" : "0", # show source
- "doctype" : "Inline",
- "uploaded_file" : open(temp[1], "rb") }
- print opener.open(validatorURL, params).read()
- remove(temp[1])
-
- if len(sys.argv[1:]) > 0:
- for arg in sys.argv[1:]:
- validateFile(arg)
- else:
- validateFile("http://www.google.com")
-
-if __name__=="__main__":
- main()
diff --git a/module/lib/SafeEval.py b/module/lib/SafeEval.py
deleted file mode 100644
index 8fc57f261..000000000
--- a/module/lib/SafeEval.py
+++ /dev/null
@@ -1,47 +0,0 @@
-## {{{ http://code.activestate.com/recipes/286134/ (r3) (modified)
-import dis
-
-_const_codes = map(dis.opmap.__getitem__, [
- 'POP_TOP','ROT_TWO','ROT_THREE','ROT_FOUR','DUP_TOP',
- 'BUILD_LIST','BUILD_MAP','BUILD_TUPLE',
- 'LOAD_CONST','RETURN_VALUE','STORE_SUBSCR'
- ])
-
-
-_load_names = ['False', 'True', 'null', 'true', 'false']
-
-_locals = {'null': None, 'true': True, 'false': False}
-
-def _get_opcodes(codeobj):
- i = 0
- opcodes = []
- s = codeobj.co_code
- names = codeobj.co_names
- while i < len(s):
- code = ord(s[i])
- opcodes.append(code)
- if code >= dis.HAVE_ARGUMENT:
- i += 3
- else:
- i += 1
- return opcodes, names
-
-def test_expr(expr, allowed_codes):
- try:
- c = compile(expr, "", "eval")
- except:
- raise ValueError, "%s is not a valid expression" % expr
- codes, names = _get_opcodes(c)
- for code in codes:
- if code not in allowed_codes:
- for n in names:
- if n not in _load_names:
- raise ValueError, "opcode %s not allowed" % dis.opname[code]
- return c
-
-
-def const_eval(expr):
- c = test_expr(expr, _const_codes)
- return eval(c, None, _locals)
-
-## end of http://code.activestate.com/recipes/286134/ }}}
diff --git a/module/lib/Unzip.py b/module/lib/Unzip.py
deleted file mode 100644
index f56fbe751..000000000
--- a/module/lib/Unzip.py
+++ /dev/null
@@ -1,50 +0,0 @@
-import zipfile
-import os
-
-class Unzip:
- def __init__(self):
- pass
-
- def extract(self, file, dir):
- if not dir.endswith(':') and not os.path.exists(dir):
- os.mkdir(dir)
-
- zf = zipfile.ZipFile(file)
-
- # create directory structure to house files
- self._createstructure(file, dir)
-
- # extract files to directory structure
- for i, name in enumerate(zf.namelist()):
-
- if not name.endswith('/') and not name.endswith("config"):
- print "extracting", name.replace("pyload/","")
- outfile = open(os.path.join(dir, name.replace("pyload/","")), 'wb')
- outfile.write(zf.read(name))
- outfile.flush()
- outfile.close()
-
- def _createstructure(self, file, dir):
- self._makedirs(self._listdirs(file), dir)
-
- def _makedirs(self, directories, basedir):
- """ Create any directories that don't currently exist """
- for dir in directories:
- curdir = os.path.join(basedir, dir)
- if not os.path.exists(curdir):
- os.mkdir(curdir)
-
- def _listdirs(self, file):
- """ Grabs all the directories in the zip structure
- This is necessary to create the structure before trying
- to extract the file to it. """
- zf = zipfile.ZipFile(file)
-
- dirs = []
-
- for name in zf.namelist():
- if name.endswith('/'):
- dirs.append(name.replace("pyload/",""))
-
- dirs.sort()
- return dirs
diff --git a/module/lib/__init__.py b/module/lib/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/lib/__init__.py
+++ /dev/null
diff --git a/module/lib/beaker/__init__.py b/module/lib/beaker/__init__.py
deleted file mode 100644
index 792d60054..000000000
--- a/module/lib/beaker/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-#
diff --git a/module/lib/beaker/cache.py b/module/lib/beaker/cache.py
deleted file mode 100644
index 4a96537ff..000000000
--- a/module/lib/beaker/cache.py
+++ /dev/null
@@ -1,459 +0,0 @@
-"""Cache object
-
-The Cache object is used to manage a set of cache files and their
-associated backend. The backends can be rotated on the fly by
-specifying an alternate type when used.
-
-Advanced users can add new backends in beaker.backends
-
-"""
-
-import warnings
-
-import beaker.container as container
-import beaker.util as util
-from beaker.exceptions import BeakerException, InvalidCacheBackendError
-
-import beaker.ext.memcached as memcached
-import beaker.ext.database as database
-import beaker.ext.sqla as sqla
-import beaker.ext.google as google
-
-# Initialize the basic available backends
-clsmap = {
- 'memory':container.MemoryNamespaceManager,
- 'dbm':container.DBMNamespaceManager,
- 'file':container.FileNamespaceManager,
- 'ext:memcached':memcached.MemcachedNamespaceManager,
- 'ext:database':database.DatabaseNamespaceManager,
- 'ext:sqla': sqla.SqlaNamespaceManager,
- 'ext:google': google.GoogleNamespaceManager,
- }
-
-# Initialize the cache region dict
-cache_regions = {}
-cache_managers = {}
-
-try:
- import pkg_resources
-
- # Load up the additional entry point defined backends
- for entry_point in pkg_resources.iter_entry_points('beaker.backends'):
- try:
- NamespaceManager = entry_point.load()
- name = entry_point.name
- if name in clsmap:
- raise BeakerException("NamespaceManager name conflict,'%s' "
- "already loaded" % name)
- clsmap[name] = NamespaceManager
- except (InvalidCacheBackendError, SyntaxError):
- # Ignore invalid backends
- pass
- except:
- import sys
- from pkg_resources import DistributionNotFound
- # Warn when there's a problem loading a NamespaceManager
- if not isinstance(sys.exc_info()[1], DistributionNotFound):
- import traceback
- from StringIO import StringIO
- tb = StringIO()
- traceback.print_exc(file=tb)
- warnings.warn("Unable to load NamespaceManager entry point: '%s': "
- "%s" % (entry_point, tb.getvalue()), RuntimeWarning,
- 2)
-except ImportError:
- pass
-
-
-
-
-def cache_region(region, *deco_args):
- """Decorate a function to cache itself using a cache region
-
- The region decorator requires arguments if there are more than
- 2 of the same named function, in the same module. This is
- because the namespace used for the functions cache is based on
- the functions name and the module.
-
-
- Example::
-
- # Add cache region settings to beaker:
- beaker.cache.cache_regions.update(dict_of_config_region_options))
-
- @cache_region('short_term', 'some_data')
- def populate_things(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- return load('rabbits', 20, 0)
-
- .. note::
-
- The function being decorated must only be called with
- positional arguments.
-
- """
- cache = [None]
-
- def decorate(func):
- namespace = util.func_namespace(func)
- def cached(*args):
- reg = cache_regions[region]
- if not reg.get('enabled', True):
- return func(*args)
-
- if not cache[0]:
- if region not in cache_regions:
- raise BeakerException('Cache region not configured: %s' % region)
- cache[0] = Cache._get_cache(namespace, reg)
-
- cache_key = " ".join(map(str, deco_args + args))
- def go():
- return func(*args)
-
- return cache[0].get_value(cache_key, createfunc=go)
- cached._arg_namespace = namespace
- cached._arg_region = region
- return cached
- return decorate
-
-
-def region_invalidate(namespace, region, *args):
- """Invalidate a cache region namespace or decorated function
-
- This function only invalidates cache spaces created with the
- cache_region decorator.
-
- :param namespace: Either the namespace of the result to invalidate, or the
- cached function reference
-
- :param region: The region the function was cached to. If the function was
- cached to a single region then this argument can be None
-
- :param args: Arguments that were used to differentiate the cached
- function as well as the arguments passed to the decorated
- function
-
- Example::
-
- # Add cache region settings to beaker:
- beaker.cache.cache_regions.update(dict_of_config_region_options))
-
- def populate_things(invalidate=False):
-
- @cache_region('short_term', 'some_data')
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- # If the results should be invalidated first
- if invalidate:
- region_invalidate(load, None, 'some_data',
- 'rabbits', 20, 0)
- return load('rabbits', 20, 0)
-
- """
- if callable(namespace):
- if not region:
- region = namespace._arg_region
- namespace = namespace._arg_namespace
-
- if not region:
- raise BeakerException("Region or callable function "
- "namespace is required")
- else:
- region = cache_regions[region]
-
- cache = Cache._get_cache(namespace, region)
- cache_key = " ".join(str(x) for x in args)
- cache.remove_value(cache_key)
-
-
-class Cache(object):
- """Front-end to the containment API implementing a data cache.
-
- :param namespace: the namespace of this Cache
-
- :param type: type of cache to use
-
- :param expire: seconds to keep cached data
-
- :param expiretime: seconds to keep cached data (legacy support)
-
- :param starttime: time when cache was cache was
-
- """
- def __init__(self, namespace, type='memory', expiretime=None,
- starttime=None, expire=None, **nsargs):
- try:
- cls = clsmap[type]
- if isinstance(cls, InvalidCacheBackendError):
- raise cls
- except KeyError:
- raise TypeError("Unknown cache implementation %r" % type)
-
- self.namespace = cls(namespace, **nsargs)
- self.expiretime = expiretime or expire
- self.starttime = starttime
- self.nsargs = nsargs
-
- @classmethod
- def _get_cache(cls, namespace, kw):
- key = namespace + str(kw)
- try:
- return cache_managers[key]
- except KeyError:
- cache_managers[key] = cache = cls(namespace, **kw)
- return cache
-
- def put(self, key, value, **kw):
- self._get_value(key, **kw).set_value(value)
- set_value = put
-
- def get(self, key, **kw):
- """Retrieve a cached value from the container"""
- return self._get_value(key, **kw).get_value()
- get_value = get
-
- def remove_value(self, key, **kw):
- mycontainer = self._get_value(key, **kw)
- if mycontainer.has_current_value():
- mycontainer.clear_value()
- remove = remove_value
-
- def _get_value(self, key, **kw):
- if isinstance(key, unicode):
- key = key.encode('ascii', 'backslashreplace')
-
- if 'type' in kw:
- return self._legacy_get_value(key, **kw)
-
- kw.setdefault('expiretime', self.expiretime)
- kw.setdefault('starttime', self.starttime)
-
- return container.Value(key, self.namespace, **kw)
-
- @util.deprecated("Specifying a "
- "'type' and other namespace configuration with cache.get()/put()/etc. "
- "is deprecated. Specify 'type' and other namespace configuration to "
- "cache_manager.get_cache() and/or the Cache constructor instead.")
- def _legacy_get_value(self, key, type, **kw):
- expiretime = kw.pop('expiretime', self.expiretime)
- starttime = kw.pop('starttime', None)
- createfunc = kw.pop('createfunc', None)
- kwargs = self.nsargs.copy()
- kwargs.update(kw)
- c = Cache(self.namespace.namespace, type=type, **kwargs)
- return c._get_value(key, expiretime=expiretime, createfunc=createfunc,
- starttime=starttime)
-
- def clear(self):
- """Clear all the values from the namespace"""
- self.namespace.remove()
-
- # dict interface
- def __getitem__(self, key):
- return self.get(key)
-
- def __contains__(self, key):
- return self._get_value(key).has_current_value()
-
- def has_key(self, key):
- return key in self
-
- def __delitem__(self, key):
- self.remove_value(key)
-
- def __setitem__(self, key, value):
- self.put(key, value)
-
-
-class CacheManager(object):
- def __init__(self, **kwargs):
- """Initialize a CacheManager object with a set of options
-
- Options should be parsed with the
- :func:`~beaker.util.parse_cache_config_options` function to
- ensure only valid options are used.
-
- """
- self.kwargs = kwargs
- self.regions = kwargs.pop('cache_regions', {})
-
- # Add these regions to the module global
- cache_regions.update(self.regions)
-
- def get_cache(self, name, **kwargs):
- kw = self.kwargs.copy()
- kw.update(kwargs)
- return Cache._get_cache(name, kw)
-
- def get_cache_region(self, name, region):
- if region not in self.regions:
- raise BeakerException('Cache region not configured: %s' % region)
- kw = self.regions[region]
- return Cache._get_cache(name, kw)
-
- def region(self, region, *args):
- """Decorate a function to cache itself using a cache region
-
- The region decorator requires arguments if there are more than
- 2 of the same named function, in the same module. This is
- because the namespace used for the functions cache is based on
- the functions name and the module.
-
-
- Example::
-
- # Assuming a cache object is available like:
- cache = CacheManager(dict_of_config_options)
-
-
- def populate_things():
-
- @cache.region('short_term', 'some_data')
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- return load('rabbits', 20, 0)
-
- .. note::
-
- The function being decorated must only be called with
- positional arguments.
-
- """
- return cache_region(region, *args)
-
- def region_invalidate(self, namespace, region, *args):
- """Invalidate a cache region namespace or decorated function
-
- This function only invalidates cache spaces created with the
- cache_region decorator.
-
- :param namespace: Either the namespace of the result to invalidate, or the
- name of the cached function
-
- :param region: The region the function was cached to. If the function was
- cached to a single region then this argument can be None
-
- :param args: Arguments that were used to differentiate the cached
- function as well as the arguments passed to the decorated
- function
-
- Example::
-
- # Assuming a cache object is available like:
- cache = CacheManager(dict_of_config_options)
-
- def populate_things(invalidate=False):
-
- @cache.region('short_term', 'some_data')
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- # If the results should be invalidated first
- if invalidate:
- cache.region_invalidate(load, None, 'some_data',
- 'rabbits', 20, 0)
- return load('rabbits', 20, 0)
-
-
- """
- return region_invalidate(namespace, region, *args)
- if callable(namespace):
- if not region:
- region = namespace._arg_region
- namespace = namespace._arg_namespace
-
- if not region:
- raise BeakerException("Region or callable function "
- "namespace is required")
- else:
- region = self.regions[region]
-
- cache = self.get_cache(namespace, **region)
- cache_key = " ".join(str(x) for x in args)
- cache.remove_value(cache_key)
-
- def cache(self, *args, **kwargs):
- """Decorate a function to cache itself with supplied parameters
-
- :param args: Used to make the key unique for this function, as in region()
- above.
-
- :param kwargs: Parameters to be passed to get_cache(), will override defaults
-
- Example::
-
- # Assuming a cache object is available like:
- cache = CacheManager(dict_of_config_options)
-
-
- def populate_things():
-
- @cache.cache('mycache', expire=15)
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- return load('rabbits', 20, 0)
-
- .. note::
-
- The function being decorated must only be called with
- positional arguments.
-
- """
- cache = [None]
- key = " ".join(str(x) for x in args)
-
- def decorate(func):
- namespace = util.func_namespace(func)
- def cached(*args):
- if not cache[0]:
- cache[0] = self.get_cache(namespace, **kwargs)
- cache_key = key + " " + " ".join(str(x) for x in args)
- def go():
- return func(*args)
- return cache[0].get_value(cache_key, createfunc=go)
- cached._arg_namespace = namespace
- return cached
- return decorate
-
- def invalidate(self, func, *args, **kwargs):
- """Invalidate a cache decorated function
-
- This function only invalidates cache spaces created with the
- cache decorator.
-
- :param func: Decorated function to invalidate
-
- :param args: Used to make the key unique for this function, as in region()
- above.
-
- :param kwargs: Parameters that were passed for use by get_cache(), note that
- this is only required if a ``type`` was specified for the
- function
-
- Example::
-
- # Assuming a cache object is available like:
- cache = CacheManager(dict_of_config_options)
-
-
- def populate_things(invalidate=False):
-
- @cache.cache('mycache', type="file", expire=15)
- def load(search_term, limit, offset):
- return load_the_data(search_term, limit, offset)
-
- # If the results should be invalidated first
- if invalidate:
- cache.invalidate(load, 'mycache', 'rabbits', 20, 0, type="file")
- return load('rabbits', 20, 0)
-
- """
- namespace = func._arg_namespace
-
- cache = self.get_cache(namespace, **kwargs)
- cache_key = " ".join(str(x) for x in args)
- cache.remove_value(cache_key)
diff --git a/module/lib/beaker/container.py b/module/lib/beaker/container.py
deleted file mode 100644
index 515e97af6..000000000
--- a/module/lib/beaker/container.py
+++ /dev/null
@@ -1,633 +0,0 @@
-"""Container and Namespace classes"""
-import anydbm
-import cPickle
-import logging
-import os
-import time
-
-import beaker.util as util
-from beaker.exceptions import CreationAbortedError, MissingCacheParameter
-from beaker.synchronization import _threading, file_synchronizer, \
- mutex_synchronizer, NameLock, null_synchronizer
-
-__all__ = ['Value', 'Container', 'ContainerContext',
- 'MemoryContainer', 'DBMContainer', 'NamespaceManager',
- 'MemoryNamespaceManager', 'DBMNamespaceManager', 'FileContainer',
- 'OpenResourceNamespaceManager',
- 'FileNamespaceManager', 'CreationAbortedError']
-
-
-logger = logging.getLogger('beaker.container')
-if logger.isEnabledFor(logging.DEBUG):
- debug = logger.debug
-else:
- def debug(message, *args):
- pass
-
-
-class NamespaceManager(object):
- """Handles dictionary operations and locking for a namespace of
- values.
-
- The implementation for setting and retrieving the namespace data is
- handled by subclasses.
-
- NamespaceManager may be used alone, or may be privately accessed by
- one or more Container objects. Container objects provide per-key
- services like expiration times and automatic recreation of values.
-
- Multiple NamespaceManagers created with a particular name will all
- share access to the same underlying datasource and will attempt to
- synchronize against a common mutex object. The scope of this
- sharing may be within a single process or across multiple
- processes, depending on the type of NamespaceManager used.
-
- The NamespaceManager itself is generally threadsafe, except in the
- case of the DBMNamespaceManager in conjunction with the gdbm dbm
- implementation.
-
- """
-
- @classmethod
- def _init_dependencies(cls):
- pass
-
- def __init__(self, namespace):
- self._init_dependencies()
- self.namespace = namespace
-
- def get_creation_lock(self, key):
- raise NotImplementedError()
-
- def do_remove(self):
- raise NotImplementedError()
-
- def acquire_read_lock(self):
- pass
-
- def release_read_lock(self):
- pass
-
- def acquire_write_lock(self, wait=True):
- return True
-
- def release_write_lock(self):
- pass
-
- def has_key(self, key):
- return self.__contains__(key)
-
- def __getitem__(self, key):
- raise NotImplementedError()
-
- def __setitem__(self, key, value):
- raise NotImplementedError()
-
- def set_value(self, key, value, expiretime=None):
- """Optional set_value() method called by Value.
-
- Allows an expiretime to be passed, for namespace
- implementations which can prune their collections
- using expiretime.
-
- """
- self[key] = value
-
- def __contains__(self, key):
- raise NotImplementedError()
-
- def __delitem__(self, key):
- raise NotImplementedError()
-
- def keys(self):
- raise NotImplementedError()
-
- def remove(self):
- self.do_remove()
-
-
-class OpenResourceNamespaceManager(NamespaceManager):
- """A NamespaceManager where read/write operations require opening/
- closing of a resource which is possibly mutexed.
-
- """
- def __init__(self, namespace):
- NamespaceManager.__init__(self, namespace)
- self.access_lock = self.get_access_lock()
- self.openers = 0
- self.mutex = _threading.Lock()
-
- def get_access_lock(self):
- raise NotImplementedError()
-
- def do_open(self, flags):
- raise NotImplementedError()
-
- def do_close(self):
- raise NotImplementedError()
-
- def acquire_read_lock(self):
- self.access_lock.acquire_read_lock()
- try:
- self.open('r', checkcount = True)
- except:
- self.access_lock.release_read_lock()
- raise
-
- def release_read_lock(self):
- try:
- self.close(checkcount = True)
- finally:
- self.access_lock.release_read_lock()
-
- def acquire_write_lock(self, wait=True):
- r = self.access_lock.acquire_write_lock(wait)
- try:
- if (wait or r):
- self.open('c', checkcount = True)
- return r
- except:
- self.access_lock.release_write_lock()
- raise
-
- def release_write_lock(self):
- try:
- self.close(checkcount=True)
- finally:
- self.access_lock.release_write_lock()
-
- def open(self, flags, checkcount=False):
- self.mutex.acquire()
- try:
- if checkcount:
- if self.openers == 0:
- self.do_open(flags)
- self.openers += 1
- else:
- self.do_open(flags)
- self.openers = 1
- finally:
- self.mutex.release()
-
- def close(self, checkcount=False):
- self.mutex.acquire()
- try:
- if checkcount:
- self.openers -= 1
- if self.openers == 0:
- self.do_close()
- else:
- if self.openers > 0:
- self.do_close()
- self.openers = 0
- finally:
- self.mutex.release()
-
- def remove(self):
- self.access_lock.acquire_write_lock()
- try:
- self.close(checkcount=False)
- self.do_remove()
- finally:
- self.access_lock.release_write_lock()
-
-class Value(object):
- __slots__ = 'key', 'createfunc', 'expiretime', 'expire_argument', 'starttime', 'storedtime',\
- 'namespace'
-
- def __init__(self, key, namespace, createfunc=None, expiretime=None, starttime=None):
- self.key = key
- self.createfunc = createfunc
- self.expire_argument = expiretime
- self.starttime = starttime
- self.storedtime = -1
- self.namespace = namespace
-
- def has_value(self):
- """return true if the container has a value stored.
-
- This is regardless of it being expired or not.
-
- """
- self.namespace.acquire_read_lock()
- try:
- return self.namespace.has_key(self.key)
- finally:
- self.namespace.release_read_lock()
-
- def can_have_value(self):
- return self.has_current_value() or self.createfunc is not None
-
- def has_current_value(self):
- self.namespace.acquire_read_lock()
- try:
- has_value = self.namespace.has_key(self.key)
- if has_value:
- try:
- stored, expired, value = self._get_value()
- return not self._is_expired(stored, expired)
- except KeyError:
- pass
- return False
- finally:
- self.namespace.release_read_lock()
-
- def _is_expired(self, storedtime, expiretime):
- """Return true if this container's value is expired."""
- return (
- (
- self.starttime is not None and
- storedtime < self.starttime
- )
- or
- (
- expiretime is not None and
- time.time() >= expiretime + storedtime
- )
- )
-
- def get_value(self):
- self.namespace.acquire_read_lock()
- try:
- has_value = self.has_value()
- if has_value:
- try:
- stored, expired, value = self._get_value()
- if not self._is_expired(stored, expired):
- return value
- except KeyError:
- # guard against un-mutexed backends raising KeyError
- has_value = False
-
- if not self.createfunc:
- raise KeyError(self.key)
- finally:
- self.namespace.release_read_lock()
-
- has_createlock = False
- creation_lock = self.namespace.get_creation_lock(self.key)
- if has_value:
- if not creation_lock.acquire(wait=False):
- debug("get_value returning old value while new one is created")
- return value
- else:
- debug("lock_creatfunc (didnt wait)")
- has_createlock = True
-
- if not has_createlock:
- debug("lock_createfunc (waiting)")
- creation_lock.acquire()
- debug("lock_createfunc (waited)")
-
- try:
- # see if someone created the value already
- self.namespace.acquire_read_lock()
- try:
- if self.has_value():
- try:
- stored, expired, value = self._get_value()
- if not self._is_expired(stored, expired):
- return value
- except KeyError:
- # guard against un-mutexed backends raising KeyError
- pass
- finally:
- self.namespace.release_read_lock()
-
- debug("get_value creating new value")
- v = self.createfunc()
- self.set_value(v)
- return v
- finally:
- creation_lock.release()
- debug("released create lock")
-
- def _get_value(self):
- value = self.namespace[self.key]
- try:
- stored, expired, value = value
- except ValueError:
- if not len(value) == 2:
- raise
- # Old format: upgrade
- stored, value = value
- expired = self.expire_argument
- debug("get_value upgrading time %r expire time %r", stored, self.expire_argument)
- self.namespace.release_read_lock()
- self.set_value(value, stored)
- self.namespace.acquire_read_lock()
- except TypeError:
- # occurs when the value is None. memcached
- # may yank the rug from under us in which case
- # that's the result
- raise KeyError(self.key)
- return stored, expired, value
-
- def set_value(self, value, storedtime=None):
- self.namespace.acquire_write_lock()
- try:
- if storedtime is None:
- storedtime = time.time()
- debug("set_value stored time %r expire time %r", storedtime, self.expire_argument)
- self.namespace.set_value(self.key, (storedtime, self.expire_argument, value))
- finally:
- self.namespace.release_write_lock()
-
- def clear_value(self):
- self.namespace.acquire_write_lock()
- try:
- debug("clear_value")
- if self.namespace.has_key(self.key):
- try:
- del self.namespace[self.key]
- except KeyError:
- # guard against un-mutexed backends raising KeyError
- pass
- self.storedtime = -1
- finally:
- self.namespace.release_write_lock()
-
-class AbstractDictionaryNSManager(NamespaceManager):
- """A subclassable NamespaceManager that places data in a dictionary.
-
- Subclasses should provide a "dictionary" attribute or descriptor
- which returns a dict-like object. The dictionary will store keys
- that are local to the "namespace" attribute of this manager, so
- ensure that the dictionary will not be used by any other namespace.
-
- e.g.::
-
- import collections
- cached_data = collections.defaultdict(dict)
-
- class MyDictionaryManager(AbstractDictionaryNSManager):
- def __init__(self, namespace):
- AbstractDictionaryNSManager.__init__(self, namespace)
- self.dictionary = cached_data[self.namespace]
-
- The above stores data in a global dictionary called "cached_data",
- which is structured as a dictionary of dictionaries, keyed
- first on namespace name to a sub-dictionary, then on actual
- cache key to value.
-
- """
-
- def get_creation_lock(self, key):
- return NameLock(
- identifier="memorynamespace/funclock/%s/%s" % (self.namespace, key),
- reentrant=True
- )
-
- def __getitem__(self, key):
- return self.dictionary[key]
-
- def __contains__(self, key):
- return self.dictionary.__contains__(key)
-
- def has_key(self, key):
- return self.dictionary.__contains__(key)
-
- def __setitem__(self, key, value):
- self.dictionary[key] = value
-
- def __delitem__(self, key):
- del self.dictionary[key]
-
- def do_remove(self):
- self.dictionary.clear()
-
- def keys(self):
- return self.dictionary.keys()
-
-class MemoryNamespaceManager(AbstractDictionaryNSManager):
- namespaces = util.SyncDict()
-
- def __init__(self, namespace, **kwargs):
- AbstractDictionaryNSManager.__init__(self, namespace)
- self.dictionary = MemoryNamespaceManager.namespaces.get(self.namespace,
- dict)
-
-class DBMNamespaceManager(OpenResourceNamespaceManager):
- def __init__(self, namespace, dbmmodule=None, data_dir=None,
- dbm_dir=None, lock_dir=None, digest_filenames=True, **kwargs):
- self.digest_filenames = digest_filenames
-
- if not dbm_dir and not data_dir:
- raise MissingCacheParameter("data_dir or dbm_dir is required")
- elif dbm_dir:
- self.dbm_dir = dbm_dir
- else:
- self.dbm_dir = data_dir + "/container_dbm"
- util.verify_directory(self.dbm_dir)
-
- if not lock_dir and not data_dir:
- raise MissingCacheParameter("data_dir or lock_dir is required")
- elif lock_dir:
- self.lock_dir = lock_dir
- else:
- self.lock_dir = data_dir + "/container_dbm_lock"
- util.verify_directory(self.lock_dir)
-
- self.dbmmodule = dbmmodule or anydbm
-
- self.dbm = None
- OpenResourceNamespaceManager.__init__(self, namespace)
-
- self.file = util.encoded_path(root= self.dbm_dir,
- identifiers=[self.namespace],
- extension='.dbm',
- digest_filenames=self.digest_filenames)
-
- debug("data file %s", self.file)
- self._checkfile()
-
- def get_access_lock(self):
- return file_synchronizer(identifier=self.namespace,
- lock_dir=self.lock_dir)
-
- def get_creation_lock(self, key):
- return file_synchronizer(
- identifier = "dbmcontainer/funclock/%s" % self.namespace,
- lock_dir=self.lock_dir
- )
-
- def file_exists(self, file):
- if os.access(file, os.F_OK):
- return True
- else:
- for ext in ('db', 'dat', 'pag', 'dir'):
- if os.access(file + os.extsep + ext, os.F_OK):
- return True
-
- return False
-
- def _checkfile(self):
- if not self.file_exists(self.file):
- g = self.dbmmodule.open(self.file, 'c')
- g.close()
-
- def get_filenames(self):
- list = []
- if os.access(self.file, os.F_OK):
- list.append(self.file)
-
- for ext in ('pag', 'dir', 'db', 'dat'):
- if os.access(self.file + os.extsep + ext, os.F_OK):
- list.append(self.file + os.extsep + ext)
- return list
-
- def do_open(self, flags):
- debug("opening dbm file %s", self.file)
- try:
- self.dbm = self.dbmmodule.open(self.file, flags)
- except:
- self._checkfile()
- self.dbm = self.dbmmodule.open(self.file, flags)
-
- def do_close(self):
- if self.dbm is not None:
- debug("closing dbm file %s", self.file)
- self.dbm.close()
-
- def do_remove(self):
- for f in self.get_filenames():
- os.remove(f)
-
- def __getitem__(self, key):
- return cPickle.loads(self.dbm[key])
-
- def __contains__(self, key):
- return self.dbm.has_key(key)
-
- def __setitem__(self, key, value):
- self.dbm[key] = cPickle.dumps(value)
-
- def __delitem__(self, key):
- del self.dbm[key]
-
- def keys(self):
- return self.dbm.keys()
-
-
-class FileNamespaceManager(OpenResourceNamespaceManager):
- def __init__(self, namespace, data_dir=None, file_dir=None, lock_dir=None,
- digest_filenames=True, **kwargs):
- self.digest_filenames = digest_filenames
-
- if not file_dir and not data_dir:
- raise MissingCacheParameter("data_dir or file_dir is required")
- elif file_dir:
- self.file_dir = file_dir
- else:
- self.file_dir = data_dir + "/container_file"
- util.verify_directory(self.file_dir)
-
- if not lock_dir and not data_dir:
- raise MissingCacheParameter("data_dir or lock_dir is required")
- elif lock_dir:
- self.lock_dir = lock_dir
- else:
- self.lock_dir = data_dir + "/container_file_lock"
- util.verify_directory(self.lock_dir)
- OpenResourceNamespaceManager.__init__(self, namespace)
-
- self.file = util.encoded_path(root=self.file_dir,
- identifiers=[self.namespace],
- extension='.cache',
- digest_filenames=self.digest_filenames)
- self.hash = {}
-
- debug("data file %s", self.file)
-
- def get_access_lock(self):
- return file_synchronizer(identifier=self.namespace,
- lock_dir=self.lock_dir)
-
- def get_creation_lock(self, key):
- return file_synchronizer(
- identifier = "filecontainer/funclock/%s" % self.namespace,
- lock_dir = self.lock_dir
- )
-
- def file_exists(self, file):
- return os.access(file, os.F_OK)
-
- def do_open(self, flags):
- if self.file_exists(self.file):
- fh = open(self.file, 'rb')
- try:
- self.hash = cPickle.load(fh)
- except (IOError, OSError, EOFError, cPickle.PickleError, ValueError):
- pass
- fh.close()
-
- self.flags = flags
-
- def do_close(self):
- if self.flags == 'c' or self.flags == 'w':
- fh = open(self.file, 'wb')
- cPickle.dump(self.hash, fh)
- fh.close()
-
- self.hash = {}
- self.flags = None
-
- def do_remove(self):
- try:
- os.remove(self.file)
- except OSError, err:
- # for instance, because we haven't yet used this cache,
- # but client code has asked for a clear() operation...
- pass
- self.hash = {}
-
- def __getitem__(self, key):
- return self.hash[key]
-
- def __contains__(self, key):
- return self.hash.has_key(key)
-
- def __setitem__(self, key, value):
- self.hash[key] = value
-
- def __delitem__(self, key):
- del self.hash[key]
-
- def keys(self):
- return self.hash.keys()
-
-
-#### legacy stuff to support the old "Container" class interface
-
-namespace_classes = {}
-
-ContainerContext = dict
-
-class ContainerMeta(type):
- def __init__(cls, classname, bases, dict_):
- namespace_classes[cls] = cls.namespace_class
- return type.__init__(cls, classname, bases, dict_)
- def __call__(self, key, context, namespace, createfunc=None,
- expiretime=None, starttime=None, **kwargs):
- if namespace in context:
- ns = context[namespace]
- else:
- nscls = namespace_classes[self]
- context[namespace] = ns = nscls(namespace, **kwargs)
- return Value(key, ns, createfunc=createfunc,
- expiretime=expiretime, starttime=starttime)
-
-class Container(object):
- __metaclass__ = ContainerMeta
- namespace_class = NamespaceManager
-
-class FileContainer(Container):
- namespace_class = FileNamespaceManager
-
-class MemoryContainer(Container):
- namespace_class = MemoryNamespaceManager
-
-class DBMContainer(Container):
- namespace_class = DBMNamespaceManager
-
-DbmContainer = DBMContainer
diff --git a/module/lib/beaker/converters.py b/module/lib/beaker/converters.py
deleted file mode 100644
index f0ad34963..000000000
--- a/module/lib/beaker/converters.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
-# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
-def asbool(obj):
- if isinstance(obj, (str, unicode)):
- obj = obj.strip().lower()
- if obj in ['true', 'yes', 'on', 'y', 't', '1']:
- return True
- elif obj in ['false', 'no', 'off', 'n', 'f', '0']:
- return False
- else:
- raise ValueError(
- "String is not true/false: %r" % obj)
- return bool(obj)
-
-def aslist(obj, sep=None, strip=True):
- if isinstance(obj, (str, unicode)):
- lst = obj.split(sep)
- if strip:
- lst = [v.strip() for v in lst]
- return lst
- elif isinstance(obj, (list, tuple)):
- return obj
- elif obj is None:
- return []
- else:
- return [obj]
diff --git a/module/lib/beaker/crypto/__init__.py b/module/lib/beaker/crypto/__init__.py
deleted file mode 100644
index 3e26b0c13..000000000
--- a/module/lib/beaker/crypto/__init__.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from warnings import warn
-
-from beaker.crypto.pbkdf2 import PBKDF2, strxor
-from beaker.crypto.util import hmac, sha1, hmac_sha1, md5
-from beaker import util
-
-keyLength = None
-
-if util.jython:
- try:
- from beaker.crypto.jcecrypto import getKeyLength, aesEncrypt
- keyLength = getKeyLength()
- except ImportError:
- pass
-else:
- try:
- from beaker.crypto.pycrypto import getKeyLength, aesEncrypt, aesDecrypt
- keyLength = getKeyLength()
- except ImportError:
- pass
-
-if not keyLength:
- has_aes = False
-else:
- has_aes = True
-
-if has_aes and keyLength < 32:
- warn('Crypto implementation only supports key lengths up to %d bits. '
- 'Generated session cookies may be incompatible with other '
- 'environments' % (keyLength * 8))
-
-
-def generateCryptoKeys(master_key, salt, iterations):
- # NB: We XOR parts of the keystream into the randomly-generated parts, just
- # in case os.urandom() isn't as random as it should be. Note that if
- # os.urandom() returns truly random data, this will have no effect on the
- # overall security.
- keystream = PBKDF2(master_key, salt, iterations=iterations)
- cipher_key = keystream.read(keyLength)
- return cipher_key
diff --git a/module/lib/beaker/crypto/jcecrypto.py b/module/lib/beaker/crypto/jcecrypto.py
deleted file mode 100644
index 4062d513e..000000000
--- a/module/lib/beaker/crypto/jcecrypto.py
+++ /dev/null
@@ -1,30 +0,0 @@
-"""
-Encryption module that uses the Java Cryptography Extensions (JCE).
-
-Note that in default installations of the Java Runtime Environment, the
-maximum key length is limited to 128 bits due to US export
-restrictions. This makes the generated keys incompatible with the ones
-generated by pycryptopp, which has no such restrictions. To fix this,
-download the "Unlimited Strength Jurisdiction Policy Files" from Sun,
-which will allow encryption using 256 bit AES keys.
-"""
-from javax.crypto import Cipher
-from javax.crypto.spec import SecretKeySpec, IvParameterSpec
-
-import jarray
-
-# Initialization vector filled with zeros
-_iv = IvParameterSpec(jarray.zeros(16, 'b'))
-
-def aesEncrypt(data, key):
- cipher = Cipher.getInstance('AES/CTR/NoPadding')
- skeySpec = SecretKeySpec(key, 'AES')
- cipher.init(Cipher.ENCRYPT_MODE, skeySpec, _iv)
- return cipher.doFinal(data).tostring()
-
-# magic.
-aesDecrypt = aesEncrypt
-
-def getKeyLength():
- maxlen = Cipher.getMaxAllowedKeyLength('AES/CTR/NoPadding')
- return min(maxlen, 256) / 8
diff --git a/module/lib/beaker/crypto/pbkdf2.py b/module/lib/beaker/crypto/pbkdf2.py
deleted file mode 100644
index 96dc5fbb2..000000000
--- a/module/lib/beaker/crypto/pbkdf2.py
+++ /dev/null
@@ -1,342 +0,0 @@
-#!/usr/bin/python
-# -*- coding: ascii -*-
-###########################################################################
-# PBKDF2.py - PKCS#5 v2.0 Password-Based Key Derivation
-#
-# Copyright (C) 2007 Dwayne C. Litzenberger <dlitz@dlitz.net>
-# All rights reserved.
-#
-# Permission to use, copy, modify, and distribute this software and its
-# documentation for any purpose and without fee is hereby granted,
-# provided that the above copyright notice appear in all copies and that
-# both that copyright notice and this permission notice appear in
-# supporting documentation.
-#
-# THE AUTHOR PROVIDES THIS SOFTWARE ``AS IS'' AND ANY EXPRESSED OR
-# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-#
-# Country of origin: Canada
-#
-###########################################################################
-# Sample PBKDF2 usage:
-# from Crypto.Cipher import AES
-# from PBKDF2 import PBKDF2
-# import os
-#
-# salt = os.urandom(8) # 64-bit salt
-# key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key
-# iv = os.urandom(16) # 128-bit IV
-# cipher = AES.new(key, AES.MODE_CBC, iv)
-# ...
-#
-# Sample crypt() usage:
-# from PBKDF2 import crypt
-# pwhash = crypt("secret")
-# alleged_pw = raw_input("Enter password: ")
-# if pwhash == crypt(alleged_pw, pwhash):
-# print "Password good"
-# else:
-# print "Invalid password"
-#
-###########################################################################
-# History:
-#
-# 2007-07-27 Dwayne C. Litzenberger <dlitz@dlitz.net>
-# - Initial Release (v1.0)
-#
-# 2007-07-31 Dwayne C. Litzenberger <dlitz@dlitz.net>
-# - Bugfix release (v1.1)
-# - SECURITY: The PyCrypto XOR cipher (used, if available, in the _strxor
-# function in the previous release) silently truncates all keys to 64
-# bytes. The way it was used in the previous release, this would only be
-# problem if the pseudorandom function that returned values larger than
-# 64 bytes (so SHA1, SHA256 and SHA512 are fine), but I don't like
-# anything that silently reduces the security margin from what is
-# expected.
-#
-###########################################################################
-
-__version__ = "1.1"
-
-from struct import pack
-from binascii import b2a_hex
-from random import randint
-
-from base64 import b64encode
-
-from beaker.crypto.util import hmac as HMAC, hmac_sha1 as SHA1
-
-def strxor(a, b):
- return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])
-
-class PBKDF2(object):
- """PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation
-
- This implementation takes a passphrase and a salt (and optionally an
- iteration count, a digest module, and a MAC module) and provides a
- file-like object from which an arbitrarily-sized key can be read.
-
- If the passphrase and/or salt are unicode objects, they are encoded as
- UTF-8 before they are processed.
-
- The idea behind PBKDF2 is to derive a cryptographic key from a
- passphrase and a salt.
-
- PBKDF2 may also be used as a strong salted password hash. The
- 'crypt' function is provided for that purpose.
-
- Remember: Keys generated using PBKDF2 are only as strong as the
- passphrases they are derived from.
- """
-
- def __init__(self, passphrase, salt, iterations=1000,
- digestmodule=SHA1, macmodule=HMAC):
- if not callable(macmodule):
- macmodule = macmodule.new
- self.__macmodule = macmodule
- self.__digestmodule = digestmodule
- self._setup(passphrase, salt, iterations, self._pseudorandom)
-
- def _pseudorandom(self, key, msg):
- """Pseudorandom function. e.g. HMAC-SHA1"""
- return self.__macmodule(key=key, msg=msg,
- digestmod=self.__digestmodule).digest()
-
- def read(self, bytes):
- """Read the specified number of key bytes."""
- if self.closed:
- raise ValueError("file-like object is closed")
-
- size = len(self.__buf)
- blocks = [self.__buf]
- i = self.__blockNum
- while size < bytes:
- i += 1
- if i > 0xffffffff:
- # We could return "" here, but
- raise OverflowError("derived key too long")
- block = self.__f(i)
- blocks.append(block)
- size += len(block)
- buf = "".join(blocks)
- retval = buf[:bytes]
- self.__buf = buf[bytes:]
- self.__blockNum = i
- return retval
-
- def __f(self, i):
- # i must fit within 32 bits
- assert (1 <= i <= 0xffffffff)
- U = self.__prf(self.__passphrase, self.__salt + pack("!L", i))
- result = U
- for j in xrange(2, 1+self.__iterations):
- U = self.__prf(self.__passphrase, U)
- result = strxor(result, U)
- return result
-
- def hexread(self, octets):
- """Read the specified number of octets. Return them as hexadecimal.
-
- Note that len(obj.hexread(n)) == 2*n.
- """
- return b2a_hex(self.read(octets))
-
- def _setup(self, passphrase, salt, iterations, prf):
- # Sanity checks:
-
- # passphrase and salt must be str or unicode (in the latter
- # case, we convert to UTF-8)
- if isinstance(passphrase, unicode):
- passphrase = passphrase.encode("UTF-8")
- if not isinstance(passphrase, str):
- raise TypeError("passphrase must be str or unicode")
- if isinstance(salt, unicode):
- salt = salt.encode("UTF-8")
- if not isinstance(salt, str):
- raise TypeError("salt must be str or unicode")
-
- # iterations must be an integer >= 1
- if not isinstance(iterations, (int, long)):
- raise TypeError("iterations must be an integer")
- if iterations < 1:
- raise ValueError("iterations must be at least 1")
-
- # prf must be callable
- if not callable(prf):
- raise TypeError("prf must be callable")
-
- self.__passphrase = passphrase
- self.__salt = salt
- self.__iterations = iterations
- self.__prf = prf
- self.__blockNum = 0
- self.__buf = ""
- self.closed = False
-
- def close(self):
- """Close the stream."""
- if not self.closed:
- del self.__passphrase
- del self.__salt
- del self.__iterations
- del self.__prf
- del self.__blockNum
- del self.__buf
- self.closed = True
-
-def crypt(word, salt=None, iterations=None):
- """PBKDF2-based unix crypt(3) replacement.
-
- The number of iterations specified in the salt overrides the 'iterations'
- parameter.
-
- The effective hash length is 192 bits.
- """
-
- # Generate a (pseudo-)random salt if the user hasn't provided one.
- if salt is None:
- salt = _makesalt()
-
- # salt must be a string or the us-ascii subset of unicode
- if isinstance(salt, unicode):
- salt = salt.encode("us-ascii")
- if not isinstance(salt, str):
- raise TypeError("salt must be a string")
-
- # word must be a string or unicode (in the latter case, we convert to UTF-8)
- if isinstance(word, unicode):
- word = word.encode("UTF-8")
- if not isinstance(word, str):
- raise TypeError("word must be a string or unicode")
-
- # Try to extract the real salt and iteration count from the salt
- if salt.startswith("$p5k2$"):
- (iterations, salt, dummy) = salt.split("$")[2:5]
- if iterations == "":
- iterations = 400
- else:
- converted = int(iterations, 16)
- if iterations != "%x" % converted: # lowercase hex, minimum digits
- raise ValueError("Invalid salt")
- iterations = converted
- if not (iterations >= 1):
- raise ValueError("Invalid salt")
-
- # Make sure the salt matches the allowed character set
- allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
- for ch in salt:
- if ch not in allowed:
- raise ValueError("Illegal character %r in salt" % (ch,))
-
- if iterations is None or iterations == 400:
- iterations = 400
- salt = "$p5k2$$" + salt
- else:
- salt = "$p5k2$%x$%s" % (iterations, salt)
- rawhash = PBKDF2(word, salt, iterations).read(24)
- return salt + "$" + b64encode(rawhash, "./")
-
-# Add crypt as a static method of the PBKDF2 class
-# This makes it easier to do "from PBKDF2 import PBKDF2" and still use
-# crypt.
-PBKDF2.crypt = staticmethod(crypt)
-
-def _makesalt():
- """Return a 48-bit pseudorandom salt for crypt().
-
- This function is not suitable for generating cryptographic secrets.
- """
- binarysalt = "".join([pack("@H", randint(0, 0xffff)) for i in range(3)])
- return b64encode(binarysalt, "./")
-
-def test_pbkdf2():
- """Module self-test"""
- from binascii import a2b_hex
-
- #
- # Test vectors from RFC 3962
- #
-
- # Test 1
- result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1).read(16)
- expected = a2b_hex("cdedb5281bb2f801565a1122b2563515")
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # Test 2
- result = PBKDF2("password", "ATHENA.MIT.EDUraeburn", 1200).hexread(32)
- expected = ("5c08eb61fdf71e4e4ec3cf6ba1f5512b"
- "a7e52ddbc5e5142f708a31e2e62b1e13")
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # Test 3
- result = PBKDF2("X"*64, "pass phrase equals block size", 1200).hexread(32)
- expected = ("139c30c0966bc32ba55fdbf212530ac9"
- "c5ec59f1a452f5cc9ad940fea0598ed1")
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # Test 4
- result = PBKDF2("X"*65, "pass phrase exceeds block size", 1200).hexread(32)
- expected = ("9ccad6d468770cd51b10e6a68721be61"
- "1a8b4d282601db3b36be9246915ec82a")
- if result != expected:
- raise RuntimeError("self-test failed")
-
- #
- # Other test vectors
- #
-
- # Chunked read
- f = PBKDF2("kickstart", "workbench", 256)
- result = f.read(17)
- result += f.read(17)
- result += f.read(1)
- result += f.read(2)
- result += f.read(3)
- expected = PBKDF2("kickstart", "workbench", 256).read(40)
- if result != expected:
- raise RuntimeError("self-test failed")
-
- #
- # crypt() test vectors
- #
-
- # crypt 1
- result = crypt("cloadm", "exec")
- expected = '$p5k2$$exec$r1EWMCMk7Rlv3L/RNcFXviDefYa0hlql'
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # crypt 2
- result = crypt("gnu", '$p5k2$c$u9HvcT4d$.....')
- expected = '$p5k2$c$u9HvcT4d$Sd1gwSVCLZYAuqZ25piRnbBEoAesaa/g'
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # crypt 3
- result = crypt("dcl", "tUsch7fU", iterations=13)
- expected = "$p5k2$d$tUsch7fU$nqDkaxMDOFBeJsTSfABsyn.PYUXilHwL"
- if result != expected:
- raise RuntimeError("self-test failed")
-
- # crypt 4 (unicode)
- result = crypt(u'\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2',
- '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ')
- expected = '$p5k2$$KosHgqNo$9mjN8gqjt02hDoP0c2J0ABtLIwtot8cQ'
- if result != expected:
- raise RuntimeError("self-test failed")
-
-if __name__ == '__main__':
- test_pbkdf2()
-
-# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/module/lib/beaker/crypto/pycrypto.py b/module/lib/beaker/crypto/pycrypto.py
deleted file mode 100644
index a3eb4d9db..000000000
--- a/module/lib/beaker/crypto/pycrypto.py
+++ /dev/null
@@ -1,31 +0,0 @@
-"""Encryption module that uses pycryptopp or pycrypto"""
-try:
- # Pycryptopp is preferred over Crypto because Crypto has had
- # various periods of not being maintained, and pycryptopp uses
- # the Crypto++ library which is generally considered the 'gold standard'
- # of crypto implementations
- from pycryptopp.cipher import aes
-
- def aesEncrypt(data, key):
- cipher = aes.AES(key)
- return cipher.process(data)
-
- # magic.
- aesDecrypt = aesEncrypt
-
-except ImportError:
- from Crypto.Cipher import AES
-
- def aesEncrypt(data, key):
- cipher = AES.new(key)
-
- data = data + (" " * (16 - (len(data) % 16)))
- return cipher.encrypt(data)
-
- def aesDecrypt(data, key):
- cipher = AES.new(key)
-
- return cipher.decrypt(data).rstrip()
-
-def getKeyLength():
- return 32
diff --git a/module/lib/beaker/crypto/util.py b/module/lib/beaker/crypto/util.py
deleted file mode 100644
index d97e8ce6f..000000000
--- a/module/lib/beaker/crypto/util.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from warnings import warn
-from beaker import util
-
-
-try:
- # Use PyCrypto (if available)
- from Crypto.Hash import HMAC as hmac, SHA as hmac_sha1
- sha1 = hmac_sha1.new
-
-except ImportError:
-
- # PyCrypto not available. Use the Python standard library.
- import hmac
-
- # When using the stdlib, we have to make sure the hmac version and sha
- # version are compatible
- if util.py24:
- from sha import sha as sha1
- import sha as hmac_sha1
- else:
- # NOTE: We have to use the callable with hashlib (hashlib.sha1),
- # otherwise hmac only accepts the sha module object itself
- from hashlib import sha1
- hmac_sha1 = sha1
-
-
-if util.py24:
- from md5 import md5
-else:
- from hashlib import md5
diff --git a/module/lib/beaker/exceptions.py b/module/lib/beaker/exceptions.py
deleted file mode 100644
index cc0eed286..000000000
--- a/module/lib/beaker/exceptions.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""Beaker exception classes"""
-
-class BeakerException(Exception):
- pass
-
-
-class CreationAbortedError(Exception):
- """Deprecated."""
-
-
-class InvalidCacheBackendError(BeakerException, ImportError):
- pass
-
-
-class MissingCacheParameter(BeakerException):
- pass
-
-
-class LockError(BeakerException):
- pass
-
-
-class InvalidCryptoBackendError(BeakerException):
- pass
diff --git a/module/lib/beaker/ext/__init__.py b/module/lib/beaker/ext/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/lib/beaker/ext/__init__.py
+++ /dev/null
diff --git a/module/lib/beaker/ext/database.py b/module/lib/beaker/ext/database.py
deleted file mode 100644
index 701e6f7d2..000000000
--- a/module/lib/beaker/ext/database.py
+++ /dev/null
@@ -1,165 +0,0 @@
-import cPickle
-import logging
-import pickle
-from datetime import datetime
-
-from beaker.container import OpenResourceNamespaceManager, Container
-from beaker.exceptions import InvalidCacheBackendError, MissingCacheParameter
-from beaker.synchronization import file_synchronizer, null_synchronizer
-from beaker.util import verify_directory, SyncDict
-
-log = logging.getLogger(__name__)
-
-sa = None
-pool = None
-types = None
-
-class DatabaseNamespaceManager(OpenResourceNamespaceManager):
- metadatas = SyncDict()
- tables = SyncDict()
-
- @classmethod
- def _init_dependencies(cls):
- global sa, pool, types
- if sa is not None:
- return
- try:
- import sqlalchemy as sa
- import sqlalchemy.pool as pool
- from sqlalchemy import types
- except ImportError:
- raise InvalidCacheBackendError("Database cache backend requires "
- "the 'sqlalchemy' library")
-
- def __init__(self, namespace, url=None, sa_opts=None, optimistic=False,
- table_name='beaker_cache', data_dir=None, lock_dir=None,
- **params):
- """Creates a database namespace manager
-
- ``url``
- SQLAlchemy compliant db url
- ``sa_opts``
- A dictionary of SQLAlchemy keyword options to initialize the engine
- with.
- ``optimistic``
- Use optimistic session locking, note that this will result in an
- additional select when updating a cache value to compare version
- numbers.
- ``table_name``
- The table name to use in the database for the cache.
- """
- OpenResourceNamespaceManager.__init__(self, namespace)
-
- if sa_opts is None:
- sa_opts = params
-
- if lock_dir:
- self.lock_dir = lock_dir
- elif data_dir:
- self.lock_dir = data_dir + "/container_db_lock"
- if self.lock_dir:
- verify_directory(self.lock_dir)
-
- # Check to see if the table's been created before
- url = url or sa_opts['sa.url']
- table_key = url + table_name
- def make_cache():
- # Check to see if we have a connection pool open already
- meta_key = url + table_name
- def make_meta():
- # SQLAlchemy pops the url, this ensures it sticks around
- # later
- sa_opts['sa.url'] = url
- engine = sa.engine_from_config(sa_opts, 'sa.')
- meta = sa.MetaData()
- meta.bind = engine
- return meta
- meta = DatabaseNamespaceManager.metadatas.get(meta_key, make_meta)
- # Create the table object and cache it now
- cache = sa.Table(table_name, meta,
- sa.Column('id', types.Integer, primary_key=True),
- sa.Column('namespace', types.String(255), nullable=False),
- sa.Column('accessed', types.DateTime, nullable=False),
- sa.Column('created', types.DateTime, nullable=False),
- sa.Column('data', types.PickleType, nullable=False),
- sa.UniqueConstraint('namespace')
- )
- cache.create(checkfirst=True)
- return cache
- self.hash = {}
- self._is_new = False
- self.loaded = False
- self.cache = DatabaseNamespaceManager.tables.get(table_key, make_cache)
-
- def get_access_lock(self):
- return null_synchronizer()
-
- def get_creation_lock(self, key):
- return file_synchronizer(
- identifier ="databasecontainer/funclock/%s" % self.namespace,
- lock_dir = self.lock_dir)
-
- def do_open(self, flags):
- # If we already loaded the data, don't bother loading it again
- if self.loaded:
- self.flags = flags
- return
-
- cache = self.cache
- result = sa.select([cache.c.data],
- cache.c.namespace==self.namespace
- ).execute().fetchone()
- if not result:
- self._is_new = True
- self.hash = {}
- else:
- self._is_new = False
- try:
- self.hash = result['data']
- except (IOError, OSError, EOFError, cPickle.PickleError,
- pickle.PickleError):
- log.debug("Couln't load pickle data, creating new storage")
- self.hash = {}
- self._is_new = True
- self.flags = flags
- self.loaded = True
-
- def do_close(self):
- if self.flags is not None and (self.flags == 'c' or self.flags == 'w'):
- cache = self.cache
- if self._is_new:
- cache.insert().execute(namespace=self.namespace, data=self.hash,
- accessed=datetime.now(),
- created=datetime.now())
- self._is_new = False
- else:
- cache.update(cache.c.namespace==self.namespace).execute(
- data=self.hash, accessed=datetime.now())
- self.flags = None
-
- def do_remove(self):
- cache = self.cache
- cache.delete(cache.c.namespace==self.namespace).execute()
- self.hash = {}
-
- # We can retain the fact that we did a load attempt, but since the
- # file is gone this will be a new namespace should it be saved.
- self._is_new = True
-
- def __getitem__(self, key):
- return self.hash[key]
-
- def __contains__(self, key):
- return self.hash.has_key(key)
-
- def __setitem__(self, key, value):
- self.hash[key] = value
-
- def __delitem__(self, key):
- del self.hash[key]
-
- def keys(self):
- return self.hash.keys()
-
-class DatabaseContainer(Container):
- namespace_manager = DatabaseNamespaceManager
diff --git a/module/lib/beaker/ext/google.py b/module/lib/beaker/ext/google.py
deleted file mode 100644
index dd8380d7f..000000000
--- a/module/lib/beaker/ext/google.py
+++ /dev/null
@@ -1,120 +0,0 @@
-import cPickle
-import logging
-from datetime import datetime
-
-from beaker.container import OpenResourceNamespaceManager, Container
-from beaker.exceptions import InvalidCacheBackendError
-from beaker.synchronization import null_synchronizer
-
-log = logging.getLogger(__name__)
-
-db = None
-
-class GoogleNamespaceManager(OpenResourceNamespaceManager):
- tables = {}
-
- @classmethod
- def _init_dependencies(cls):
- global db
- if db is not None:
- return
- try:
- db = __import__('google.appengine.ext.db').appengine.ext.db
- except ImportError:
- raise InvalidCacheBackendError("Datastore cache backend requires the "
- "'google.appengine.ext' library")
-
- def __init__(self, namespace, table_name='beaker_cache', **params):
- """Creates a datastore namespace manager"""
- OpenResourceNamespaceManager.__init__(self, namespace)
-
- def make_cache():
- table_dict = dict(created=db.DateTimeProperty(),
- accessed=db.DateTimeProperty(),
- data=db.BlobProperty())
- table = type(table_name, (db.Model,), table_dict)
- return table
- self.table_name = table_name
- self.cache = GoogleNamespaceManager.tables.setdefault(table_name, make_cache())
- self.hash = {}
- self._is_new = False
- self.loaded = False
- self.log_debug = logging.DEBUG >= log.getEffectiveLevel()
-
- # Google wants namespaces to start with letters, change the namespace
- # to start with a letter
- self.namespace = 'p%s' % self.namespace
-
- def get_access_lock(self):
- return null_synchronizer()
-
- def get_creation_lock(self, key):
- # this is weird, should probably be present
- return null_synchronizer()
-
- def do_open(self, flags):
- # If we already loaded the data, don't bother loading it again
- if self.loaded:
- self.flags = flags
- return
-
- item = self.cache.get_by_key_name(self.namespace)
-
- if not item:
- self._is_new = True
- self.hash = {}
- else:
- self._is_new = False
- try:
- self.hash = cPickle.loads(str(item.data))
- except (IOError, OSError, EOFError, cPickle.PickleError):
- if self.log_debug:
- log.debug("Couln't load pickle data, creating new storage")
- self.hash = {}
- self._is_new = True
- self.flags = flags
- self.loaded = True
-
- def do_close(self):
- if self.flags is not None and (self.flags == 'c' or self.flags == 'w'):
- if self._is_new:
- item = self.cache(key_name=self.namespace)
- item.data = cPickle.dumps(self.hash)
- item.created = datetime.now()
- item.accessed = datetime.now()
- item.put()
- self._is_new = False
- else:
- item = self.cache.get_by_key_name(self.namespace)
- item.data = cPickle.dumps(self.hash)
- item.accessed = datetime.now()
- item.put()
- self.flags = None
-
- def do_remove(self):
- item = self.cache.get_by_key_name(self.namespace)
- item.delete()
- self.hash = {}
-
- # We can retain the fact that we did a load attempt, but since the
- # file is gone this will be a new namespace should it be saved.
- self._is_new = True
-
- def __getitem__(self, key):
- return self.hash[key]
-
- def __contains__(self, key):
- return self.hash.has_key(key)
-
- def __setitem__(self, key, value):
- self.hash[key] = value
-
- def __delitem__(self, key):
- del self.hash[key]
-
- def keys(self):
- return self.hash.keys()
-
-
-class GoogleContainer(Container):
- namespace_class = GoogleNamespaceManager
diff --git a/module/lib/beaker/ext/memcached.py b/module/lib/beaker/ext/memcached.py
deleted file mode 100644
index 96516953f..000000000
--- a/module/lib/beaker/ext/memcached.py
+++ /dev/null
@@ -1,82 +0,0 @@
-from beaker.container import NamespaceManager, Container
-from beaker.exceptions import InvalidCacheBackendError, MissingCacheParameter
-from beaker.synchronization import file_synchronizer, null_synchronizer
-from beaker.util import verify_directory, SyncDict
-import warnings
-
-memcache = None
-
-class MemcachedNamespaceManager(NamespaceManager):
- clients = SyncDict()
-
- @classmethod
- def _init_dependencies(cls):
- global memcache
- if memcache is not None:
- return
- try:
- import pylibmc as memcache
- except ImportError:
- try:
- import cmemcache as memcache
- warnings.warn("cmemcache is known to have serious "
- "concurrency issues; consider using 'memcache' or 'pylibmc'")
- except ImportError:
- try:
- import memcache
- except ImportError:
- raise InvalidCacheBackendError("Memcached cache backend requires either "
- "the 'memcache' or 'cmemcache' library")
-
- def __init__(self, namespace, url=None, data_dir=None, lock_dir=None, **params):
- NamespaceManager.__init__(self, namespace)
-
- if not url:
- raise MissingCacheParameter("url is required")
-
- if lock_dir:
- self.lock_dir = lock_dir
- elif data_dir:
- self.lock_dir = data_dir + "/container_mcd_lock"
- if self.lock_dir:
- verify_directory(self.lock_dir)
-
- self.mc = MemcachedNamespaceManager.clients.get(url, memcache.Client, url.split(';'))
-
- def get_creation_lock(self, key):
- return file_synchronizer(
- identifier="memcachedcontainer/funclock/%s" % self.namespace,lock_dir = self.lock_dir)
-
- def _format_key(self, key):
- return self.namespace + '_' + key.replace(' ', '\302\267')
-
- def __getitem__(self, key):
- return self.mc.get(self._format_key(key))
-
- def __contains__(self, key):
- value = self.mc.get(self._format_key(key))
- return value is not None
-
- def has_key(self, key):
- return key in self
-
- def set_value(self, key, value, expiretime=None):
- if expiretime:
- self.mc.set(self._format_key(key), value, time=expiretime)
- else:
- self.mc.set(self._format_key(key), value)
-
- def __setitem__(self, key, value):
- self.set_value(key, value)
-
- def __delitem__(self, key):
- self.mc.delete(self._format_key(key))
-
- def do_remove(self):
- self.mc.flush_all()
-
- def keys(self):
- raise NotImplementedError("Memcache caching does not support iteration of all cache keys")
-
-class MemcachedContainer(Container):
- namespace_class = MemcachedNamespaceManager
diff --git a/module/lib/beaker/ext/sqla.py b/module/lib/beaker/ext/sqla.py
deleted file mode 100644
index 8c79633c1..000000000
--- a/module/lib/beaker/ext/sqla.py
+++ /dev/null
@@ -1,133 +0,0 @@
-import cPickle
-import logging
-import pickle
-from datetime import datetime
-
-from beaker.container import OpenResourceNamespaceManager, Container
-from beaker.exceptions import InvalidCacheBackendError, MissingCacheParameter
-from beaker.synchronization import file_synchronizer, null_synchronizer
-from beaker.util import verify_directory, SyncDict
-
-
-log = logging.getLogger(__name__)
-
-sa = None
-
-class SqlaNamespaceManager(OpenResourceNamespaceManager):
- binds = SyncDict()
- tables = SyncDict()
-
- @classmethod
- def _init_dependencies(cls):
- global sa
- if sa is not None:
- return
- try:
- import sqlalchemy as sa
- except ImportError:
- raise InvalidCacheBackendError("SQLAlchemy, which is required by "
- "this backend, is not installed")
-
- def __init__(self, namespace, bind, table, data_dir=None, lock_dir=None,
- **kwargs):
- """Create a namespace manager for use with a database table via
- SQLAlchemy.
-
- ``bind``
- SQLAlchemy ``Engine`` or ``Connection`` object
-
- ``table``
- SQLAlchemy ``Table`` object in which to store namespace data.
- This should usually be something created by ``make_cache_table``.
- """
- OpenResourceNamespaceManager.__init__(self, namespace)
-
- if lock_dir:
- self.lock_dir = lock_dir
- elif data_dir:
- self.lock_dir = data_dir + "/container_db_lock"
- if self.lock_dir:
- verify_directory(self.lock_dir)
-
- self.bind = self.__class__.binds.get(str(bind.url), lambda: bind)
- self.table = self.__class__.tables.get('%s:%s' % (bind.url, table.name),
- lambda: table)
- self.hash = {}
- self._is_new = False
- self.loaded = False
-
- def get_access_lock(self):
- return null_synchronizer()
-
- def get_creation_lock(self, key):
- return file_synchronizer(
- identifier ="databasecontainer/funclock/%s" % self.namespace,
- lock_dir=self.lock_dir)
-
- def do_open(self, flags):
- if self.loaded:
- self.flags = flags
- return
- select = sa.select([self.table.c.data],
- (self.table.c.namespace == self.namespace))
- result = self.bind.execute(select).fetchone()
- if not result:
- self._is_new = True
- self.hash = {}
- else:
- self._is_new = False
- try:
- self.hash = result['data']
- except (IOError, OSError, EOFError, cPickle.PickleError,
- pickle.PickleError):
- log.debug("Couln't load pickle data, creating new storage")
- self.hash = {}
- self._is_new = True
- self.flags = flags
- self.loaded = True
-
- def do_close(self):
- if self.flags is not None and (self.flags == 'c' or self.flags == 'w'):
- if self._is_new:
- insert = self.table.insert()
- self.bind.execute(insert, namespace=self.namespace, data=self.hash,
- accessed=datetime.now(), created=datetime.now())
- self._is_new = False
- else:
- update = self.table.update(self.table.c.namespace == self.namespace)
- self.bind.execute(update, data=self.hash, accessed=datetime.now())
- self.flags = None
-
- def do_remove(self):
- delete = self.table.delete(self.table.c.namespace == self.namespace)
- self.bind.execute(delete)
- self.hash = {}
- self._is_new = True
-
- def __getitem__(self, key):
- return self.hash[key]
-
- def __contains__(self, key):
- return self.hash.has_key(key)
-
- def __setitem__(self, key, value):
- self.hash[key] = value
-
- def __delitem__(self, key):
- del self.hash[key]
-
- def keys(self):
- return self.hash.keys()
-
-
-class SqlaContainer(Container):
- namespace_manager = SqlaNamespaceManager
-
-def make_cache_table(metadata, table_name='beaker_cache'):
- """Return a ``Table`` object suitable for storing cached values for the
- namespace manager. Do not create the table."""
- return sa.Table(table_name, metadata,
- sa.Column('namespace', sa.String(255), primary_key=True),
- sa.Column('accessed', sa.DateTime, nullable=False),
- sa.Column('created', sa.DateTime, nullable=False),
- sa.Column('data', sa.PickleType, nullable=False))
diff --git a/module/lib/beaker/middleware.py b/module/lib/beaker/middleware.py
deleted file mode 100644
index 7ba88b37d..000000000
--- a/module/lib/beaker/middleware.py
+++ /dev/null
@@ -1,165 +0,0 @@
-import warnings
-
-try:
- from paste.registry import StackedObjectProxy
- beaker_session = StackedObjectProxy(name="Beaker Session")
- beaker_cache = StackedObjectProxy(name="Cache Manager")
-except:
- beaker_cache = None
- beaker_session = None
-
-from beaker.cache import CacheManager
-from beaker.session import Session, SessionObject
-from beaker.util import coerce_cache_params, coerce_session_params, \
- parse_cache_config_options
-
-
-class CacheMiddleware(object):
- cache = beaker_cache
-
- def __init__(self, app, config=None, environ_key='beaker.cache', **kwargs):
- """Initialize the Cache Middleware
-
- The Cache middleware will make a Cache instance available
- every request under the ``environ['beaker.cache']`` key by
- default. The location in environ can be changed by setting
- ``environ_key``.
-
- ``config``
- dict All settings should be prefixed by 'cache.'. This
- method of passing variables is intended for Paste and other
- setups that accumulate multiple component settings in a
- single dictionary. If config contains *no cache. prefixed
- args*, then *all* of the config options will be used to
- intialize the Cache objects.
-
- ``environ_key``
- Location where the Cache instance will keyed in the WSGI
- environ
-
- ``**kwargs``
- All keyword arguments are assumed to be cache settings and
- will override any settings found in ``config``
-
- """
- self.app = app
- config = config or {}
-
- self.options = {}
-
- # Update the options with the parsed config
- self.options.update(parse_cache_config_options(config))
-
- # Add any options from kwargs, but leave out the defaults this
- # time
- self.options.update(
- parse_cache_config_options(kwargs, include_defaults=False))
-
- # Assume all keys are intended for cache if none are prefixed with
- # 'cache.'
- if not self.options and config:
- self.options = config
-
- self.options.update(kwargs)
- self.cache_manager = CacheManager(**self.options)
- self.environ_key = environ_key
-
- def __call__(self, environ, start_response):
- if environ.get('paste.registry'):
- if environ['paste.registry'].reglist:
- environ['paste.registry'].register(self.cache,
- self.cache_manager)
- environ[self.environ_key] = self.cache_manager
- return self.app(environ, start_response)
-
-
-class SessionMiddleware(object):
- session = beaker_session
-
- def __init__(self, wrap_app, config=None, environ_key='beaker.session',
- **kwargs):
- """Initialize the Session Middleware
-
- The Session middleware will make a lazy session instance
- available every request under the ``environ['beaker.session']``
- key by default. The location in environ can be changed by
- setting ``environ_key``.
-
- ``config``
- dict All settings should be prefixed by 'session.'. This
- method of passing variables is intended for Paste and other
- setups that accumulate multiple component settings in a
- single dictionary. If config contains *no cache. prefixed
- args*, then *all* of the config options will be used to
- intialize the Cache objects.
-
- ``environ_key``
- Location where the Session instance will keyed in the WSGI
- environ
-
- ``**kwargs``
- All keyword arguments are assumed to be session settings and
- will override any settings found in ``config``
-
- """
- config = config or {}
-
- # Load up the default params
- self.options = dict(invalidate_corrupt=True, type=None,
- data_dir=None, key='beaker.session.id',
- timeout=None, secret=None, log_file=None)
-
- # Pull out any config args meant for beaker session. if there are any
- for dct in [config, kwargs]:
- for key, val in dct.iteritems():
- if key.startswith('beaker.session.'):
- self.options[key[15:]] = val
- if key.startswith('session.'):
- self.options[key[8:]] = val
- if key.startswith('session_'):
- warnings.warn('Session options should start with session. '
- 'instead of session_.', DeprecationWarning, 2)
- self.options[key[8:]] = val
-
- # Coerce and validate session params
- coerce_session_params(self.options)
-
- # Assume all keys are intended for cache if none are prefixed with
- # 'cache.'
- if not self.options and config:
- self.options = config
-
- self.options.update(kwargs)
- self.wrap_app = wrap_app
- self.environ_key = environ_key
-
- def __call__(self, environ, start_response):
- session = SessionObject(environ, **self.options)
- if environ.get('paste.registry'):
- if environ['paste.registry'].reglist:
- environ['paste.registry'].register(self.session, session)
- environ[self.environ_key] = session
- environ['beaker.get_session'] = self._get_session
-
- def session_start_response(status, headers, exc_info = None):
- if session.accessed():
- session.persist()
- if session.__dict__['_headers']['set_cookie']:
- cookie = session.__dict__['_headers']['cookie_out']
- if cookie:
- headers.append(('Set-cookie', cookie))
- return start_response(status, headers, exc_info)
- return self.wrap_app(environ, session_start_response)
-
- def _get_session(self):
- return Session({}, use_cookies=False, **self.options)
-
-
-def session_filter_factory(global_conf, **kwargs):
- def filter(app):
- return SessionMiddleware(app, global_conf, **kwargs)
- return filter
-
-
-def session_filter_app_factory(app, global_conf, **kwargs):
- return SessionMiddleware(app, global_conf, **kwargs)
diff --git a/module/lib/beaker/session.py b/module/lib/beaker/session.py
deleted file mode 100644
index 7d465530b..000000000
--- a/module/lib/beaker/session.py
+++ /dev/null
@@ -1,618 +0,0 @@
-import Cookie
-import os
-import random
-import time
-from datetime import datetime, timedelta
-
-from beaker.crypto import hmac as HMAC, hmac_sha1 as SHA1, md5
-from beaker.util import pickle
-
-from beaker import crypto
-from beaker.cache import clsmap
-from beaker.exceptions import BeakerException, InvalidCryptoBackendError
-from base64 import b64encode, b64decode
-
-
-__all__ = ['SignedCookie', 'Session']
-
-getpid = hasattr(os, 'getpid') and os.getpid or (lambda : '')
-
-class SignedCookie(Cookie.BaseCookie):
- """Extends python cookie to give digital signature support"""
- def __init__(self, secret, input=None):
- self.secret = secret
- Cookie.BaseCookie.__init__(self, input)
-
- def value_decode(self, val):
- val = val.strip('"')
- sig = HMAC.new(self.secret, val[40:], SHA1).hexdigest()
-
- # Avoid timing attacks
- invalid_bits = 0
- input_sig = val[:40]
- if len(sig) != len(input_sig):
- return None, val
-
- for a, b in zip(sig, input_sig):
- invalid_bits += a != b
-
- if invalid_bits:
- return None, val
- else:
- return val[40:], val
-
- def value_encode(self, val):
- sig = HMAC.new(self.secret, val, SHA1).hexdigest()
- return str(val), ("%s%s" % (sig, val))
-
-
-class Session(dict):
- """Session object that uses container package for storage.
-
- ``key``
- The name the cookie should be set to.
- ``timeout``
- How long session data is considered valid. This is used
- regardless of the cookie being present or not to determine
- whether session data is still valid.
- ``cookie_domain``
- Domain to use for the cookie.
- ``secure``
- Whether or not the cookie should only be sent over SSL.
- """
- def __init__(self, request, id=None, invalidate_corrupt=False,
- use_cookies=True, type=None, data_dir=None,
- key='beaker.session.id', timeout=None, cookie_expires=True,
- cookie_domain=None, secret=None, secure=False,
- namespace_class=None, **namespace_args):
- if not type:
- if data_dir:
- self.type = 'file'
- else:
- self.type = 'memory'
- else:
- self.type = type
-
- self.namespace_class = namespace_class or clsmap[self.type]
-
- self.namespace_args = namespace_args
-
- self.request = request
- self.data_dir = data_dir
- self.key = key
-
- self.timeout = timeout
- self.use_cookies = use_cookies
- self.cookie_expires = cookie_expires
-
- # Default cookie domain/path
- self._domain = cookie_domain
- self._path = '/'
- self.was_invalidated = False
- self.secret = secret
- self.secure = secure
- self.id = id
- self.accessed_dict = {}
-
- if self.use_cookies:
- cookieheader = request.get('cookie', '')
- if secret:
- try:
- self.cookie = SignedCookie(secret, input=cookieheader)
- except Cookie.CookieError:
- self.cookie = SignedCookie(secret, input=None)
- else:
- self.cookie = Cookie.SimpleCookie(input=cookieheader)
-
- if not self.id and self.key in self.cookie:
- self.id = self.cookie[self.key].value
-
- self.is_new = self.id is None
- if self.is_new:
- self._create_id()
- self['_accessed_time'] = self['_creation_time'] = time.time()
- else:
- try:
- self.load()
- except:
- if invalidate_corrupt:
- self.invalidate()
- else:
- raise
-
- def _create_id(self):
- self.id = md5(
- md5("%f%s%f%s" % (time.time(), id({}), random.random(),
- getpid())).hexdigest(),
- ).hexdigest()
- self.is_new = True
- self.last_accessed = None
- if self.use_cookies:
- self.cookie[self.key] = self.id
- if self._domain:
- self.cookie[self.key]['domain'] = self._domain
- if self.secure:
- self.cookie[self.key]['secure'] = True
- self.cookie[self.key]['path'] = self._path
- if self.cookie_expires is not True:
- if self.cookie_expires is False:
- expires = datetime.fromtimestamp( 0x7FFFFFFF )
- elif isinstance(self.cookie_expires, timedelta):
- expires = datetime.today() + self.cookie_expires
- elif isinstance(self.cookie_expires, datetime):
- expires = self.cookie_expires
- else:
- raise ValueError("Invalid argument for cookie_expires: %s"
- % repr(self.cookie_expires))
- self.cookie[self.key]['expires'] = \
- expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT" )
- self.request['cookie_out'] = self.cookie[self.key].output(header='')
- self.request['set_cookie'] = False
-
- def created(self):
- return self['_creation_time']
- created = property(created)
-
- def _set_domain(self, domain):
- self['_domain'] = domain
- self.cookie[self.key]['domain'] = domain
- self.request['cookie_out'] = self.cookie[self.key].output(header='')
- self.request['set_cookie'] = True
-
- def _get_domain(self):
- return self._domain
-
- domain = property(_get_domain, _set_domain)
-
- def _set_path(self, path):
- self['_path'] = path
- self.cookie[self.key]['path'] = path
- self.request['cookie_out'] = self.cookie[self.key].output(header='')
- self.request['set_cookie'] = True
-
- def _get_path(self):
- return self._path
-
- path = property(_get_path, _set_path)
-
- def _delete_cookie(self):
- self.request['set_cookie'] = True
- self.cookie[self.key] = self.id
- if self._domain:
- self.cookie[self.key]['domain'] = self._domain
- if self.secure:
- self.cookie[self.key]['secure'] = True
- self.cookie[self.key]['path'] = '/'
- expires = datetime.today().replace(year=2003)
- self.cookie[self.key]['expires'] = \
- expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT" )
- self.request['cookie_out'] = self.cookie[self.key].output(header='')
- self.request['set_cookie'] = True
-
- def delete(self):
- """Deletes the session from the persistent storage, and sends
- an expired cookie out"""
- if self.use_cookies:
- self._delete_cookie()
- self.clear()
-
- def invalidate(self):
- """Invalidates this session, creates a new session id, returns
- to the is_new state"""
- self.clear()
- self.was_invalidated = True
- self._create_id()
- self.load()
-
- def load(self):
- "Loads the data from this session from persistent storage"
- self.namespace = self.namespace_class(self.id,
- data_dir=self.data_dir, digest_filenames=False,
- **self.namespace_args)
- now = time.time()
- self.request['set_cookie'] = True
-
- self.namespace.acquire_read_lock()
- timed_out = False
- try:
- self.clear()
- try:
- session_data = self.namespace['session']
-
- # Memcached always returns a key, its None when its not
- # present
- if session_data is None:
- session_data = {
- '_creation_time':now,
- '_accessed_time':now
- }
- self.is_new = True
- except (KeyError, TypeError):
- session_data = {
- '_creation_time':now,
- '_accessed_time':now
- }
- self.is_new = True
-
- if self.timeout is not None and \
- now - session_data['_accessed_time'] > self.timeout:
- timed_out= True
- else:
- # Properly set the last_accessed time, which is different
- # than the *currently* _accessed_time
- if self.is_new or '_accessed_time' not in session_data:
- self.last_accessed = None
- else:
- self.last_accessed = session_data['_accessed_time']
-
- # Update the current _accessed_time
- session_data['_accessed_time'] = now
- self.update(session_data)
- self.accessed_dict = session_data.copy()
- finally:
- self.namespace.release_read_lock()
- if timed_out:
- self.invalidate()
-
- def save(self, accessed_only=False):
- """Saves the data for this session to persistent storage
-
- If accessed_only is True, then only the original data loaded
- at the beginning of the request will be saved, with the updated
- last accessed time.
-
- """
- # Look to see if its a new session that was only accessed
- # Don't save it under that case
- if accessed_only and self.is_new:
- return None
-
- if not hasattr(self, 'namespace'):
- self.namespace = self.namespace_class(
- self.id,
- data_dir=self.data_dir,
- digest_filenames=False,
- **self.namespace_args)
-
- self.namespace.acquire_write_lock()
- try:
- if accessed_only:
- data = dict(self.accessed_dict.items())
- else:
- data = dict(self.items())
-
- # Save the data
- if not data and 'session' in self.namespace:
- del self.namespace['session']
- else:
- self.namespace['session'] = data
- finally:
- self.namespace.release_write_lock()
- if self.is_new:
- self.request['set_cookie'] = True
-
- def revert(self):
- """Revert the session to its original state from its first
- access in the request"""
- self.clear()
- self.update(self.accessed_dict)
-
- # TODO: I think both these methods should be removed. They're from
- # the original mod_python code i was ripping off but they really
- # have no use here.
- def lock(self):
- """Locks this session against other processes/threads. This is
- automatic when load/save is called.
-
- ***use with caution*** and always with a corresponding 'unlock'
- inside a "finally:" block, as a stray lock typically cannot be
- unlocked without shutting down the whole application.
-
- """
- self.namespace.acquire_write_lock()
-
- def unlock(self):
- """Unlocks this session against other processes/threads. This
- is automatic when load/save is called.
-
- ***use with caution*** and always within a "finally:" block, as
- a stray lock typically cannot be unlocked without shutting down
- the whole application.
-
- """
- self.namespace.release_write_lock()
-
-class CookieSession(Session):
- """Pure cookie-based session
-
- Options recognized when using cookie-based sessions are slightly
- more restricted than general sessions.
-
- ``key``
- The name the cookie should be set to.
- ``timeout``
- How long session data is considered valid. This is used
- regardless of the cookie being present or not to determine
- whether session data is still valid.
- ``encrypt_key``
- The key to use for the session encryption, if not provided the
- session will not be encrypted.
- ``validate_key``
- The key used to sign the encrypted session
- ``cookie_domain``
- Domain to use for the cookie.
- ``secure``
- Whether or not the cookie should only be sent over SSL.
-
- """
- def __init__(self, request, key='beaker.session.id', timeout=None,
- cookie_expires=True, cookie_domain=None, encrypt_key=None,
- validate_key=None, secure=False, **kwargs):
-
- if not crypto.has_aes and encrypt_key:
- raise InvalidCryptoBackendError("No AES library is installed, can't generate "
- "encrypted cookie-only Session.")
-
- self.request = request
- self.key = key
- self.timeout = timeout
- self.cookie_expires = cookie_expires
- self.encrypt_key = encrypt_key
- self.validate_key = validate_key
- self.request['set_cookie'] = False
- self.secure = secure
- self._domain = cookie_domain
- self._path = '/'
-
- try:
- cookieheader = request['cookie']
- except KeyError:
- cookieheader = ''
-
- if validate_key is None:
- raise BeakerException("No validate_key specified for Cookie only "
- "Session.")
-
- try:
- self.cookie = SignedCookie(validate_key, input=cookieheader)
- except Cookie.CookieError:
- self.cookie = SignedCookie(validate_key, input=None)
-
- self['_id'] = self._make_id()
- self.is_new = True
-
- # If we have a cookie, load it
- if self.key in self.cookie and self.cookie[self.key].value is not None:
- self.is_new = False
- try:
- self.update(self._decrypt_data())
- except:
- pass
- if self.timeout is not None and time.time() - \
- self['_accessed_time'] > self.timeout:
- self.clear()
- self.accessed_dict = self.copy()
- self._create_cookie()
-
- def created(self):
- return self['_creation_time']
- created = property(created)
-
- def id(self):
- return self['_id']
- id = property(id)
-
- def _set_domain(self, domain):
- self['_domain'] = domain
- self._domain = domain
-
- def _get_domain(self):
- return self._domain
-
- domain = property(_get_domain, _set_domain)
-
- def _set_path(self, path):
- self['_path'] = path
- self._path = path
-
- def _get_path(self):
- return self._path
-
- path = property(_get_path, _set_path)
-
- def _encrypt_data(self):
- """Serialize, encipher, and base64 the session dict"""
- if self.encrypt_key:
- nonce = b64encode(os.urandom(40))[:8]
- encrypt_key = crypto.generateCryptoKeys(self.encrypt_key,
- self.validate_key + nonce, 1)
- data = pickle.dumps(self.copy(), 2)
- return nonce + b64encode(crypto.aesEncrypt(data, encrypt_key))
- else:
- data = pickle.dumps(self.copy(), 2)
- return b64encode(data)
-
- def _decrypt_data(self):
- """Bas64, decipher, then un-serialize the data for the session
- dict"""
- if self.encrypt_key:
- nonce = self.cookie[self.key].value[:8]
- encrypt_key = crypto.generateCryptoKeys(self.encrypt_key,
- self.validate_key + nonce, 1)
- payload = b64decode(self.cookie[self.key].value[8:])
- data = crypto.aesDecrypt(payload, encrypt_key)
- return pickle.loads(data)
- else:
- data = b64decode(self.cookie[self.key].value)
- return pickle.loads(data)
-
- def _make_id(self):
- return md5(md5(
- "%f%s%f%s" % (time.time(), id({}), random.random(), getpid())
- ).hexdigest()
- ).hexdigest()
-
- def save(self, accessed_only=False):
- """Saves the data for this session to persistent storage"""
- if accessed_only and self.is_new:
- return
- if accessed_only:
- self.clear()
- self.update(self.accessed_dict)
- self._create_cookie()
-
- def expire(self):
- """Delete the 'expires' attribute on this Session, if any."""
-
- self.pop('_expires', None)
-
- def _create_cookie(self):
- if '_creation_time' not in self:
- self['_creation_time'] = time.time()
- if '_id' not in self:
- self['_id'] = self._make_id()
- self['_accessed_time'] = time.time()
-
- if self.cookie_expires is not True:
- if self.cookie_expires is False:
- expires = datetime.fromtimestamp( 0x7FFFFFFF )
- elif isinstance(self.cookie_expires, timedelta):
- expires = datetime.today() + self.cookie_expires
- elif isinstance(self.cookie_expires, datetime):
- expires = self.cookie_expires
- else:
- raise ValueError("Invalid argument for cookie_expires: %s"
- % repr(self.cookie_expires))
- self['_expires'] = expires
- elif '_expires' in self:
- expires = self['_expires']
- else:
- expires = None
-
- val = self._encrypt_data()
- if len(val) > 4064:
- raise BeakerException("Cookie value is too long to store")
-
- self.cookie[self.key] = val
- if '_domain' in self:
- self.cookie[self.key]['domain'] = self['_domain']
- elif self._domain:
- self.cookie[self.key]['domain'] = self._domain
- if self.secure:
- self.cookie[self.key]['secure'] = True
-
- self.cookie[self.key]['path'] = self.get('_path', '/')
-
- if expires:
- self.cookie[self.key]['expires'] = \
- expires.strftime("%a, %d-%b-%Y %H:%M:%S GMT" )
- self.request['cookie_out'] = self.cookie[self.key].output(header='')
- self.request['set_cookie'] = True
-
- def delete(self):
- """Delete the cookie, and clear the session"""
- # Send a delete cookie request
- self._delete_cookie()
- self.clear()
-
- def invalidate(self):
- """Clear the contents and start a new session"""
- self.delete()
- self['_id'] = self._make_id()
-
-
-class SessionObject(object):
- """Session proxy/lazy creator
-
- This object proxies access to the actual session object, so that in
- the case that the session hasn't been used before, it will be
- setup. This avoid creating and loading the session from persistent
- storage unless its actually used during the request.
-
- """
- def __init__(self, environ, **params):
- self.__dict__['_params'] = params
- self.__dict__['_environ'] = environ
- self.__dict__['_sess'] = None
- self.__dict__['_headers'] = []
-
- def _session(self):
- """Lazy initial creation of session object"""
- if self.__dict__['_sess'] is None:
- params = self.__dict__['_params']
- environ = self.__dict__['_environ']
- self.__dict__['_headers'] = req = {'cookie_out':None}
- req['cookie'] = environ.get('HTTP_COOKIE')
- if params.get('type') == 'cookie':
- self.__dict__['_sess'] = CookieSession(req, **params)
- else:
- self.__dict__['_sess'] = Session(req, use_cookies=True,
- **params)
- return self.__dict__['_sess']
-
- def __getattr__(self, attr):
- return getattr(self._session(), attr)
-
- def __setattr__(self, attr, value):
- setattr(self._session(), attr, value)
-
- def __delattr__(self, name):
- self._session().__delattr__(name)
-
- def __getitem__(self, key):
- return self._session()[key]
-
- def __setitem__(self, key, value):
- self._session()[key] = value
-
- def __delitem__(self, key):
- self._session().__delitem__(key)
-
- def __repr__(self):
- return self._session().__repr__()
-
- def __iter__(self):
- """Only works for proxying to a dict"""
- return iter(self._session().keys())
-
- def __contains__(self, key):
- return self._session().has_key(key)
-
- def get_by_id(self, id):
- """Loads a session given a session ID"""
- params = self.__dict__['_params']
- session = Session({}, use_cookies=False, id=id, **params)
- if session.is_new:
- return None
- return session
-
- def save(self):
- self.__dict__['_dirty'] = True
-
- def delete(self):
- self.__dict__['_dirty'] = True
- self._session().delete()
-
- def persist(self):
- """Persist the session to the storage
-
- If its set to autosave, then the entire session will be saved
- regardless of if save() has been called. Otherwise, just the
- accessed time will be updated if save() was not called, or
- the session will be saved if save() was called.
-
- """
- if self.__dict__['_params'].get('auto'):
- self._session().save()
- else:
- if self.__dict__.get('_dirty'):
- self._session().save()
- else:
- self._session().save(accessed_only=True)
-
- def dirty(self):
- return self.__dict__.get('_dirty', False)
-
- def accessed(self):
- """Returns whether or not the session has been accessed"""
- return self.__dict__['_sess'] is not None
diff --git a/module/lib/beaker/synchronization.py b/module/lib/beaker/synchronization.py
deleted file mode 100644
index 761303707..000000000
--- a/module/lib/beaker/synchronization.py
+++ /dev/null
@@ -1,381 +0,0 @@
-"""Synchronization functions.
-
-File- and mutex-based mutual exclusion synchronizers are provided,
-as well as a name-based mutex which locks within an application
-based on a string name.
-
-"""
-
-import os
-import sys
-import tempfile
-
-try:
- import threading as _threading
-except ImportError:
- import dummy_threading as _threading
-
-# check for fcntl module
-try:
- sys.getwindowsversion()
- has_flock = False
-except:
- try:
- import fcntl
- has_flock = True
- except ImportError:
- has_flock = False
-
-from beaker import util
-from beaker.exceptions import LockError
-
-__all__ = ["file_synchronizer", "mutex_synchronizer", "null_synchronizer",
- "NameLock", "_threading"]
-
-
-class NameLock(object):
- """a proxy for an RLock object that is stored in a name based
- registry.
-
- Multiple threads can get a reference to the same RLock based on the
- name alone, and synchronize operations related to that name.
-
- """
- locks = util.WeakValuedRegistry()
-
- class NLContainer(object):
- def __init__(self, reentrant):
- if reentrant:
- self.lock = _threading.RLock()
- else:
- self.lock = _threading.Lock()
- def __call__(self):
- return self.lock
-
- def __init__(self, identifier = None, reentrant = False):
- if identifier is None:
- self._lock = NameLock.NLContainer(reentrant)
- else:
- self._lock = NameLock.locks.get(identifier, NameLock.NLContainer,
- reentrant)
-
- def acquire(self, wait = True):
- return self._lock().acquire(wait)
-
- def release(self):
- self._lock().release()
-
-
-_synchronizers = util.WeakValuedRegistry()
-def _synchronizer(identifier, cls, **kwargs):
- return _synchronizers.sync_get((identifier, cls), cls, identifier, **kwargs)
-
-
-def file_synchronizer(identifier, **kwargs):
- if not has_flock or 'lock_dir' not in kwargs:
- return mutex_synchronizer(identifier)
- else:
- return _synchronizer(identifier, FileSynchronizer, **kwargs)
-
-
-def mutex_synchronizer(identifier, **kwargs):
- return _synchronizer(identifier, ConditionSynchronizer, **kwargs)
-
-
-class null_synchronizer(object):
- def acquire_write_lock(self, wait=True):
- return True
- def acquire_read_lock(self):
- pass
- def release_write_lock(self):
- pass
- def release_read_lock(self):
- pass
- acquire = acquire_write_lock
- release = release_write_lock
-
-
-class SynchronizerImpl(object):
- def __init__(self):
- self._state = util.ThreadLocal()
-
- class SyncState(object):
- __slots__ = 'reentrantcount', 'writing', 'reading'
-
- def __init__(self):
- self.reentrantcount = 0
- self.writing = False
- self.reading = False
-
- def state(self):
- if not self._state.has():
- state = SynchronizerImpl.SyncState()
- self._state.put(state)
- return state
- else:
- return self._state.get()
- state = property(state)
-
- def release_read_lock(self):
- state = self.state
-
- if state.writing:
- raise LockError("lock is in writing state")
- if not state.reading:
- raise LockError("lock is not in reading state")
-
- if state.reentrantcount == 1:
- self.do_release_read_lock()
- state.reading = False
-
- state.reentrantcount -= 1
-
- def acquire_read_lock(self, wait = True):
- state = self.state
-
- if state.writing:
- raise LockError("lock is in writing state")
-
- if state.reentrantcount == 0:
- x = self.do_acquire_read_lock(wait)
- if (wait or x):
- state.reentrantcount += 1
- state.reading = True
- return x
- elif state.reading:
- state.reentrantcount += 1
- return True
-
- def release_write_lock(self):
- state = self.state
-
- if state.reading:
- raise LockError("lock is in reading state")
- if not state.writing:
- raise LockError("lock is not in writing state")
-
- if state.reentrantcount == 1:
- self.do_release_write_lock()
- state.writing = False
-
- state.reentrantcount -= 1
-
- release = release_write_lock
-
- def acquire_write_lock(self, wait = True):
- state = self.state
-
- if state.reading:
- raise LockError("lock is in reading state")
-
- if state.reentrantcount == 0:
- x = self.do_acquire_write_lock(wait)
- if (wait or x):
- state.reentrantcount += 1
- state.writing = True
- return x
- elif state.writing:
- state.reentrantcount += 1
- return True
-
- acquire = acquire_write_lock
-
- def do_release_read_lock(self):
- raise NotImplementedError()
-
- def do_acquire_read_lock(self):
- raise NotImplementedError()
-
- def do_release_write_lock(self):
- raise NotImplementedError()
-
- def do_acquire_write_lock(self):
- raise NotImplementedError()
-
-
-class FileSynchronizer(SynchronizerImpl):
- """a synchronizer which locks using flock().
-
- Adapted for Python/multithreads from Apache::Session::Lock::File,
- http://search.cpan.org/src/CWEST/Apache-Session-1.81/Session/Lock/File.pm
-
- This module does not unlink temporary files,
- because it interferes with proper locking. This can cause
- problems on certain systems (Linux) whose file systems (ext2) do not
- perform well with lots of files in one directory. To prevent this
- you should use a script to clean out old files from your lock directory.
-
- """
- def __init__(self, identifier, lock_dir):
- super(FileSynchronizer, self).__init__()
- self._filedescriptor = util.ThreadLocal()
-
- if lock_dir is None:
- lock_dir = tempfile.gettempdir()
- else:
- lock_dir = lock_dir
-
- self.filename = util.encoded_path(
- lock_dir,
- [identifier],
- extension='.lock'
- )
-
- def _filedesc(self):
- return self._filedescriptor.get()
- _filedesc = property(_filedesc)
-
- def _open(self, mode):
- filedescriptor = self._filedesc
- if filedescriptor is None:
- filedescriptor = os.open(self.filename, mode)
- self._filedescriptor.put(filedescriptor)
- return filedescriptor
-
- def do_acquire_read_lock(self, wait):
- filedescriptor = self._open(os.O_CREAT | os.O_RDONLY)
- if not wait:
- try:
- fcntl.flock(filedescriptor, fcntl.LOCK_SH | fcntl.LOCK_NB)
- return True
- except IOError:
- os.close(filedescriptor)
- self._filedescriptor.remove()
- return False
- else:
- fcntl.flock(filedescriptor, fcntl.LOCK_SH)
- return True
-
- def do_acquire_write_lock(self, wait):
- filedescriptor = self._open(os.O_CREAT | os.O_WRONLY)
- if not wait:
- try:
- fcntl.flock(filedescriptor, fcntl.LOCK_EX | fcntl.LOCK_NB)
- return True
- except IOError:
- os.close(filedescriptor)
- self._filedescriptor.remove()
- return False
- else:
- fcntl.flock(filedescriptor, fcntl.LOCK_EX)
- return True
-
- def do_release_read_lock(self):
- self._release_all_locks()
-
- def do_release_write_lock(self):
- self._release_all_locks()
-
- def _release_all_locks(self):
- filedescriptor = self._filedesc
- if filedescriptor is not None:
- fcntl.flock(filedescriptor, fcntl.LOCK_UN)
- os.close(filedescriptor)
- self._filedescriptor.remove()
-
-
-class ConditionSynchronizer(SynchronizerImpl):
- """a synchronizer using a Condition."""
-
- def __init__(self, identifier):
- super(ConditionSynchronizer, self).__init__()
-
- # counts how many asynchronous methods are executing
- self.async = 0
-
- # pointer to thread that is the current sync operation
- self.current_sync_operation = None
-
- # condition object to lock on
- self.condition = _threading.Condition(_threading.Lock())
-
- def do_acquire_read_lock(self, wait = True):
- self.condition.acquire()
- try:
- # see if a synchronous operation is waiting to start
- # or is already running, in which case we wait (or just
- # give up and return)
- if wait:
- while self.current_sync_operation is not None:
- self.condition.wait()
- else:
- if self.current_sync_operation is not None:
- return False
-
- self.async += 1
- finally:
- self.condition.release()
-
- if not wait:
- return True
-
- def do_release_read_lock(self):
- self.condition.acquire()
- try:
- self.async -= 1
-
- # check if we are the last asynchronous reader thread
- # out the door.
- if self.async == 0:
- # yes. so if a sync operation is waiting, notifyAll to wake
- # it up
- if self.current_sync_operation is not None:
- self.condition.notifyAll()
- elif self.async < 0:
- raise LockError("Synchronizer error - too many "
- "release_read_locks called")
- finally:
- self.condition.release()
-
- def do_acquire_write_lock(self, wait = True):
- self.condition.acquire()
- try:
- # here, we are not a synchronous reader, and after returning,
- # assuming waiting or immediate availability, we will be.
-
- if wait:
- # if another sync is working, wait
- while self.current_sync_operation is not None:
- self.condition.wait()
- else:
- # if another sync is working,
- # we dont want to wait, so forget it
- if self.current_sync_operation is not None:
- return False
-
- # establish ourselves as the current sync
- # this indicates to other read/write operations
- # that they should wait until this is None again
- self.current_sync_operation = _threading.currentThread()
-
- # now wait again for asyncs to finish
- if self.async > 0:
- if wait:
- # wait
- self.condition.wait()
- else:
- # we dont want to wait, so forget it
- self.current_sync_operation = None
- return False
- finally:
- self.condition.release()
-
- if not wait:
- return True
-
- def do_release_write_lock(self):
- self.condition.acquire()
- try:
- if self.current_sync_operation is not _threading.currentThread():
- raise LockError("Synchronizer error - current thread doesnt "
- "have the write lock")
-
- # reset the current sync operation so
- # another can get it
- self.current_sync_operation = None
-
- # tell everyone to get ready
- self.condition.notifyAll()
- finally:
- # everyone go !!
- self.condition.release()
diff --git a/module/lib/beaker/util.py b/module/lib/beaker/util.py
deleted file mode 100644
index 04c9617c5..000000000
--- a/module/lib/beaker/util.py
+++ /dev/null
@@ -1,302 +0,0 @@
-"""Beaker utilities"""
-
-try:
- import thread as _thread
- import threading as _threading
-except ImportError:
- import dummy_thread as _thread
- import dummy_threading as _threading
-
-from datetime import datetime, timedelta
-import os
-import string
-import types
-import weakref
-import warnings
-import sys
-
-py3k = getattr(sys, 'py3kwarning', False) or sys.version_info >= (3, 0)
-py24 = sys.version_info < (2,5)
-jython = sys.platform.startswith('java')
-
-if py3k or jython:
- import pickle
-else:
- import cPickle as pickle
-
-from beaker.converters import asbool
-from threading import local as _tlocal
-
-
-__all__ = ["ThreadLocal", "Registry", "WeakValuedRegistry", "SyncDict",
- "encoded_path", "verify_directory"]
-
-
-def verify_directory(dir):
- """verifies and creates a directory. tries to
- ignore collisions with other threads and processes."""
-
- tries = 0
- while not os.access(dir, os.F_OK):
- try:
- tries += 1
- os.makedirs(dir)
- except:
- if tries > 5:
- raise
-
-
-def deprecated(message):
- def wrapper(fn):
- def deprecated_method(*args, **kargs):
- warnings.warn(message, DeprecationWarning, 2)
- return fn(*args, **kargs)
- # TODO: use decorator ? functools.wrapper ?
- deprecated_method.__name__ = fn.__name__
- deprecated_method.__doc__ = "%s\n\n%s" % (message, fn.__doc__)
- return deprecated_method
- return wrapper
-
-class ThreadLocal(object):
- """stores a value on a per-thread basis"""
-
- __slots__ = '_tlocal'
-
- def __init__(self):
- self._tlocal = _tlocal()
-
- def put(self, value):
- self._tlocal.value = value
-
- def has(self):
- return hasattr(self._tlocal, 'value')
-
- def get(self, default=None):
- return getattr(self._tlocal, 'value', default)
-
- def remove(self):
- del self._tlocal.value
-
-class SyncDict(object):
- """
- An efficient/threadsafe singleton map algorithm, a.k.a.
- "get a value based on this key, and create if not found or not
- valid" paradigm:
-
- exists && isvalid ? get : create
-
- Designed to work with weakref dictionaries to expect items
- to asynchronously disappear from the dictionary.
-
- Use python 2.3.3 or greater ! a major bug was just fixed in Nov.
- 2003 that was driving me nuts with garbage collection/weakrefs in
- this section.
-
- """
- def __init__(self):
- self.mutex = _thread.allocate_lock()
- self.dict = {}
-
- def get(self, key, createfunc, *args, **kwargs):
- try:
- if self.has_key(key):
- return self.dict[key]
- else:
- return self.sync_get(key, createfunc, *args, **kwargs)
- except KeyError:
- return self.sync_get(key, createfunc, *args, **kwargs)
-
- def sync_get(self, key, createfunc, *args, **kwargs):
- self.mutex.acquire()
- try:
- try:
- if self.has_key(key):
- return self.dict[key]
- else:
- return self._create(key, createfunc, *args, **kwargs)
- except KeyError:
- return self._create(key, createfunc, *args, **kwargs)
- finally:
- self.mutex.release()
-
- def _create(self, key, createfunc, *args, **kwargs):
- self[key] = obj = createfunc(*args, **kwargs)
- return obj
-
- def has_key(self, key):
- return self.dict.has_key(key)
-
- def __contains__(self, key):
- return self.dict.__contains__(key)
- def __getitem__(self, key):
- return self.dict.__getitem__(key)
- def __setitem__(self, key, value):
- self.dict.__setitem__(key, value)
- def __delitem__(self, key):
- return self.dict.__delitem__(key)
- def clear(self):
- self.dict.clear()
-
-
-class WeakValuedRegistry(SyncDict):
- def __init__(self):
- self.mutex = _threading.RLock()
- self.dict = weakref.WeakValueDictionary()
-
-sha1 = None
-def encoded_path(root, identifiers, extension = ".enc", depth = 3,
- digest_filenames=True):
-
- """Generate a unique file-accessible path from the given list of
- identifiers starting at the given root directory."""
- ident = "_".join(identifiers)
-
- global sha1
- if sha1 is None:
- from beaker.crypto import sha1
-
- if digest_filenames:
- if py3k:
- ident = sha1(ident.encode('utf-8')).hexdigest()
- else:
- ident = sha1(ident).hexdigest()
-
- ident = os.path.basename(ident)
-
- tokens = []
- for d in range(1, depth):
- tokens.append(ident[0:d])
-
- dir = os.path.join(root, *tokens)
- verify_directory(dir)
-
- return os.path.join(dir, ident + extension)
-
-
-def verify_options(opt, types, error):
- if not isinstance(opt, types):
- if not isinstance(types, tuple):
- types = (types,)
- coerced = False
- for typ in types:
- try:
- if typ in (list, tuple):
- opt = [x.strip() for x in opt.split(',')]
- else:
- if typ == bool:
- typ = asbool
- opt = typ(opt)
- coerced = True
- except:
- pass
- if coerced:
- break
- if not coerced:
- raise Exception(error)
- elif isinstance(opt, str) and not opt.strip():
- raise Exception("Empty strings are invalid for: %s" % error)
- return opt
-
-
-def verify_rules(params, ruleset):
- for key, types, message in ruleset:
- if key in params:
- params[key] = verify_options(params[key], types, message)
- return params
-
-
-def coerce_session_params(params):
- rules = [
- ('data_dir', (str, types.NoneType), "data_dir must be a string "
- "referring to a directory."),
- ('lock_dir', (str, types.NoneType), "lock_dir must be a string referring to a "
- "directory."),
- ('type', (str, types.NoneType), "Session type must be a string."),
- ('cookie_expires', (bool, datetime, timedelta), "Cookie expires was "
- "not a boolean, datetime, or timedelta instance."),
- ('cookie_domain', (str, types.NoneType), "Cookie domain must be a "
- "string."),
- ('id', (str,), "Session id must be a string."),
- ('key', (str,), "Session key must be a string."),
- ('secret', (str, types.NoneType), "Session secret must be a string."),
- ('validate_key', (str, types.NoneType), "Session encrypt_key must be "
- "a string."),
- ('encrypt_key', (str, types.NoneType), "Session validate_key must be "
- "a string."),
- ('secure', (bool, types.NoneType), "Session secure must be a boolean."),
- ('timeout', (int, types.NoneType), "Session timeout must be an "
- "integer."),
- ('auto', (bool, types.NoneType), "Session is created if accessed."),
- ]
- return verify_rules(params, rules)
-
-
-def coerce_cache_params(params):
- rules = [
- ('data_dir', (str, types.NoneType), "data_dir must be a string "
- "referring to a directory."),
- ('lock_dir', (str, types.NoneType), "lock_dir must be a string referring to a "
- "directory."),
- ('type', (str,), "Cache type must be a string."),
- ('enabled', (bool, types.NoneType), "enabled must be true/false "
- "if present."),
- ('expire', (int, types.NoneType), "expire must be an integer representing "
- "how many seconds the cache is valid for"),
- ('regions', (list, tuple, types.NoneType), "Regions must be a "
- "comma seperated list of valid regions")
- ]
- return verify_rules(params, rules)
-
-
-def parse_cache_config_options(config, include_defaults=True):
- """Parse configuration options and validate for use with the
- CacheManager"""
-
- # Load default cache options
- if include_defaults:
- options= dict(type='memory', data_dir=None, expire=None,
- log_file=None)
- else:
- options = {}
- for key, val in config.iteritems():
- if key.startswith('beaker.cache.'):
- options[key[13:]] = val
- if key.startswith('cache.'):
- options[key[6:]] = val
- coerce_cache_params(options)
-
- # Set cache to enabled if not turned off
- if 'enabled' not in options:
- options['enabled'] = True
-
- # Configure region dict if regions are available
- regions = options.pop('regions', None)
- if regions:
- region_configs = {}
- for region in regions:
- # Setup the default cache options
- region_options = dict(data_dir=options.get('data_dir'),
- lock_dir=options.get('lock_dir'),
- type=options.get('type'),
- enabled=options['enabled'],
- expire=options.get('expire'))
- region_len = len(region) + 1
- for key in options.keys():
- if key.startswith('%s.' % region):
- region_options[key[region_len:]] = options.pop(key)
- coerce_cache_params(region_options)
- region_configs[region] = region_options
- options['cache_regions'] = region_configs
- return options
-
-def func_namespace(func):
- """Generates a unique namespace for a function"""
- kls = None
- if hasattr(func, 'im_func'):
- kls = func.im_class
- func = func.im_func
-
- if kls:
- return '%s.%s' % (kls.__module__, kls.__name__)
- else:
- return '%s.%s' % (func.__module__, func.__name__)
diff --git a/module/lib/bottle.py b/module/lib/bottle.py
deleted file mode 100644
index 2c243278e..000000000
--- a/module/lib/bottle.py
+++ /dev/null
@@ -1,2922 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""
-Bottle is a fast and simple micro-framework for small web applications. It
-offers request dispatching (Routes) with url parameter support, templates,
-a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and
-template engines - all in a single file and with no dependencies other than the
-Python Standard Library.
-
-Homepage and documentation: http://bottlepy.org/
-
-Copyright (c) 2011, Marcel Hellkamp.
-License: MIT (see LICENSE.txt for details)
-"""
-
-from __future__ import with_statement
-
-__author__ = 'Marcel Hellkamp'
-__version__ = '0.10.2'
-__license__ = 'MIT'
-
-# The gevent server adapter needs to patch some modules before they are imported
-# This is why we parse the commandline parameters here but handle them later
-if __name__ == '__main__':
- from optparse import OptionParser
- _cmd_parser = OptionParser(usage="usage: %prog [options] package.module:app")
- _opt = _cmd_parser.add_option
- _opt("--version", action="store_true", help="show version number.")
- _opt("-b", "--bind", metavar="ADDRESS", help="bind socket to ADDRESS.")
- _opt("-s", "--server", default='wsgiref', help="use SERVER as backend.")
- _opt("-p", "--plugin", action="append", help="install additional plugin/s.")
- _opt("--debug", action="store_true", help="start server in debug mode.")
- _opt("--reload", action="store_true", help="auto-reload on file changes.")
- _cmd_options, _cmd_args = _cmd_parser.parse_args()
- if _cmd_options.server and _cmd_options.server.startswith('gevent'):
- import gevent.monkey; gevent.monkey.patch_all()
-
-import sys
-import base64
-import cgi
-import email.utils
-import functools
-import hmac
-import httplib
-import imp
-import itertools
-import mimetypes
-import os
-import re
-import subprocess
-import tempfile
-import thread
-import threading
-import time
-import warnings
-
-from Cookie import SimpleCookie
-from datetime import date as datedate, datetime, timedelta
-from tempfile import TemporaryFile
-from traceback import format_exc, print_exc
-from urlparse import urljoin, SplitResult as UrlSplitResult
-
-# Workaround for a bug in some versions of lib2to3 (fixed on CPython 2.7 and 3.2)
-import urllib
-urlencode = urllib.urlencode
-urlquote = urllib.quote
-urlunquote = urllib.unquote
-
-try: from collections import MutableMapping as DictMixin
-except ImportError: # pragma: no cover
- from UserDict import DictMixin
-
-try: from urlparse import parse_qs
-except ImportError: # pragma: no cover
- from cgi import parse_qs
-
-try: import cPickle as pickle
-except ImportError: # pragma: no cover
- import pickle
-
-try: from json import dumps as json_dumps, loads as json_lds
-except ImportError: # pragma: no cover
- try: from simplejson import dumps as json_dumps, loads as json_lds
- except ImportError: # pragma: no cover
- try: from django.utils.simplejson import dumps as json_dumps, loads as json_lds
- except ImportError: # pragma: no cover
- def json_dumps(data):
- raise ImportError("JSON support requires Python 2.6 or simplejson.")
- json_lds = json_dumps
-
-py3k = sys.version_info >= (3,0,0)
-NCTextIOWrapper = None
-
-if py3k: # pragma: no cover
- json_loads = lambda s: json_lds(touni(s))
- # See Request.POST
- from io import BytesIO
- def touni(x, enc='utf8', err='strict'):
- """ Convert anything to unicode """
- return str(x, enc, err) if isinstance(x, bytes) else str(x)
- if sys.version_info < (3,2,0):
- from io import TextIOWrapper
- class NCTextIOWrapper(TextIOWrapper):
- ''' Garbage collecting an io.TextIOWrapper(buffer) instance closes
- the wrapped buffer. This subclass keeps it open. '''
- def close(self): pass
-else:
- json_loads = json_lds
- from StringIO import StringIO as BytesIO
- bytes = str
- def touni(x, enc='utf8', err='strict'):
- """ Convert anything to unicode """
- return x if isinstance(x, unicode) else unicode(str(x), enc, err)
-
-def tob(data, enc='utf8'):
- """ Convert anything to bytes """
- return data.encode(enc) if isinstance(data, unicode) else bytes(data)
-
-tonat = touni if py3k else tob
-tonat.__doc__ = """ Convert anything to native strings """
-
-def try_update_wrapper(wrapper, wrapped, *a, **ka):
- try: # Bug: functools breaks if wrapper is an instane method
- functools.update_wrapper(wrapper, wrapped, *a, **ka)
- except AttributeError: pass
-
-# Backward compatibility
-def depr(message):
- warnings.warn(message, DeprecationWarning, stacklevel=3)
-
-
-# Small helpers
-def makelist(data):
- if isinstance(data, (tuple, list, set, dict)): return list(data)
- elif data: return [data]
- else: return []
-
-
-class DictProperty(object):
- ''' Property that maps to a key in a local dict-like attribute. '''
- def __init__(self, attr, key=None, read_only=False):
- self.attr, self.key, self.read_only = attr, key, read_only
-
- def __call__(self, func):
- functools.update_wrapper(self, func, updated=[])
- self.getter, self.key = func, self.key or func.__name__
- return self
-
- def __get__(self, obj, cls):
- if obj is None: return self
- key, storage = self.key, getattr(obj, self.attr)
- if key not in storage: storage[key] = self.getter(obj)
- return storage[key]
-
- def __set__(self, obj, value):
- if self.read_only: raise AttributeError("Read-Only property.")
- getattr(obj, self.attr)[self.key] = value
-
- def __delete__(self, obj):
- if self.read_only: raise AttributeError("Read-Only property.")
- del getattr(obj, self.attr)[self.key]
-
-
-class CachedProperty(object):
- ''' A property that is only computed once per instance and then replaces
- itself with an ordinary attribute. Deleting the attribute resets the
- property. '''
-
- def __init__(self, func):
- self.func = func
-
- def __get__(self, obj, cls):
- if obj is None: return self
- value = obj.__dict__[self.func.__name__] = self.func(obj)
- return value
-
-cached_property = CachedProperty
-
-
-class lazy_attribute(object): # Does not need configuration -> lower-case name
- ''' A property that caches itself to the class object. '''
- def __init__(self, func):
- functools.update_wrapper(self, func, updated=[])
- self.getter = func
-
- def __get__(self, obj, cls):
- value = self.getter(cls)
- setattr(cls, self.__name__, value)
- return value
-
-
-
-
-
-
-###############################################################################
-# Exceptions and Events ########################################################
-###############################################################################
-
-
-class BottleException(Exception):
- """ A base class for exceptions used by bottle. """
- pass
-
-
-#TODO: These should subclass BaseRequest
-
-class HTTPResponse(BottleException):
- """ Used to break execution and immediately finish the response """
- def __init__(self, output='', status=200, header=None):
- super(BottleException, self).__init__("HTTP Response %d" % status)
- self.status = int(status)
- self.output = output
- self.headers = HeaderDict(header) if header else None
-
- def apply(self, response):
- if self.headers:
- for key, value in self.headers.iterallitems():
- response.headers[key] = value
- response.status = self.status
-
-
-class HTTPError(HTTPResponse):
- """ Used to generate an error page """
- def __init__(self, code=500, output='Unknown Error', exception=None,
- traceback=None, header=None):
- super(HTTPError, self).__init__(output, code, header)
- self.exception = exception
- self.traceback = traceback
-
- def __repr__(self):
- return template(ERROR_PAGE_TEMPLATE, e=self)
-
-
-
-
-
-
-###############################################################################
-# Routing ######################################################################
-###############################################################################
-
-
-class RouteError(BottleException):
- """ This is a base class for all routing related exceptions """
-
-
-class RouteReset(BottleException):
- """ If raised by a plugin or request handler, the route is reset and all
- plugins are re-applied. """
-
-class RouterUnknownModeError(RouteError): pass
-
-class RouteSyntaxError(RouteError):
- """ The route parser found something not supported by this router """
-
-class RouteBuildError(RouteError):
- """ The route could not been built """
-
-class Router(object):
- ''' A Router is an ordered collection of route->target pairs. It is used to
- efficiently match WSGI requests against a number of routes and return
- the first target that satisfies the request. The target may be anything,
- usually a string, ID or callable object. A route consists of a path-rule
- and a HTTP method.
-
- The path-rule is either a static path (e.g. `/contact`) or a dynamic
- path that contains wildcards (e.g. `/wiki/<page>`). The wildcard syntax
- and details on the matching order are described in docs:`routing`.
- '''
-
- default_pattern = '[^/]+'
- default_filter = 're'
- #: Sorry for the mess. It works. Trust me.
- rule_syntax = re.compile('(\\\\*)'\
- '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)'\
- '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)'\
- '(?::((?:\\\\.|[^\\\\>]+)+)?)?)?>))')
-
- def __init__(self, strict=False):
- self.rules = {} # A {rule: Rule} mapping
- self.builder = {} # A rule/name->build_info mapping
- self.static = {} # Cache for static routes: {path: {method: target}}
- self.dynamic = [] # Cache for dynamic routes. See _compile()
- #: If true, static routes are no longer checked first.
- self.strict_order = strict
- self.filters = {'re': self.re_filter, 'int': self.int_filter,
- 'float': self.re_filter, 'path': self.path_filter}
-
- def re_filter(self, conf):
- return conf or self.default_pattern, None, None
-
- def int_filter(self, conf):
- return r'-?\d+', int, lambda x: str(int(x))
-
- def float_filter(self, conf):
- return r'-?\d*\.\d+', float, lambda x: str(float(x))
-
- def path_filter(self, conf):
- return r'.*?', None, None
-
- def add_filter(self, name, func):
- ''' Add a filter. The provided function is called with the configuration
- string as parameter and must return a (regexp, to_python, to_url) tuple.
- The first element is a string, the last two are callables or None. '''
- self.filters[name] = func
-
- def parse_rule(self, rule):
- ''' Parses a rule into a (name, filter, conf) token stream. If mode is
- None, name contains a static rule part. '''
- offset, prefix = 0, ''
- for match in self.rule_syntax.finditer(rule):
- prefix += rule[offset:match.start()]
- g = match.groups()
- if len(g[0])%2: # Escaped wildcard
- prefix += match.group(0)[len(g[0]):]
- offset = match.end()
- continue
- if prefix: yield prefix, None, None
- name, filtr, conf = g[1:4] if not g[2] is None else g[4:7]
- if not filtr: filtr = self.default_filter
- yield name, filtr, conf or None
- offset, prefix = match.end(), ''
- if offset <= len(rule) or prefix:
- yield prefix+rule[offset:], None, None
-
- def add(self, rule, method, target, name=None):
- ''' Add a new route or replace the target for an existing route. '''
- if rule in self.rules:
- self.rules[rule][method] = target
- if name: self.builder[name] = self.builder[rule]
- return
-
- target = self.rules[rule] = {method: target}
-
- # Build pattern and other structures for dynamic routes
- anons = 0 # Number of anonymous wildcards
- pattern = '' # Regular expression pattern
- filters = [] # Lists of wildcard input filters
- builder = [] # Data structure for the URL builder
- is_static = True
- for key, mode, conf in self.parse_rule(rule):
- if mode:
- is_static = False
- mask, in_filter, out_filter = self.filters[mode](conf)
- if key:
- pattern += '(?P<%s>%s)' % (key, mask)
- else:
- pattern += '(?:%s)' % mask
- key = 'anon%d' % anons; anons += 1
- if in_filter: filters.append((key, in_filter))
- builder.append((key, out_filter or str))
- elif key:
- pattern += re.escape(key)
- builder.append((None, key))
- self.builder[rule] = builder
- if name: self.builder[name] = builder
-
- if is_static and not self.strict_order:
- self.static[self.build(rule)] = target
- return
-
- def fpat_sub(m):
- return m.group(0) if len(m.group(1)) % 2 else m.group(1) + '(?:'
- flat_pattern = re.sub(r'(\\*)(\(\?P<[^>]*>|\((?!\?))', fpat_sub, pattern)
-
- try:
- re_match = re.compile('^(%s)$' % pattern).match
- except re.error, e:
- raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, e))
-
- def match(path):
- """ Return an url-argument dictionary. """
- url_args = re_match(path).groupdict()
- for name, wildcard_filter in filters:
- try:
- url_args[name] = wildcard_filter(url_args[name])
- except ValueError:
- raise HTTPError(400, 'Path has wrong format.')
- return url_args
-
- try:
- combined = '%s|(^%s$)' % (self.dynamic[-1][0].pattern, flat_pattern)
- self.dynamic[-1] = (re.compile(combined), self.dynamic[-1][1])
- self.dynamic[-1][1].append((match, target))
- except (AssertionError, IndexError), e: # AssertionError: Too many groups
- self.dynamic.append((re.compile('(^%s$)' % flat_pattern),
- [(match, target)]))
- return match
-
- def build(self, _name, *anons, **query):
- ''' Build an URL by filling the wildcards in a rule. '''
- builder = self.builder.get(_name)
- if not builder: raise RouteBuildError("No route with that name.", _name)
- try:
- for i, value in enumerate(anons): query['anon%d'%i] = value
- url = ''.join([f(query.pop(n)) if n else f for (n,f) in builder])
- return url if not query else url+'?'+urlencode(query)
- except KeyError, e:
- raise RouteBuildError('Missing URL argument: %r' % e.args[0])
-
- def match(self, environ):
- ''' Return a (target, url_agrs) tuple or raise HTTPError(400/404/405). '''
- path, targets, urlargs = environ['PATH_INFO'] or '/', None, {}
- if path in self.static:
- targets = self.static[path]
- else:
- for combined, rules in self.dynamic:
- match = combined.match(path)
- if not match: continue
- getargs, targets = rules[match.lastindex - 1]
- urlargs = getargs(path) if getargs else {}
- break
-
- if not targets:
- raise HTTPError(404, "Not found: " + repr(environ['PATH_INFO']))
- method = environ['REQUEST_METHOD'].upper()
- if method in targets:
- return targets[method], urlargs
- if method == 'HEAD' and 'GET' in targets:
- return targets['GET'], urlargs
- if 'ANY' in targets:
- return targets['ANY'], urlargs
- allowed = [verb for verb in targets if verb != 'ANY']
- if 'GET' in allowed and 'HEAD' not in allowed:
- allowed.append('HEAD')
- raise HTTPError(405, "Method not allowed.",
- header=[('Allow',",".join(allowed))])
-
-
-
-class Route(object):
- ''' This class wraps a route callback along with route specific metadata and
- configuration and applies Plugins on demand. It is also responsible for
- turing an URL path rule into a regular expression usable by the Router.
- '''
-
-
- def __init__(self, app, rule, method, callback, name=None,
- plugins=None, skiplist=None, **config):
- #: The application this route is installed to.
- self.app = app
- #: The path-rule string (e.g. ``/wiki/:page``).
- self.rule = rule
- #: The HTTP method as a string (e.g. ``GET``).
- self.method = method
- #: The original callback with no plugins applied. Useful for introspection.
- self.callback = callback
- #: The name of the route (if specified) or ``None``.
- self.name = name or None
- #: A list of route-specific plugins (see :meth:`Bottle.route`).
- self.plugins = plugins or []
- #: A list of plugins to not apply to this route (see :meth:`Bottle.route`).
- self.skiplist = skiplist or []
- #: Additional keyword arguments passed to the :meth:`Bottle.route`
- #: decorator are stored in this dictionary. Used for route-specific
- #: plugin configuration and meta-data.
- self.config = ConfigDict(config)
-
- def __call__(self, *a, **ka):
- depr("Some APIs changed to return Route() instances instead of"\
- " callables. Make sure to use the Route.call method and not to"\
- " call Route instances directly.")
- return self.call(*a, **ka)
-
- @cached_property
- def call(self):
- ''' The route callback with all plugins applied. This property is
- created on demand and then cached to speed up subsequent requests.'''
- return self._make_callback()
-
- def reset(self):
- ''' Forget any cached values. The next time :attr:`call` is accessed,
- all plugins are re-applied. '''
- self.__dict__.pop('call', None)
-
- def prepare(self):
- ''' Do all on-demand work immediately (useful for debugging).'''
- self.call
-
- @property
- def _context(self):
- depr('Switch to Plugin API v2 and access the Route object directly.')
- return dict(rule=self.rule, method=self.method, callback=self.callback,
- name=self.name, app=self.app, config=self.config,
- apply=self.plugins, skip=self.skiplist)
-
- def all_plugins(self):
- ''' Yield all Plugins affecting this route. '''
- unique = set()
- for p in reversed(self.app.plugins + self.plugins):
- if True in self.skiplist: break
- name = getattr(p, 'name', False)
- if name and (name in self.skiplist or name in unique): continue
- if p in self.skiplist or type(p) in self.skiplist: continue
- if name: unique.add(name)
- yield p
-
- def _make_callback(self):
- callback = self.callback
- for plugin in self.all_plugins():
- try:
- if hasattr(plugin, 'apply'):
- api = getattr(plugin, 'api', 1)
- context = self if api > 1 else self._context
- callback = plugin.apply(callback, context)
- else:
- callback = plugin(callback)
- except RouteReset: # Try again with changed configuration.
- return self._make_callback()
- if not callback is self.callback:
- try_update_wrapper(callback, self.callback)
- return callback
-
-
-
-
-
-
-###############################################################################
-# Application Object ###########################################################
-###############################################################################
-
-
-class Bottle(object):
- """ WSGI application """
-
- def __init__(self, catchall=True, autojson=True, config=None):
- """ Create a new bottle instance.
- You usually don't do that. Use `bottle.app.push()` instead.
- """
- self.routes = [] # List of installed :class:`Route` instances.
- self.router = Router() # Maps requests to :class:`Route` instances.
- self.plugins = [] # List of installed plugins.
-
- self.error_handler = {}
- #: If true, most exceptions are catched and returned as :exc:`HTTPError`
- self.config = ConfigDict(config or {})
- self.catchall = catchall
- #: An instance of :class:`HooksPlugin`. Empty by default.
- self.hooks = HooksPlugin()
- self.install(self.hooks)
- if autojson:
- self.install(JSONPlugin())
- self.install(TemplatePlugin())
-
- def mount(self, prefix, app, **options):
- ''' Mount an application (:class:`Bottle` or plain WSGI) to a specific
- URL prefix. Example::
-
- root_app.mount('/admin/', admin_app)
-
- :param prefix: path prefix or `mount-point`. If it ends in a slash,
- that slash is mandatory.
- :param app: an instance of :class:`Bottle` or a WSGI application.
-
- All other parameters are passed to the underlying :meth:`route` call.
- '''
- if isinstance(app, basestring):
- prefix, app = app, prefix
- depr('Parameter order of Bottle.mount() changed.') # 0.10
-
- parts = filter(None, prefix.split('/'))
- if not parts: raise ValueError('Empty path prefix.')
- path_depth = len(parts)
- options.setdefault('skip', True)
- options.setdefault('method', 'ANY')
-
- @self.route('/%s/:#.*#' % '/'.join(parts), **options)
- def mountpoint():
- try:
- request.path_shift(path_depth)
- rs = BaseResponse([], 200)
- def start_response(status, header):
- rs.status = status
- for name, value in header: rs.add_header(name, value)
- return rs.body.append
- rs.body = itertools.chain(rs.body, app(request.environ, start_response))
- return HTTPResponse(rs.body, rs.status, rs.headers)
- finally:
- request.path_shift(-path_depth)
-
- if not prefix.endswith('/'):
- self.route('/' + '/'.join(parts), callback=mountpoint, **options)
-
- def install(self, plugin):
- ''' Add a plugin to the list of plugins and prepare it for being
- applied to all routes of this application. A plugin may be a simple
- decorator or an object that implements the :class:`Plugin` API.
- '''
- if hasattr(plugin, 'setup'): plugin.setup(self)
- if not callable(plugin) and not hasattr(plugin, 'apply'):
- raise TypeError("Plugins must be callable or implement .apply()")
- self.plugins.append(plugin)
- self.reset()
- return plugin
-
- def uninstall(self, plugin):
- ''' Uninstall plugins. Pass an instance to remove a specific plugin, a type
- object to remove all plugins that match that type, a string to remove
- all plugins with a matching ``name`` attribute or ``True`` to remove all
- plugins. Return the list of removed plugins. '''
- removed, remove = [], plugin
- for i, plugin in list(enumerate(self.plugins))[::-1]:
- if remove is True or remove is plugin or remove is type(plugin) \
- or getattr(plugin, 'name', True) == remove:
- removed.append(plugin)
- del self.plugins[i]
- if hasattr(plugin, 'close'): plugin.close()
- if removed: self.reset()
- return removed
-
- def reset(self, route=None):
- ''' Reset all routes (force plugins to be re-applied) and clear all
- caches. If an ID or route object is given, only that specific route
- is affected. '''
- if route is None: routes = self.routes
- elif isinstance(route, Route): routes = [route]
- else: routes = [self.routes[route]]
- for route in routes: route.reset()
- if DEBUG:
- for route in routes: route.prepare()
- self.hooks.trigger('app_reset')
-
- def close(self):
- ''' Close the application and all installed plugins. '''
- for plugin in self.plugins:
- if hasattr(plugin, 'close'): plugin.close()
- self.stopped = True
-
- def match(self, environ):
- """ Search for a matching route and return a (:class:`Route` , urlargs)
- tuple. The second value is a dictionary with parameters extracted
- from the URL. Raise :exc:`HTTPError` (404/405) on a non-match."""
- return self.router.match(environ)
-
- def get_url(self, routename, **kargs):
- """ Return a string that matches a named route """
- scriptname = request.environ.get('SCRIPT_NAME', '').strip('/') + '/'
- location = self.router.build(routename, **kargs).lstrip('/')
- return urljoin(urljoin('/', scriptname), location)
-
- def route(self, path=None, method='GET', callback=None, name=None,
- apply=None, skip=None, **config):
- """ A decorator to bind a function to a request URL. Example::
-
- @app.route('/hello/:name')
- def hello(name):
- return 'Hello %s' % name
-
- The ``:name`` part is a wildcard. See :class:`Router` for syntax
- details.
-
- :param path: Request path or a list of paths to listen to. If no
- path is specified, it is automatically generated from the
- signature of the function.
- :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of
- methods to listen to. (default: `GET`)
- :param callback: An optional shortcut to avoid the decorator
- syntax. ``route(..., callback=func)`` equals ``route(...)(func)``
- :param name: The name for this route. (default: None)
- :param apply: A decorator or plugin or a list of plugins. These are
- applied to the route callback in addition to installed plugins.
- :param skip: A list of plugins, plugin classes or names. Matching
- plugins are not installed to this route. ``True`` skips all.
-
- Any additional keyword arguments are stored as route-specific
- configuration and passed to plugins (see :meth:`Plugin.apply`).
- """
- if callable(path): path, callback = None, path
- plugins = makelist(apply)
- skiplist = makelist(skip)
- def decorator(callback):
- # TODO: Documentation and tests
- if isinstance(callback, basestring): callback = load(callback)
- for rule in makelist(path) or yieldroutes(callback):
- for verb in makelist(method):
- verb = verb.upper()
- route = Route(self, rule, verb, callback, name=name,
- plugins=plugins, skiplist=skiplist, **config)
- self.routes.append(route)
- self.router.add(rule, verb, route, name=name)
- if DEBUG: route.prepare()
- return callback
- return decorator(callback) if callback else decorator
-
- def get(self, path=None, method='GET', **options):
- """ Equals :meth:`route`. """
- return self.route(path, method, **options)
-
- def post(self, path=None, method='POST', **options):
- """ Equals :meth:`route` with a ``POST`` method parameter. """
- return self.route(path, method, **options)
-
- def put(self, path=None, method='PUT', **options):
- """ Equals :meth:`route` with a ``PUT`` method parameter. """
- return self.route(path, method, **options)
-
- def delete(self, path=None, method='DELETE', **options):
- """ Equals :meth:`route` with a ``DELETE`` method parameter. """
- return self.route(path, method, **options)
-
- def error(self, code=500):
- """ Decorator: Register an output handler for a HTTP error code"""
- def wrapper(handler):
- self.error_handler[int(code)] = handler
- return handler
- return wrapper
-
- def hook(self, name):
- """ Return a decorator that attaches a callback to a hook. """
- def wrapper(func):
- self.hooks.add(name, func)
- return func
- return wrapper
-
- def handle(self, path, method='GET'):
- """ (deprecated) Execute the first matching route callback and return
- the result. :exc:`HTTPResponse` exceptions are catched and returned.
- If :attr:`Bottle.catchall` is true, other exceptions are catched as
- well and returned as :exc:`HTTPError` instances (500).
- """
- depr("This method will change semantics in 0.10. Try to avoid it.")
- if isinstance(path, dict):
- return self._handle(path)
- return self._handle({'PATH_INFO': path, 'REQUEST_METHOD': method.upper()})
-
- def _handle(self, environ):
- try:
- route, args = self.router.match(environ)
- environ['route.handle'] = environ['bottle.route'] = route
- environ['route.url_args'] = args
- return route.call(**args)
- except HTTPResponse, r:
- return r
- except RouteReset:
- route.reset()
- return self._handle(environ)
- except (KeyboardInterrupt, SystemExit, MemoryError):
- raise
- except Exception, e:
- if not self.catchall: raise
- stacktrace = format_exc(10)
- environ['wsgi.errors'].write(stacktrace)
- return HTTPError(500, "Internal Server Error", e, stacktrace)
-
- def _cast(self, out, request, response, peek=None):
- """ Try to convert the parameter into something WSGI compatible and set
- correct HTTP headers when possible.
- Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like,
- iterable of strings and iterable of unicodes
- """
-
- # Empty output is done here
- if not out:
- response['Content-Length'] = 0
- return []
- # Join lists of byte or unicode strings. Mixed lists are NOT supported
- if isinstance(out, (tuple, list))\
- and isinstance(out[0], (bytes, unicode)):
- out = out[0][0:0].join(out) # b'abc'[0:0] -> b''
- # Encode unicode strings
- if isinstance(out, unicode):
- out = out.encode(response.charset)
- # Byte Strings are just returned
- if isinstance(out, bytes):
- response['Content-Length'] = len(out)
- return [out]
- # HTTPError or HTTPException (recursive, because they may wrap anything)
- # TODO: Handle these explicitly in handle() or make them iterable.
- if isinstance(out, HTTPError):
- out.apply(response)
- out = self.error_handler.get(out.status, repr)(out)
- if isinstance(out, HTTPResponse):
- depr('Error handlers must not return :exc:`HTTPResponse`.') #0.9
- return self._cast(out, request, response)
- if isinstance(out, HTTPResponse):
- out.apply(response)
- return self._cast(out.output, request, response)
-
- # File-like objects.
- if hasattr(out, 'read'):
- if 'wsgi.file_wrapper' in request.environ:
- return request.environ['wsgi.file_wrapper'](out)
- elif hasattr(out, 'close') or not hasattr(out, '__iter__'):
- return WSGIFileWrapper(out)
-
- # Handle Iterables. We peek into them to detect their inner type.
- try:
- out = iter(out)
- first = out.next()
- while not first:
- first = out.next()
- except StopIteration:
- return self._cast('', request, response)
- except HTTPResponse, e:
- first = e
- except Exception, e:
- first = HTTPError(500, 'Unhandled exception', e, format_exc(10))
- if isinstance(e, (KeyboardInterrupt, SystemExit, MemoryError))\
- or not self.catchall:
- raise
- # These are the inner types allowed in iterator or generator objects.
- if isinstance(first, HTTPResponse):
- return self._cast(first, request, response)
- if isinstance(first, bytes):
- return itertools.chain([first], out)
- if isinstance(first, unicode):
- return itertools.imap(lambda x: x.encode(response.charset),
- itertools.chain([first], out))
- return self._cast(HTTPError(500, 'Unsupported response type: %s'\
- % type(first)), request, response)
-
- def wsgi(self, environ, start_response):
- """ The bottle WSGI-interface. """
- try:
- environ['bottle.app'] = self
- request.bind(environ)
- response.bind()
- out = self._cast(self._handle(environ), request, response)
- # rfc2616 section 4.3
- if response._status_code in (100, 101, 204, 304)\
- or request.method == 'HEAD':
- if hasattr(out, 'close'): out.close()
- out = []
- start_response(response._status_line, list(response.iter_headers()))
- return out
- except (KeyboardInterrupt, SystemExit, MemoryError):
- raise
- except Exception, e:
- if not self.catchall: raise
- err = '<h1>Critical error while processing request: %s</h1>' \
- % environ.get('PATH_INFO', '/')
- if DEBUG:
- err += '<h2>Error:</h2>\n<pre>%s</pre>\n' % repr(e)
- err += '<h2>Traceback:</h2>\n<pre>%s</pre>\n' % format_exc(10)
- environ['wsgi.errors'].write(err) #TODO: wsgi.error should not get html
- start_response('500 INTERNAL SERVER ERROR', [('Content-Type', 'text/html')])
- return [tob(err)]
-
- def __call__(self, environ, start_response):
- ''' Each instance of :class:'Bottle' is a WSGI application. '''
- return self.wsgi(environ, start_response)
-
-
-
-
-
-
-###############################################################################
-# HTTP and WSGI Tools ##########################################################
-###############################################################################
-
-
-class BaseRequest(DictMixin):
- """ A wrapper for WSGI environment dictionaries that adds a lot of
- convenient access methods and properties. Most of them are read-only."""
-
- #: Maximum size of memory buffer for :attr:`body` in bytes.
- MEMFILE_MAX = 102400
-
- def __init__(self, environ):
- """ Wrap a WSGI environ dictionary. """
- #: The wrapped WSGI environ dictionary. This is the only real attribute.
- #: All other attributes actually are read-only properties.
- self.environ = environ
- environ['bottle.request'] = self
-
- @property
- def path(self):
- ''' The value of ``PATH_INFO`` with exactly one prefixed slash (to fix
- broken clients and avoid the "empty path" edge case). '''
- return '/' + self.environ.get('PATH_INFO','').lstrip('/')
-
- @property
- def method(self):
- ''' The ``REQUEST_METHOD`` value as an uppercase string. '''
- return self.environ.get('REQUEST_METHOD', 'GET').upper()
-
- @DictProperty('environ', 'bottle.request.headers', read_only=True)
- def headers(self):
- ''' A :class:`WSGIHeaderDict` that provides case-insensitive access to
- HTTP request headers. '''
- return WSGIHeaderDict(self.environ)
-
- def get_header(self, name, default=None):
- ''' Return the value of a request header, or a given default value. '''
- return self.headers.get(name, default)
-
- @DictProperty('environ', 'bottle.request.cookies', read_only=True)
- def cookies(self):
- """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT
- decoded. Use :meth:`get_cookie` if you expect signed cookies. """
- cookies = SimpleCookie(self.environ.get('HTTP_COOKIE',''))
- return FormsDict((c.key, c.value) for c in cookies.itervalues())
-
- def get_cookie(self, key, default=None, secret=None):
- """ Return the content of a cookie. To read a `Signed Cookie`, the
- `secret` must match the one used to create the cookie (see
- :meth:`BaseResponse.set_cookie`). If anything goes wrong (missing
- cookie or wrong signature), return a default value. """
- value = self.cookies.get(key)
- if secret and value:
- dec = cookie_decode(value, secret) # (key, value) tuple or None
- return dec[1] if dec and dec[0] == key else default
- return value or default
-
- @DictProperty('environ', 'bottle.request.query', read_only=True)
- def query(self):
- ''' The :attr:`query_string` parsed into a :class:`FormsDict`. These
- values are sometimes called "URL arguments" or "GET parameters", but
- not to be confused with "URL wildcards" as they are provided by the
- :class:`Router`. '''
- data = parse_qs(self.query_string, keep_blank_values=True)
- get = self.environ['bottle.get'] = FormsDict()
- for key, values in data.iteritems():
- for value in values:
- get[key] = value
- return get
-
- @DictProperty('environ', 'bottle.request.forms', read_only=True)
- def forms(self):
- """ Form values parsed from an `url-encoded` or `multipart/form-data`
- encoded POST or PUT request body. The result is retuned as a
- :class:`FormsDict`. All keys and values are strings. File uploads
- are stored separately in :attr:`files`. """
- forms = FormsDict()
- for name, item in self.POST.iterallitems():
- if not hasattr(item, 'filename'):
- forms[name] = item
- return forms
-
- @DictProperty('environ', 'bottle.request.params', read_only=True)
- def params(self):
- """ A :class:`FormsDict` with the combined values of :attr:`query` and
- :attr:`forms`. File uploads are stored in :attr:`files`. """
- params = FormsDict()
- for key, value in self.query.iterallitems():
- params[key] = value
- for key, value in self.forms.iterallitems():
- params[key] = value
- return params
-
- @DictProperty('environ', 'bottle.request.files', read_only=True)
- def files(self):
- """ File uploads parsed from an `url-encoded` or `multipart/form-data`
- encoded POST or PUT request body. The values are instances of
- :class:`cgi.FieldStorage`. The most important attributes are:
-
- filename
- The filename, if specified; otherwise None; this is the client
- side filename, *not* the file name on which it is stored (that's
- a temporary file you don't deal with)
- file
- The file(-like) object from which you can read the data.
- value
- The value as a *string*; for file uploads, this transparently
- reads the file every time you request the value. Do not do this
- on big files.
- """
- files = FormsDict()
- for name, item in self.POST.iterallitems():
- if hasattr(item, 'filename'):
- files[name] = item
- return files
-
- @DictProperty('environ', 'bottle.request.json', read_only=True)
- def json(self):
- ''' If the ``Content-Type`` header is ``application/json``, this
- property holds the parsed content of the request body. Only requests
- smaller than :attr:`MEMFILE_MAX` are processed to avoid memory
- exhaustion. '''
- if 'application/json' in self.environ.get('CONTENT_TYPE', '') \
- and 0 < self.content_length < self.MEMFILE_MAX:
- return json_loads(self.body.read(self.MEMFILE_MAX))
- return None
-
- @DictProperty('environ', 'bottle.request.body', read_only=True)
- def _body(self):
- maxread = max(0, self.content_length)
- stream = self.environ['wsgi.input']
- body = BytesIO() if maxread < self.MEMFILE_MAX else TemporaryFile(mode='w+b')
- while maxread > 0:
- part = stream.read(min(maxread, self.MEMFILE_MAX))
- if not part: break
- body.write(part)
- maxread -= len(part)
- self.environ['wsgi.input'] = body
- body.seek(0)
- return body
-
- @property
- def body(self):
- """ The HTTP request body as a seek-able file-like object. Depending on
- :attr:`MEMFILE_MAX`, this is either a temporary file or a
- :class:`io.BytesIO` instance. Accessing this property for the first
- time reads and replaces the ``wsgi.input`` environ variable.
- Subsequent accesses just do a `seek(0)` on the file object. """
- self._body.seek(0)
- return self._body
-
- #: An alias for :attr:`query`.
- GET = query
-
- @DictProperty('environ', 'bottle.request.post', read_only=True)
- def POST(self):
- """ The values of :attr:`forms` and :attr:`files` combined into a single
- :class:`FormsDict`. Values are either strings (form values) or
- instances of :class:`cgi.FieldStorage` (file uploads).
- """
- post = FormsDict()
- safe_env = {'QUERY_STRING':''} # Build a safe environment for cgi
- for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'):
- if key in self.environ: safe_env[key] = self.environ[key]
- if NCTextIOWrapper:
- fb = NCTextIOWrapper(self.body, encoding='ISO-8859-1', newline='\n')
- else:
- fb = self.body
- data = cgi.FieldStorage(fp=fb, environ=safe_env, keep_blank_values=True)
- for item in data.list or []:
- post[item.name] = item if item.filename else item.value
- return post
-
- @property
- def COOKIES(self):
- ''' Alias for :attr:`cookies` (deprecated). '''
- depr('BaseRequest.COOKIES was renamed to BaseRequest.cookies (lowercase).')
- return self.cookies
-
- @property
- def url(self):
- """ The full request URI including hostname and scheme. If your app
- lives behind a reverse proxy or load balancer and you get confusing
- results, make sure that the ``X-Forwarded-Host`` header is set
- correctly. """
- return self.urlparts.geturl()
-
- @DictProperty('environ', 'bottle.request.urlparts', read_only=True)
- def urlparts(self):
- ''' The :attr:`url` string as an :class:`urlparse.SplitResult` tuple.
- The tuple contains (scheme, host, path, query_string and fragment),
- but the fragment is always empty because it is not visible to the
- server. '''
- env = self.environ
- http = env.get('wsgi.url_scheme', 'http')
- host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST')
- if not host:
- # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients.
- host = env.get('SERVER_NAME', '127.0.0.1')
- port = env.get('SERVER_PORT')
- if port and port != ('80' if http == 'http' else '443'):
- host += ':' + port
- path = urlquote(self.fullpath)
- return UrlSplitResult(http, host, path, env.get('QUERY_STRING'), '')
-
- @property
- def fullpath(self):
- """ Request path including :attr:`script_name` (if present). """
- return urljoin(self.script_name, self.path.lstrip('/'))
-
- @property
- def query_string(self):
- """ The raw :attr:`query` part of the URL (everything in between ``?``
- and ``#``) as a string. """
- return self.environ.get('QUERY_STRING', '')
-
- @property
- def script_name(self):
- ''' The initial portion of the URL's `path` that was removed by a higher
- level (server or routing middleware) before the application was
- called. This script path is returned with leading and tailing
- slashes. '''
- script_name = self.environ.get('SCRIPT_NAME', '').strip('/')
- return '/' + script_name + '/' if script_name else '/'
-
- def path_shift(self, shift=1):
- ''' Shift path segments from :attr:`path` to :attr:`script_name` and
- vice versa.
-
- :param shift: The number of path segments to shift. May be negative
- to change the shift direction. (default: 1)
- '''
- script = self.environ.get('SCRIPT_NAME','/')
- self['SCRIPT_NAME'], self['PATH_INFO'] = path_shift(script, self.path, shift)
-
- @property
- def content_length(self):
- ''' The request body length as an integer. The client is responsible to
- set this header. Otherwise, the real length of the body is unknown
- and -1 is returned. In this case, :attr:`body` will be empty. '''
- return int(self.environ.get('CONTENT_LENGTH') or -1)
-
- @property
- def is_xhr(self):
- ''' True if the request was triggered by a XMLHttpRequest. This only
- works with JavaScript libraries that support the `X-Requested-With`
- header (most of the popular libraries do). '''
- requested_with = self.environ.get('HTTP_X_REQUESTED_WITH','')
- return requested_with.lower() == 'xmlhttprequest'
-
- @property
- def is_ajax(self):
- ''' Alias for :attr:`is_xhr`. "Ajax" is not the right term. '''
- return self.is_xhr
-
- @property
- def auth(self):
- """ HTTP authentication data as a (user, password) tuple. This
- implementation currently supports basic (not digest) authentication
- only. If the authentication happened at a higher level (e.g. in the
- front web-server or a middleware), the password field is None, but
- the user field is looked up from the ``REMOTE_USER`` environ
- variable. On any errors, None is returned. """
- basic = parse_auth(self.environ.get('HTTP_AUTHORIZATION',''))
- if basic: return basic
- ruser = self.environ.get('REMOTE_USER')
- if ruser: return (ruser, None)
- return None
-
- @property
- def remote_route(self):
- """ A list of all IPs that were involved in this request, starting with
- the client IP and followed by zero or more proxies. This does only
- work if all proxies support the ```X-Forwarded-For`` header. Note
- that this information can be forged by malicious clients. """
- proxy = self.environ.get('HTTP_X_FORWARDED_FOR')
- if proxy: return [ip.strip() for ip in proxy.split(',')]
- remote = self.environ.get('REMOTE_ADDR')
- return [remote] if remote else []
-
- @property
- def remote_addr(self):
- """ The client IP as a string. Note that this information can be forged
- by malicious clients. """
- route = self.remote_route
- return route[0] if route else None
-
- def copy(self):
- """ Return a new :class:`Request` with a shallow :attr:`environ` copy. """
- return Request(self.environ.copy())
-
- def __getitem__(self, key): return self.environ[key]
- def __delitem__(self, key): self[key] = ""; del(self.environ[key])
- def __iter__(self): return iter(self.environ)
- def __len__(self): return len(self.environ)
- def keys(self): return self.environ.keys()
- def __setitem__(self, key, value):
- """ Change an environ value and clear all caches that depend on it. """
-
- if self.environ.get('bottle.request.readonly'):
- raise KeyError('The environ dictionary is read-only.')
-
- self.environ[key] = value
- todelete = ()
-
- if key == 'wsgi.input':
- todelete = ('body', 'forms', 'files', 'params', 'post', 'json')
- elif key == 'QUERY_STRING':
- todelete = ('query', 'params')
- elif key.startswith('HTTP_'):
- todelete = ('headers', 'cookies')
-
- for key in todelete:
- self.environ.pop('bottle.request.'+key, None)
-
- def __repr__(self):
- return '<%s: %s %s>' % (self.__class__.__name__, self.method, self.url)
-
-def _hkey(s):
- return s.title().replace('_','-')
-
-
-class HeaderProperty(object):
- def __init__(self, name, reader=None, writer=str, default=''):
- self.name, self.reader, self.writer, self.default = name, reader, writer, default
- self.__doc__ = 'Current value of the %r header.' % name.title()
-
- def __get__(self, obj, cls):
- if obj is None: return self
- value = obj.headers.get(self.name)
- return self.reader(value) if (value and self.reader) else (value or self.default)
-
- def __set__(self, obj, value):
- if self.writer: value = self.writer(value)
- obj.headers[self.name] = value
-
- def __delete__(self, obj):
- if self.name in obj.headers:
- del obj.headers[self.name]
-
-
-class BaseResponse(object):
- """ Storage class for a response body as well as headers and cookies.
-
- This class does support dict-like case-insensitive item-access to
- headers, but is NOT a dict. Most notably, iterating over a response
- yields parts of the body and not the headers.
- """
-
- default_status = 200
- default_content_type = 'text/html; charset=UTF-8'
-
- # Header blacklist for specific response codes
- # (rfc2616 section 10.2.3 and 10.3.5)
- bad_headers = {
- 204: set(('Content-Type',)),
- 304: set(('Allow', 'Content-Encoding', 'Content-Language',
- 'Content-Length', 'Content-Range', 'Content-Type',
- 'Content-Md5', 'Last-Modified'))}
-
- def __init__(self, body='', status=None, **headers):
- self._status_line = None
- self._status_code = None
- self.body = body
- self._cookies = None
- self._headers = {'Content-Type': [self.default_content_type]}
- self.status = status or self.default_status
- if headers:
- for name, value in headers.items():
- self[name] = value
-
- def copy(self):
- ''' Returns a copy of self. '''
- copy = Response()
- copy.status = self.status
- copy._headers = dict((k, v[:]) for (k, v) in self._headers.items())
- return copy
-
- def __iter__(self):
- return iter(self.body)
-
- def close(self):
- if hasattr(self.body, 'close'):
- self.body.close()
-
- @property
- def status_line(self):
- ''' The HTTP status line as a string (e.g. ``404 Not Found``).'''
- return self._status_line
-
- @property
- def status_code(self):
- ''' The HTTP status code as an integer (e.g. 404).'''
- return self._status_code
-
- def _set_status(self, status):
- if isinstance(status, int):
- code, status = status, _HTTP_STATUS_LINES.get(status)
- elif ' ' in status:
- status = status.strip()
- code = int(status.split()[0])
- else:
- raise ValueError('String status line without a reason phrase.')
- if not 100 <= code <= 999: raise ValueError('Status code out of range.')
- self._status_code = code
- self._status_line = status or ('%d Unknown' % code)
-
- def _get_status(self):
- depr('BaseReuqest.status will change to return a string in 0.11. Use'\
- ' status_line and status_code to make sure.') #0.10
- return self._status_code
-
- status = property(_get_status, _set_status, None,
- ''' A writeable property to change the HTTP response status. It accepts
- either a numeric code (100-999) or a string with a custom reason
- phrase (e.g. "404 Brain not found"). Both :data:`status_line` and
- :data:`status_code` are updates accordingly. The return value is
- always a numeric code. ''')
- del _get_status, _set_status
-
- @property
- def headers(self):
- ''' An instance of :class:`HeaderDict`, a case-insensitive dict-like
- view on the response headers. '''
- self.__dict__['headers'] = hdict = HeaderDict()
- hdict.dict = self._headers
- return hdict
-
- def __contains__(self, name): return _hkey(name) in self._headers
- def __delitem__(self, name): del self._headers[_hkey(name)]
- def __getitem__(self, name): return self._headers[_hkey(name)][-1]
- def __setitem__(self, name, value): self._headers[_hkey(name)] = [str(value)]
-
- def get_header(self, name, default=None):
- ''' Return the value of a previously defined header. If there is no
- header with that name, return a default value. '''
- return self._headers.get(_hkey(name), [default])[-1]
-
- def set_header(self, name, value, append=False):
- ''' Create a new response header, replacing any previously defined
- headers with the same name. '''
- if append:
- self.add_header(name, value)
- else:
- self._headers[_hkey(name)] = [str(value)]
-
- def add_header(self, name, value):
- ''' Add an additional response header, not removing duplicates. '''
- self._headers.setdefault(_hkey(name), []).append(str(value))
-
- def iter_headers(self):
- ''' Yield (header, value) tuples, skipping headers that are not
- allowed with the current response status code. '''
- headers = self._headers.iteritems()
- bad_headers = self.bad_headers.get(self.status_code)
- if bad_headers:
- headers = [h for h in headers if h[0] not in bad_headers]
- for name, values in headers:
- for value in values:
- yield name, value
- if self._cookies:
- for c in self._cookies.values():
- yield 'Set-Cookie', c.OutputString()
-
- def wsgiheader(self):
- depr('The wsgiheader method is deprecated. See headerlist.') #0.10
- return self.headerlist
-
- @property
- def headerlist(self):
- ''' WSGI conform list of (header, value) tuples. '''
- return list(self.iter_headers())
-
- content_type = HeaderProperty('Content-Type')
- content_length = HeaderProperty('Content-Length', reader=int)
-
- @property
- def charset(self):
- """ Return the charset specified in the content-type header (default: utf8). """
- if 'charset=' in self.content_type:
- return self.content_type.split('charset=')[-1].split(';')[0].strip()
- return 'UTF-8'
-
- @property
- def COOKIES(self):
- """ A dict-like SimpleCookie instance. This should not be used directly.
- See :meth:`set_cookie`. """
- depr('The COOKIES dict is deprecated. Use `set_cookie()` instead.') # 0.10
- if not self._cookies:
- self._cookies = SimpleCookie()
- return self._cookies
-
- def set_cookie(self, name, value, secret=None, **options):
- ''' Create a new cookie or replace an old one. If the `secret` parameter is
- set, create a `Signed Cookie` (described below).
-
- :param name: the name of the cookie.
- :param value: the value of the cookie.
- :param secret: a signature key required for signed cookies.
-
- Additionally, this method accepts all RFC 2109 attributes that are
- supported by :class:`cookie.Morsel`, including:
-
- :param max_age: maximum age in seconds. (default: None)
- :param expires: a datetime object or UNIX timestamp. (default: None)
- :param domain: the domain that is allowed to read the cookie.
- (default: current domain)
- :param path: limits the cookie to a given path (default: current path)
- :param secure: limit the cookie to HTTPS connections (default: off).
- :param httponly: prevents client-side javascript to read this cookie
- (default: off, requires Python 2.6 or newer).
-
- If neither `expires` nor `max_age` is set (default), the cookie will
- expire at the end of the browser session (as soon as the browser
- window is closed).
-
- Signed cookies may store any pickle-able object and are
- cryptographically signed to prevent manipulation. Keep in mind that
- cookies are limited to 4kb in most browsers.
-
- Warning: Signed cookies are not encrypted (the client can still see
- the content) and not copy-protected (the client can restore an old
- cookie). The main intention is to make pickling and unpickling
- save, not to store secret information at client side.
- '''
- if not self._cookies:
- self._cookies = SimpleCookie()
-
- if secret:
- value = touni(cookie_encode((name, value), secret))
- elif not isinstance(value, basestring):
- raise TypeError('Secret key missing for non-string Cookie.')
-
- if len(value) > 4096: raise ValueError('Cookie value to long.')
- self._cookies[name] = value
-
- for key, value in options.iteritems():
- if key == 'max_age':
- if isinstance(value, timedelta):
- value = value.seconds + value.days * 24 * 3600
- if key == 'expires':
- if isinstance(value, (datedate, datetime)):
- value = value.timetuple()
- elif isinstance(value, (int, float)):
- value = time.gmtime(value)
- value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value)
- self._cookies[name][key.replace('_', '-')] = value
-
- def delete_cookie(self, key, **kwargs):
- ''' Delete a cookie. Be sure to use the same `domain` and `path`
- settings as used to create the cookie. '''
- kwargs['max_age'] = -1
- kwargs['expires'] = 0
- self.set_cookie(key, '', **kwargs)
-
- def __repr__(self):
- out = ''
- for name, value in self.headerlist:
- out += '%s: %s\n' % (name.title(), value.strip())
- return out
-
-
-class LocalRequest(BaseRequest, threading.local):
- ''' A thread-local subclass of :class:`BaseRequest`. '''
- def __init__(self): pass
- bind = BaseRequest.__init__
-
-
-class LocalResponse(BaseResponse, threading.local):
- ''' A thread-local subclass of :class:`BaseResponse`. '''
- bind = BaseResponse.__init__
-
-Response = LocalResponse # BC 0.9
-Request = LocalRequest # BC 0.9
-
-
-
-
-
-
-###############################################################################
-# Plugins ######################################################################
-###############################################################################
-
-class PluginError(BottleException): pass
-
-class JSONPlugin(object):
- name = 'json'
- api = 2
-
- def __init__(self, json_dumps=json_dumps):
- self.json_dumps = json_dumps
-
- def apply(self, callback, context):
- dumps = self.json_dumps
- if not dumps: return callback
- def wrapper(*a, **ka):
- rv = callback(*a, **ka)
- if isinstance(rv, dict):
- #Attempt to serialize, raises exception on failure
- json_response = dumps(rv)
- #Set content type only if serialization succesful
- response.content_type = 'application/json'
- return json_response
- return rv
- return wrapper
-
-
-class HooksPlugin(object):
- name = 'hooks'
- api = 2
-
- _names = 'before_request', 'after_request', 'app_reset'
-
- def __init__(self):
- self.hooks = dict((name, []) for name in self._names)
- self.app = None
-
- def _empty(self):
- return not (self.hooks['before_request'] or self.hooks['after_request'])
-
- def setup(self, app):
- self.app = app
-
- def add(self, name, func):
- ''' Attach a callback to a hook. '''
- was_empty = self._empty()
- self.hooks.setdefault(name, []).append(func)
- if self.app and was_empty and not self._empty(): self.app.reset()
-
- def remove(self, name, func):
- ''' Remove a callback from a hook. '''
- was_empty = self._empty()
- if name in self.hooks and func in self.hooks[name]:
- self.hooks[name].remove(func)
- if self.app and not was_empty and self._empty(): self.app.reset()
-
- def trigger(self, name, *a, **ka):
- ''' Trigger a hook and return a list of results. '''
- hooks = self.hooks[name]
- if ka.pop('reversed', False): hooks = hooks[::-1]
- return [hook(*a, **ka) for hook in hooks]
-
- def apply(self, callback, context):
- if self._empty(): return callback
- def wrapper(*a, **ka):
- self.trigger('before_request')
- rv = callback(*a, **ka)
- self.trigger('after_request', reversed=True)
- return rv
- return wrapper
-
-
-class TemplatePlugin(object):
- ''' This plugin applies the :func:`view` decorator to all routes with a
- `template` config parameter. If the parameter is a tuple, the second
- element must be a dict with additional options (e.g. `template_engine`)
- or default variables for the template. '''
- name = 'template'
- api = 2
-
- def apply(self, callback, route):
- conf = route.config.get('template')
- if isinstance(conf, (tuple, list)) and len(conf) == 2:
- return view(conf[0], **conf[1])(callback)
- elif isinstance(conf, str) and 'template_opts' in route.config:
- depr('The `template_opts` parameter is deprecated.') #0.9
- return view(conf, **route.config['template_opts'])(callback)
- elif isinstance(conf, str):
- return view(conf)(callback)
- else:
- return callback
-
-
-#: Not a plugin, but part of the plugin API. TODO: Find a better place.
-class _ImportRedirect(object):
- def __init__(self, name, impmask):
- ''' Create a virtual package that redirects imports (see PEP 302). '''
- self.name = name
- self.impmask = impmask
- self.module = sys.modules.setdefault(name, imp.new_module(name))
- self.module.__dict__.update({'__file__': __file__, '__path__': [],
- '__all__': [], '__loader__': self})
- sys.meta_path.append(self)
-
- def find_module(self, fullname, path=None):
- if '.' not in fullname: return
- packname, modname = fullname.rsplit('.', 1)
- if packname != self.name: return
- return self
-
- def load_module(self, fullname):
- if fullname in sys.modules: return sys.modules[fullname]
- packname, modname = fullname.rsplit('.', 1)
- realname = self.impmask % modname
- __import__(realname)
- module = sys.modules[fullname] = sys.modules[realname]
- setattr(self.module, modname, module)
- module.__loader__ = self
- return module
-
-
-
-
-
-
-###############################################################################
-# Common Utilities #############################################################
-###############################################################################
-
-
-class MultiDict(DictMixin):
- """ This dict stores multiple values per key, but behaves exactly like a
- normal dict in that it returns only the newest value for any given key.
- There are special methods available to access the full list of values.
- """
-
- def __init__(self, *a, **k):
- self.dict = dict((k, [v]) for k, v in dict(*a, **k).iteritems())
- def __len__(self): return len(self.dict)
- def __iter__(self): return iter(self.dict)
- def __contains__(self, key): return key in self.dict
- def __delitem__(self, key): del self.dict[key]
- def __getitem__(self, key): return self.dict[key][-1]
- def __setitem__(self, key, value): self.append(key, value)
- def iterkeys(self): return self.dict.iterkeys()
- def itervalues(self): return (v[-1] for v in self.dict.itervalues())
- def iteritems(self): return ((k, v[-1]) for (k, v) in self.dict.iteritems())
- def iterallitems(self):
- for key, values in self.dict.iteritems():
- for value in values:
- yield key, value
-
- # 2to3 is not able to fix these automatically.
- keys = iterkeys if py3k else lambda self: list(self.iterkeys())
- values = itervalues if py3k else lambda self: list(self.itervalues())
- items = iteritems if py3k else lambda self: list(self.iteritems())
- allitems = iterallitems if py3k else lambda self: list(self.iterallitems())
-
- def get(self, key, default=None, index=-1, type=None):
- ''' Return the most recent value for a key.
-
- :param default: The default value to be returned if the key is not
- present or the type conversion fails.
- :param index: An index for the list of available values.
- :param type: If defined, this callable is used to cast the value
- into a specific type. Exception are suppressed and result in
- the default value to be returned.
- '''
- try:
- val = self.dict[key][index]
- return type(val) if type else val
- except Exception, e:
- pass
- return default
-
- def append(self, key, value):
- ''' Add a new value to the list of values for this key. '''
- self.dict.setdefault(key, []).append(value)
-
- def replace(self, key, value):
- ''' Replace the list of values with a single value. '''
- self.dict[key] = [value]
-
- def getall(self, key):
- ''' Return a (possibly empty) list of values for a key. '''
- return self.dict.get(key) or []
-
- #: Aliases for WTForms to mimic other multi-dict APIs (Django)
- getone = get
- getlist = getall
-
-
-
-class FormsDict(MultiDict):
- ''' This :class:`MultiDict` subclass is used to store request form data.
- Additionally to the normal dict-like item access methods (which return
- unmodified data as native strings), this container also supports
- attribute-like access to its values. Attribues are automatiically de- or
- recoded to match :attr:`input_encoding` (default: 'utf8'). Missing
- attributes default to an empty string. '''
-
- #: Encoding used for attribute values.
- input_encoding = 'utf8'
-
- def getunicode(self, name, default=None, encoding=None):
- value, enc = self.get(name, default), encoding or self.input_encoding
- try:
- if isinstance(value, bytes): # Python 2 WSGI
- return value.decode(enc)
- elif isinstance(value, unicode): # Python 3 WSGI
- return value.encode('latin1').decode(enc)
- return value
- except UnicodeError, e:
- return default
-
- def __getattr__(self, name): return self.getunicode(name, default=u'')
-
-
-class HeaderDict(MultiDict):
- """ A case-insensitive version of :class:`MultiDict` that defaults to
- replace the old value instead of appending it. """
-
- def __init__(self, *a, **ka):
- self.dict = {}
- if a or ka: self.update(*a, **ka)
-
- def __contains__(self, key): return _hkey(key) in self.dict
- def __delitem__(self, key): del self.dict[_hkey(key)]
- def __getitem__(self, key): return self.dict[_hkey(key)][-1]
- def __setitem__(self, key, value): self.dict[_hkey(key)] = [str(value)]
- def append(self, key, value):
- self.dict.setdefault(_hkey(key), []).append(str(value))
- def replace(self, key, value): self.dict[_hkey(key)] = [str(value)]
- def getall(self, key): return self.dict.get(_hkey(key)) or []
- def get(self, key, default=None, index=-1):
- return MultiDict.get(self, _hkey(key), default, index)
- def filter(self, names):
- for name in map(_hkey, names):
- if name in self.dict:
- del self.dict[name]
-
-
-class WSGIHeaderDict(DictMixin):
- ''' This dict-like class wraps a WSGI environ dict and provides convenient
- access to HTTP_* fields. Keys and values are native strings
- (2.x bytes or 3.x unicode) and keys are case-insensitive. If the WSGI
- environment contains non-native string values, these are de- or encoded
- using a lossless 'latin1' character set.
-
- The API will remain stable even on changes to the relevant PEPs.
- Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one
- that uses non-native strings.)
- '''
- #: List of keys that do not have a 'HTTP_' prefix.
- cgikeys = ('CONTENT_TYPE', 'CONTENT_LENGTH')
-
- def __init__(self, environ):
- self.environ = environ
-
- def _ekey(self, key):
- ''' Translate header field name to CGI/WSGI environ key. '''
- key = key.replace('-','_').upper()
- if key in self.cgikeys:
- return key
- return 'HTTP_' + key
-
- def raw(self, key, default=None):
- ''' Return the header value as is (may be bytes or unicode). '''
- return self.environ.get(self._ekey(key), default)
-
- def __getitem__(self, key):
- return tonat(self.environ[self._ekey(key)], 'latin1')
-
- def __setitem__(self, key, value):
- raise TypeError("%s is read-only." % self.__class__)
-
- def __delitem__(self, key):
- raise TypeError("%s is read-only." % self.__class__)
-
- def __iter__(self):
- for key in self.environ:
- if key[:5] == 'HTTP_':
- yield key[5:].replace('_', '-').title()
- elif key in self.cgikeys:
- yield key.replace('_', '-').title()
-
- def keys(self): return [x for x in self]
- def __len__(self): return len(self.keys())
- def __contains__(self, key): return self._ekey(key) in self.environ
-
-
-class ConfigDict(dict):
- ''' A dict-subclass with some extras: You can access keys like attributes.
- Uppercase attributes create new ConfigDicts and act as name-spaces.
- Other missing attributes return None. Calling a ConfigDict updates its
- values and returns itself.
-
- >>> cfg = ConfigDict()
- >>> cfg.Namespace.value = 5
- >>> cfg.OtherNamespace(a=1, b=2)
- >>> cfg
- {'Namespace': {'value': 5}, 'OtherNamespace': {'a': 1, 'b': 2}}
- '''
-
- def __getattr__(self, key):
- if key not in self and key[0].isupper():
- self[key] = ConfigDict()
- return self.get(key)
-
- def __setattr__(self, key, value):
- if hasattr(dict, key):
- raise AttributeError('Read-only attribute.')
- if key in self and self[key] and isinstance(self[key], ConfigDict):
- raise AttributeError('Non-empty namespace attribute.')
- self[key] = value
-
- def __delattr__(self, key):
- if key in self: del self[key]
-
- def __call__(self, *a, **ka):
- for key, value in dict(*a, **ka).iteritems(): setattr(self, key, value)
- return self
-
-
-class AppStack(list):
- """ A stack-like list. Calling it returns the head of the stack. """
-
- def __call__(self):
- """ Return the current default application. """
- return self[-1]
-
- def push(self, value=None):
- """ Add a new :class:`Bottle` instance to the stack """
- if not isinstance(value, Bottle):
- value = Bottle()
- self.append(value)
- return value
-
-
-class WSGIFileWrapper(object):
-
- def __init__(self, fp, buffer_size=1024*64):
- self.fp, self.buffer_size = fp, buffer_size
- for attr in ('fileno', 'close', 'read', 'readlines'):
- if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr))
-
- def __iter__(self):
- read, buff = self.fp.read, self.buffer_size
- while True:
- part = read(buff)
- if not part: break
- yield part
-
-
-
-
-
-
-###############################################################################
-# Application Helper ###########################################################
-###############################################################################
-
-
-def abort(code=500, text='Unknown Error: Application stopped.'):
- """ Aborts execution and causes a HTTP error. """
- raise HTTPError(code, text)
-
-
-def redirect(url, code=None):
- """ Aborts execution and causes a 303 or 302 redirect, depending on
- the HTTP protocol version. """
- if code is None:
- code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302
- location = urljoin(request.url, url)
- raise HTTPResponse("", status=code, header=dict(Location=location))
-
-
-def static_file(filename, root, mimetype='auto', download=False):
- """ Open a file in a safe way and return :exc:`HTTPResponse` with status
- code 200, 305, 401 or 404. Set Content-Type, Content-Encoding,
- Content-Length and Last-Modified header. Obey If-Modified-Since header
- and HEAD requests.
- """
- root = os.path.abspath(root) + os.sep
- filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))
- header = dict()
-
- if not filename.startswith(root):
- return HTTPError(403, "Access denied.")
- if not os.path.exists(filename) or not os.path.isfile(filename):
- return HTTPError(404, "File does not exist.")
- if not os.access(filename, os.R_OK):
- return HTTPError(403, "You do not have permission to access this file.")
-
- if mimetype == 'auto':
- mimetype, encoding = mimetypes.guess_type(filename)
- if mimetype: header['Content-Type'] = mimetype
- if encoding: header['Content-Encoding'] = encoding
- elif mimetype:
- header['Content-Type'] = mimetype
-
- if download:
- download = os.path.basename(filename if download == True else download)
- header['Content-Disposition'] = 'attachment; filename="%s"' % download
-
- stats = os.stat(filename)
- header['Content-Length'] = stats.st_size
- lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
- header['Last-Modified'] = lm
-
- ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
- if ims:
- ims = parse_date(ims.split(";")[0].strip())
- if ims is not None and ims >= int(stats.st_mtime):
- header['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
- return HTTPResponse(status=304, header=header)
-
- body = '' if request.method == 'HEAD' else open(filename, 'rb')
- return HTTPResponse(body, header=header)
-
-
-
-
-
-
-###############################################################################
-# HTTP Utilities and MISC (TODO) ###############################################
-###############################################################################
-
-
-def debug(mode=True):
- """ Change the debug level.
- There is only one debug level supported at the moment."""
- global DEBUG
- DEBUG = bool(mode)
-
-
-def parse_date(ims):
- """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """
- try:
- ts = email.utils.parsedate_tz(ims)
- return time.mktime(ts[:8] + (0,)) - (ts[9] or 0) - time.timezone
- except (TypeError, ValueError, IndexError, OverflowError):
- return None
-
-
-def parse_auth(header):
- """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None"""
- try:
- method, data = header.split(None, 1)
- if method.lower() == 'basic':
- #TODO: Add 2to3 save base64[encode/decode] functions.
- user, pwd = touni(base64.b64decode(tob(data))).split(':',1)
- return user, pwd
- except (KeyError, ValueError):
- return None
-
-
-def _lscmp(a, b):
- ''' Compares two strings in a cryptographically save way:
- Runtime is not affected by length of common prefix. '''
- return not sum(0 if x==y else 1 for x, y in zip(a, b)) and len(a) == len(b)
-
-
-def cookie_encode(data, key):
- ''' Encode and sign a pickle-able object. Return a (byte) string '''
- msg = base64.b64encode(pickle.dumps(data, -1))
- sig = base64.b64encode(hmac.new(tob(key), msg).digest())
- return tob('!') + sig + tob('?') + msg
-
-
-def cookie_decode(data, key):
- ''' Verify and decode an encoded string. Return an object or None.'''
- data = tob(data)
- if cookie_is_encoded(data):
- sig, msg = data.split(tob('?'), 1)
- if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())):
- return pickle.loads(base64.b64decode(msg))
- return None
-
-
-def cookie_is_encoded(data):
- ''' Return True if the argument looks like a encoded cookie.'''
- return bool(data.startswith(tob('!')) and tob('?') in data)
-
-
-def html_escape(string):
- ''' Escape HTML special characters ``&<>`` and quotes ``'"``. '''
- return string.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;')\
- .replace('"','&quot;').replace("'",'&#039;')
-
-
-def html_quote(string):
- ''' Escape and quote a string to be used as an HTTP attribute.'''
- return '"%s"' % html_escape(string).replace('\n','%#10;')\
- .replace('\r','&#13;').replace('\t','&#9;')
-
-
-def yieldroutes(func):
- """ Return a generator for routes that match the signature (name, args)
- of the func parameter. This may yield more than one route if the function
- takes optional keyword arguments. The output is best described by example::
-
- a() -> '/a'
- b(x, y) -> '/b/:x/:y'
- c(x, y=5) -> '/c/:x' and '/c/:x/:y'
- d(x=5, y=6) -> '/d' and '/d/:x' and '/d/:x/:y'
- """
- import inspect # Expensive module. Only import if necessary.
- path = '/' + func.__name__.replace('__','/').lstrip('/')
- spec = inspect.getargspec(func)
- argc = len(spec[0]) - len(spec[3] or [])
- path += ('/:%s' * argc) % tuple(spec[0][:argc])
- yield path
- for arg in spec[0][argc:]:
- path += '/:%s' % arg
- yield path
-
-
-def path_shift(script_name, path_info, shift=1):
- ''' Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa.
-
- :return: The modified paths.
- :param script_name: The SCRIPT_NAME path.
- :param script_name: The PATH_INFO path.
- :param shift: The number of path fragments to shift. May be negative to
- change the shift direction. (default: 1)
- '''
- if shift == 0: return script_name, path_info
- pathlist = path_info.strip('/').split('/')
- scriptlist = script_name.strip('/').split('/')
- if pathlist and pathlist[0] == '': pathlist = []
- if scriptlist and scriptlist[0] == '': scriptlist = []
- if shift > 0 and shift <= len(pathlist):
- moved = pathlist[:shift]
- scriptlist = scriptlist + moved
- pathlist = pathlist[shift:]
- elif shift < 0 and shift >= -len(scriptlist):
- moved = scriptlist[shift:]
- pathlist = moved + pathlist
- scriptlist = scriptlist[:shift]
- else:
- empty = 'SCRIPT_NAME' if shift < 0 else 'PATH_INFO'
- raise AssertionError("Cannot shift. Nothing left from %s" % empty)
- new_script_name = '/' + '/'.join(scriptlist)
- new_path_info = '/' + '/'.join(pathlist)
- if path_info.endswith('/') and pathlist: new_path_info += '/'
- return new_script_name, new_path_info
-
-
-def validate(**vkargs):
- """
- Validates and manipulates keyword arguments by user defined callables.
- Handles ValueError and missing arguments by raising HTTPError(403).
- """
- depr('Use route wildcard filters instead.')
- def decorator(func):
- @functools.wraps(func)
- def wrapper(*args, **kargs):
- for key, value in vkargs.iteritems():
- if key not in kargs:
- abort(403, 'Missing parameter: %s' % key)
- try:
- kargs[key] = value(kargs[key])
- except ValueError:
- abort(403, 'Wrong parameter format for: %s' % key)
- return func(*args, **kargs)
- return wrapper
- return decorator
-
-
-def auth_basic(check, realm="private", text="Access denied"):
- ''' Callback decorator to require HTTP auth (basic).
- TODO: Add route(check_auth=...) parameter. '''
- def decorator(func):
- def wrapper(*a, **ka):
- user, password = request.auth or (None, None)
- if user is None or not check(user, password):
- response.headers['WWW-Authenticate'] = 'Basic realm="%s"' % realm
- return HTTPError(401, text)
- return func(*a, **ka)
- return wrapper
- return decorator
-
-
-def make_default_app_wrapper(name):
- ''' Return a callable that relays calls to the current default app. '''
- @functools.wraps(getattr(Bottle, name))
- def wrapper(*a, **ka):
- return getattr(app(), name)(*a, **ka)
- return wrapper
-
-
-for name in '''route get post put delete error mount
- hook install uninstall'''.split():
- globals()[name] = make_default_app_wrapper(name)
-url = make_default_app_wrapper('get_url')
-del name
-
-
-
-
-
-
-###############################################################################
-# Server Adapter ###############################################################
-###############################################################################
-
-
-class ServerAdapter(object):
- quiet = False
- def __init__(self, host='127.0.0.1', port=8080, **config):
- self.options = config
- self.host = host
- self.port = int(port)
-
- def run(self, handler): # pragma: no cover
- pass
-
- def __repr__(self):
- args = ', '.join(['%s=%s'%(k,repr(v)) for k, v in self.options.items()])
- return "%s(%s)" % (self.__class__.__name__, args)
-
-
-class CGIServer(ServerAdapter):
- quiet = True
- def run(self, handler): # pragma: no cover
- from wsgiref.handlers import CGIHandler
- def fixed_environ(environ, start_response):
- environ.setdefault('PATH_INFO', '')
- return handler(environ, start_response)
- CGIHandler().run(fixed_environ)
-
-
-class FlupFCGIServer(ServerAdapter):
- def run(self, handler): # pragma: no cover
- import flup.server.fcgi
- self.options.setdefault('bindAddress', (self.host, self.port))
- flup.server.fcgi.WSGIServer(handler, **self.options).run()
-
-
-class WSGIRefServer(ServerAdapter):
- def run(self, handler): # pragma: no cover
- from wsgiref.simple_server import make_server, WSGIRequestHandler
- if self.quiet:
- class QuietHandler(WSGIRequestHandler):
- def log_request(*args, **kw): pass
- self.options['handler_class'] = QuietHandler
- srv = make_server(self.host, self.port, handler, **self.options)
- srv.serve_forever()
-
-
-class CherryPyServer(ServerAdapter):
- def run(self, handler): # pragma: no cover
- from cherrypy import wsgiserver
- server = wsgiserver.CherryPyWSGIServer((self.host, self.port), handler)
- try:
- server.start()
- finally:
- server.stop()
-
-
-class PasteServer(ServerAdapter):
- def run(self, handler): # pragma: no cover
- from paste import httpserver
- if not self.quiet:
- from paste.translogger import TransLogger
- handler = TransLogger(handler)
- httpserver.serve(handler, host=self.host, port=str(self.port),
- **self.options)
-
-
-class MeinheldServer(ServerAdapter):
- def run(self, handler):
- from meinheld import server
- server.listen((self.host, self.port))
- server.run(handler)
-
-
-class FapwsServer(ServerAdapter):
- """ Extremely fast webserver using libev. See http://www.fapws.org/ """
- def run(self, handler): # pragma: no cover
- import fapws._evwsgi as evwsgi
- from fapws import base, config
- port = self.port
- if float(config.SERVER_IDENT[-2:]) > 0.4:
- # fapws3 silently changed its API in 0.5
- port = str(port)
- evwsgi.start(self.host, port)
- # fapws3 never releases the GIL. Complain upstream. I tried. No luck.
- if 'BOTTLE_CHILD' in os.environ and not self.quiet:
- print "WARNING: Auto-reloading does not work with Fapws3."
- print " (Fapws3 breaks python thread support)"
- evwsgi.set_base_module(base)
- def app(environ, start_response):
- environ['wsgi.multiprocess'] = False
- return handler(environ, start_response)
- evwsgi.wsgi_cb(('', app))
- evwsgi.run()
-
-
-class TornadoServer(ServerAdapter):
- """ The super hyped asynchronous server by facebook. Untested. """
- def run(self, handler): # pragma: no cover
- import tornado.wsgi, tornado.httpserver, tornado.ioloop
- container = tornado.wsgi.WSGIContainer(handler)
- server = tornado.httpserver.HTTPServer(container)
- server.listen(port=self.port)
- tornado.ioloop.IOLoop.instance().start()
-
-
-class AppEngineServer(ServerAdapter):
- """ Adapter for Google App Engine. """
- quiet = True
- def run(self, handler):
- from google.appengine.ext.webapp import util
- # A main() function in the handler script enables 'App Caching'.
- # Lets makes sure it is there. This _really_ improves performance.
- module = sys.modules.get('__main__')
- if module and not hasattr(module, 'main'):
- module.main = lambda: util.run_wsgi_app(handler)
- util.run_wsgi_app(handler)
-
-
-class TwistedServer(ServerAdapter):
- """ Untested. """
- def run(self, handler):
- from twisted.web import server, wsgi
- from twisted.python.threadpool import ThreadPool
- from twisted.internet import reactor
- thread_pool = ThreadPool()
- thread_pool.start()
- reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop)
- factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler))
- reactor.listenTCP(self.port, factory, interface=self.host)
- reactor.run()
-
-
-class DieselServer(ServerAdapter):
- """ Untested. """
- def run(self, handler):
- from diesel.protocols.wsgi import WSGIApplication
- app = WSGIApplication(handler, port=self.port)
- app.run()
-
-
-class GeventServer(ServerAdapter):
- """ Untested. Options:
-
- * `monkey` (default: True) fixes the stdlib to use greenthreads.
- * `fast` (default: False) uses libevent's http server, but has some
- issues: No streaming, no pipelining, no SSL.
- """
- def run(self, handler):
- from gevent import wsgi as wsgi_fast, pywsgi, monkey, local
- if self.options.get('monkey', True):
- if not threading.local is local.local: monkey.patch_all()
- wsgi = wsgi_fast if self.options.get('fast') else pywsgi
- wsgi.WSGIServer((self.host, self.port), handler).serve_forever()
-
-
-class GunicornServer(ServerAdapter):
- """ Untested. See http://gunicorn.org/configure.html for options. """
- def run(self, handler):
- from gunicorn.app.base import Application
-
- config = {'bind': "%s:%d" % (self.host, int(self.port))}
- config.update(self.options)
-
- class GunicornApplication(Application):
- def init(self, parser, opts, args):
- return config
-
- def load(self):
- return handler
-
- GunicornApplication().run()
-
-
-class EventletServer(ServerAdapter):
- """ Untested """
- def run(self, handler):
- from eventlet import wsgi, listen
- wsgi.server(listen((self.host, self.port)), handler)
-
-
-class RocketServer(ServerAdapter):
- """ Untested. """
- def run(self, handler):
- from rocket import Rocket
- server = Rocket((self.host, self.port), 'wsgi', { 'wsgi_app' : handler })
- server.start()
-
-
-class BjoernServer(ServerAdapter):
- """ Fast server written in C: https://github.com/jonashaag/bjoern """
- def run(self, handler):
- from bjoern import run
- run(handler, self.host, self.port)
-
-
-class AutoServer(ServerAdapter):
- """ Untested. """
- adapters = [PasteServer, CherryPyServer, TwistedServer, WSGIRefServer]
- def run(self, handler):
- for sa in self.adapters:
- try:
- return sa(self.host, self.port, **self.options).run(handler)
- except ImportError:
- pass
-
-server_names = {
- 'cgi': CGIServer,
- 'flup': FlupFCGIServer,
- 'wsgiref': WSGIRefServer,
- 'cherrypy': CherryPyServer,
- 'paste': PasteServer,
- 'fapws3': FapwsServer,
- 'tornado': TornadoServer,
- 'gae': AppEngineServer,
- 'twisted': TwistedServer,
- 'diesel': DieselServer,
- 'meinheld': MeinheldServer,
- 'gunicorn': GunicornServer,
- 'eventlet': EventletServer,
- 'gevent': GeventServer,
- 'rocket': RocketServer,
- 'bjoern' : BjoernServer,
- 'auto': AutoServer,
-}
-
-
-
-
-
-
-###############################################################################
-# Application Control ##########################################################
-###############################################################################
-
-
-def load(target, **namespace):
- """ Import a module or fetch an object from a module.
-
- * ``package.module`` returns `module` as a module object.
- * ``pack.mod:name`` returns the module variable `name` from `pack.mod`.
- * ``pack.mod:func()`` calls `pack.mod.func()` and returns the result.
-
- The last form accepts not only function calls, but any type of
- expression. Keyword arguments passed to this function are available as
- local variables. Example: ``import_string('re:compile(x)', x='[a-z]')``
- """
- module, target = target.split(":", 1) if ':' in target else (target, None)
- if module not in sys.modules: __import__(module)
- if not target: return sys.modules[module]
- if target.isalnum(): return getattr(sys.modules[module], target)
- package_name = module.split('.')[0]
- namespace[package_name] = sys.modules[package_name]
- return eval('%s.%s' % (module, target), namespace)
-
-
-def load_app(target):
- """ Load a bottle application from a module and make sure that the import
- does not affect the current default application, but returns a separate
- application object. See :func:`load` for the target parameter. """
- global NORUN; NORUN, nr_old = True, NORUN
- try:
- tmp = default_app.push() # Create a new "default application"
- rv = load(target) # Import the target module
- return rv if callable(rv) else tmp
- finally:
- default_app.remove(tmp) # Remove the temporary added default application
- NORUN = nr_old
-
-def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,
- interval=1, reloader=False, quiet=False, plugins=None, **kargs):
- """ Start a server instance. This method blocks until the server terminates.
-
- :param app: WSGI application or target string supported by
- :func:`load_app`. (default: :func:`default_app`)
- :param server: Server adapter to use. See :data:`server_names` keys
- for valid names or pass a :class:`ServerAdapter` subclass.
- (default: `wsgiref`)
- :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on
- all interfaces including the external one. (default: 127.0.0.1)
- :param port: Server port to bind to. Values below 1024 require root
- privileges. (default: 8080)
- :param reloader: Start auto-reloading server? (default: False)
- :param interval: Auto-reloader interval in seconds (default: 1)
- :param quiet: Suppress output to stdout and stderr? (default: False)
- :param options: Options passed to the server adapter.
- """
- if NORUN: return
- if reloader and not os.environ.get('BOTTLE_CHILD'):
- try:
- fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock')
- os.close(fd) # We only need this file to exist. We never write to it
- while os.path.exists(lockfile):
- args = [sys.executable] + sys.argv
- environ = os.environ.copy()
- environ['BOTTLE_CHILD'] = 'true'
- environ['BOTTLE_LOCKFILE'] = lockfile
- p = subprocess.Popen(args, env=environ)
- while p.poll() is None: # Busy wait...
- os.utime(lockfile, None) # I am alive!
- time.sleep(interval)
- if p.poll() != 3:
- if os.path.exists(lockfile): os.unlink(lockfile)
- sys.exit(p.poll())
- except KeyboardInterrupt:
- pass
- finally:
- if os.path.exists(lockfile):
- os.unlink(lockfile)
- return
-
- stderr = sys.stderr.write
-
- try:
- app = app or default_app()
- if isinstance(app, basestring):
- app = load_app(app)
- if not callable(app):
- raise ValueError("Application is not callable: %r" % app)
-
- for plugin in plugins or []:
- app.install(plugin)
-
- if server in server_names:
- server = server_names.get(server)
- if isinstance(server, basestring):
- server = load(server)
- if isinstance(server, type):
- server = server(host=host, port=port, **kargs)
- if not isinstance(server, ServerAdapter):
- raise ValueError("Unknown or unsupported server: %r" % server)
-
- server.quiet = server.quiet or quiet
- if not server.quiet:
- stderr("Bottle server starting up (using %s)...\n" % repr(server))
- stderr("Listening on http://%s:%d/\n" % (server.host, server.port))
- stderr("Hit Ctrl-C to quit.\n\n")
-
- if reloader:
- lockfile = os.environ.get('BOTTLE_LOCKFILE')
- bgcheck = FileCheckerThread(lockfile, interval)
- with bgcheck:
- server.run(app)
- if bgcheck.status == 'reload':
- sys.exit(3)
- else:
- server.run(app)
- except KeyboardInterrupt:
- pass
- except (SyntaxError, ImportError):
- if not reloader: raise
- if not getattr(server, 'quiet', False): print_exc()
- sys.exit(3)
- finally:
- if not getattr(server, 'quiet', False): stderr('Shutdown...\n')
-
-
-class FileCheckerThread(threading.Thread):
- ''' Interrupt main-thread as soon as a changed module file is detected,
- the lockfile gets deleted or gets to old. '''
-
- def __init__(self, lockfile, interval):
- threading.Thread.__init__(self)
- self.lockfile, self.interval = lockfile, interval
- #: Is one of 'reload', 'error' or 'exit'
- self.status = None
-
- def run(self):
- exists = os.path.exists
- mtime = lambda path: os.stat(path).st_mtime
- files = dict()
-
- for module in sys.modules.values():
- path = getattr(module, '__file__', '')
- if path[-4:] in ('.pyo', '.pyc'): path = path[:-1]
- if path and exists(path): files[path] = mtime(path)
-
- while not self.status:
- if not exists(self.lockfile)\
- or mtime(self.lockfile) < time.time() - self.interval - 5:
- self.status = 'error'
- thread.interrupt_main()
- for path, lmtime in files.iteritems():
- if not exists(path) or mtime(path) > lmtime:
- self.status = 'reload'
- thread.interrupt_main()
- break
- time.sleep(self.interval)
-
- def __enter__(self):
- self.start()
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- if not self.status: self.status = 'exit' # silent exit
- self.join()
- return issubclass(exc_type, KeyboardInterrupt)
-
-
-
-
-
-###############################################################################
-# Template Adapters ############################################################
-###############################################################################
-
-
-class TemplateError(HTTPError):
- def __init__(self, message):
- HTTPError.__init__(self, 500, message)
-
-
-class BaseTemplate(object):
- """ Base class and minimal API for template adapters """
- extensions = ['tpl','html','thtml','stpl']
- settings = {} #used in prepare()
- defaults = {} #used in render()
-
- def __init__(self, source=None, name=None, lookup=[], encoding='utf8', **settings):
- """ Create a new template.
- If the source parameter (str or buffer) is missing, the name argument
- is used to guess a template filename. Subclasses can assume that
- self.source and/or self.filename are set. Both are strings.
- The lookup, encoding and settings parameters are stored as instance
- variables.
- The lookup parameter stores a list containing directory paths.
- The encoding parameter should be used to decode byte strings or files.
- The settings parameter contains a dict for engine-specific settings.
- """
- self.name = name
- self.source = source.read() if hasattr(source, 'read') else source
- self.filename = source.filename if hasattr(source, 'filename') else None
- self.lookup = map(os.path.abspath, lookup)
- self.encoding = encoding
- self.settings = self.settings.copy() # Copy from class variable
- self.settings.update(settings) # Apply
- if not self.source and self.name:
- self.filename = self.search(self.name, self.lookup)
- if not self.filename:
- raise TemplateError('Template %s not found.' % repr(name))
- if not self.source and not self.filename:
- raise TemplateError('No template specified.')
- self.prepare(**self.settings)
-
- @classmethod
- def search(cls, name, lookup=[]):
- """ Search name in all directories specified in lookup.
- First without, then with common extensions. Return first hit. """
- if os.path.isfile(name): return name
- for spath in lookup:
- fname = os.path.join(spath, name)
- if os.path.isfile(fname):
- return fname
- for ext in cls.extensions:
- if os.path.isfile('%s.%s' % (fname, ext)):
- return '%s.%s' % (fname, ext)
-
- @classmethod
- def global_config(cls, key, *args):
- ''' This reads or sets the global settings stored in class.settings. '''
- if args:
- cls.settings = cls.settings.copy() # Make settings local to class
- cls.settings[key] = args[0]
- else:
- return cls.settings[key]
-
- def prepare(self, **options):
- """ Run preparations (parsing, caching, ...).
- It should be possible to call this again to refresh a template or to
- update settings.
- """
- raise NotImplementedError
-
- def render(self, *args, **kwargs):
- """ Render the template with the specified local variables and return
- a single byte or unicode string. If it is a byte string, the encoding
- must match self.encoding. This method must be thread-safe!
- Local variables may be provided in dictionaries (*args)
- or directly, as keywords (**kwargs).
- """
- raise NotImplementedError
-
-
-class MakoTemplate(BaseTemplate):
- def prepare(self, **options):
- from mako.template import Template
- from mako.lookup import TemplateLookup
- options.update({'input_encoding':self.encoding})
- options.setdefault('format_exceptions', bool(DEBUG))
- lookup = TemplateLookup(directories=self.lookup, **options)
- if self.source:
- self.tpl = Template(self.source, lookup=lookup, **options)
- else:
- self.tpl = Template(uri=self.name, filename=self.filename, lookup=lookup, **options)
-
- def render(self, *args, **kwargs):
- for dictarg in args: kwargs.update(dictarg)
- _defaults = self.defaults.copy()
- _defaults.update(kwargs)
- return self.tpl.render(**_defaults)
-
-
-class CheetahTemplate(BaseTemplate):
- def prepare(self, **options):
- from Cheetah.Template import Template
- self.context = threading.local()
- self.context.vars = {}
- options['searchList'] = [self.context.vars]
- if self.source:
- self.tpl = Template(source=self.source, **options)
- else:
- self.tpl = Template(file=self.filename, **options)
-
- def render(self, *args, **kwargs):
- for dictarg in args: kwargs.update(dictarg)
- self.context.vars.update(self.defaults)
- self.context.vars.update(kwargs)
- out = str(self.tpl)
- self.context.vars.clear()
- return out
-
-
-class Jinja2Template(BaseTemplate):
- def prepare(self, filters=None, tests=None, **kwargs):
- from jinja2 import Environment, FunctionLoader
- if 'prefix' in kwargs: # TODO: to be removed after a while
- raise RuntimeError('The keyword argument `prefix` has been removed. '
- 'Use the full jinja2 environment name line_statement_prefix instead.')
- self.env = Environment(loader=FunctionLoader(self.loader), **kwargs)
- if filters: self.env.filters.update(filters)
- if tests: self.env.tests.update(tests)
- if self.source:
- self.tpl = self.env.from_string(self.source)
- else:
- self.tpl = self.env.get_template(self.filename)
-
- def render(self, *args, **kwargs):
- for dictarg in args: kwargs.update(dictarg)
- _defaults = self.defaults.copy()
- _defaults.update(kwargs)
- return self.tpl.render(**_defaults)
-
- def loader(self, name):
- fname = self.search(name, self.lookup)
- if fname:
- with open(fname, "rb") as f:
- return f.read().decode(self.encoding)
-
-
-class SimpleTALTemplate(BaseTemplate):
- ''' Untested! '''
- def prepare(self, **options):
- from simpletal import simpleTAL
- # TODO: add option to load METAL files during render
- if self.source:
- self.tpl = simpleTAL.compileHTMLTemplate(self.source)
- else:
- with open(self.filename, 'rb') as fp:
- self.tpl = simpleTAL.compileHTMLTemplate(tonat(fp.read()))
-
- def render(self, *args, **kwargs):
- from simpletal import simpleTALES
- for dictarg in args: kwargs.update(dictarg)
- # TODO: maybe reuse a context instead of always creating one
- context = simpleTALES.Context()
- for k,v in self.defaults.items():
- context.addGlobal(k, v)
- for k,v in kwargs.items():
- context.addGlobal(k, v)
- output = StringIO()
- self.tpl.expand(context, output)
- return output.getvalue()
-
-
-class SimpleTemplate(BaseTemplate):
- blocks = ('if', 'elif', 'else', 'try', 'except', 'finally', 'for', 'while',
- 'with', 'def', 'class')
- dedent_blocks = ('elif', 'else', 'except', 'finally')
-
- @lazy_attribute
- def re_pytokens(cls):
- ''' This matches comments and all kinds of quoted strings but does
- NOT match comments (#...) within quoted strings. (trust me) '''
- return re.compile(r'''
- (''(?!')|""(?!")|'{6}|"{6} # Empty strings (all 4 types)
- |'(?:[^\\']|\\.)+?' # Single quotes (')
- |"(?:[^\\"]|\\.)+?" # Double quotes (")
- |'{3}(?:[^\\]|\\.|\n)+?'{3} # Triple-quoted strings (')
- |"{3}(?:[^\\]|\\.|\n)+?"{3} # Triple-quoted strings (")
- |\#.* # Comments
- )''', re.VERBOSE)
-
- def prepare(self, escape_func=html_escape, noescape=False, **kwargs):
- self.cache = {}
- enc = self.encoding
- self._str = lambda x: touni(x, enc)
- self._escape = lambda x: escape_func(touni(x, enc))
- if noescape:
- self._str, self._escape = self._escape, self._str
-
- @classmethod
- def split_comment(cls, code):
- """ Removes comments (#...) from python code. """
- if '#' not in code: return code
- #: Remove comments only (leave quoted strings as they are)
- subf = lambda m: '' if m.group(0)[0]=='#' else m.group(0)
- return re.sub(cls.re_pytokens, subf, code)
-
- @cached_property
- def co(self):
- return compile(self.code, self.filename or '<string>', 'exec')
-
- @cached_property
- def code(self):
- stack = [] # Current Code indentation
- lineno = 0 # Current line of code
- ptrbuffer = [] # Buffer for printable strings and token tuple instances
- codebuffer = [] # Buffer for generated python code
- multiline = dedent = oneline = False
- template = self.source or open(self.filename, 'rb').read()
-
- def yield_tokens(line):
- for i, part in enumerate(re.split(r'\{\{(.*?)\}\}', line)):
- if i % 2:
- if part.startswith('!'): yield 'RAW', part[1:]
- else: yield 'CMD', part
- else: yield 'TXT', part
-
- def flush(): # Flush the ptrbuffer
- if not ptrbuffer: return
- cline = ''
- for line in ptrbuffer:
- for token, value in line:
- if token == 'TXT': cline += repr(value)
- elif token == 'RAW': cline += '_str(%s)' % value
- elif token == 'CMD': cline += '_escape(%s)' % value
- cline += ', '
- cline = cline[:-2] + '\\\n'
- cline = cline[:-2]
- if cline[:-1].endswith('\\\\\\\\\\n'):
- cline = cline[:-7] + cline[-1] # 'nobr\\\\\n' --> 'nobr'
- cline = '_printlist([' + cline + '])'
- del ptrbuffer[:] # Do this before calling code() again
- code(cline)
-
- def code(stmt):
- for line in stmt.splitlines():
- codebuffer.append(' ' * len(stack) + line.strip())
-
- for line in template.splitlines(True):
- lineno += 1
- line = line if isinstance(line, unicode)\
- else unicode(line, encoding=self.encoding)
- if lineno <= 2:
- m = re.search(r"%.*coding[:=]\s*([-\w\.]+)", line)
- if m: self.encoding = m.group(1)
- if m: line = line.replace('coding','coding (removed)')
- if line.strip()[:2].count('%') == 1:
- line = line.split('%',1)[1].lstrip() # Full line following the %
- cline = self.split_comment(line).strip()
- cmd = re.split(r'[^a-zA-Z0-9_]', cline)[0]
- flush() # You are actually reading this? Good luck, it's a mess :)
- if cmd in self.blocks or multiline:
- cmd = multiline or cmd
- dedent = cmd in self.dedent_blocks # "else:"
- if dedent and not oneline and not multiline:
- cmd = stack.pop()
- code(line)
- oneline = not cline.endswith(':') # "if 1: pass"
- multiline = cmd if cline.endswith('\\') else False
- if not oneline and not multiline:
- stack.append(cmd)
- elif cmd == 'end' and stack:
- code('#end(%s) %s' % (stack.pop(), line.strip()[3:]))
- elif cmd == 'include':
- p = cline.split(None, 2)[1:]
- if len(p) == 2:
- code("_=_include(%s, _stdout, %s)" % (repr(p[0]), p[1]))
- elif p:
- code("_=_include(%s, _stdout)" % repr(p[0]))
- else: # Empty %include -> reverse of %rebase
- code("_printlist(_base)")
- elif cmd == 'rebase':
- p = cline.split(None, 2)[1:]
- if len(p) == 2:
- code("globals()['_rebase']=(%s, dict(%s))" % (repr(p[0]), p[1]))
- elif p:
- code("globals()['_rebase']=(%s, {})" % repr(p[0]))
- else:
- code(line)
- else: # Line starting with text (not '%') or '%%' (escaped)
- if line.strip().startswith('%%'):
- line = line.replace('%%', '%', 1)
- ptrbuffer.append(yield_tokens(line))
- flush()
- return '\n'.join(codebuffer) + '\n'
-
- def subtemplate(self, _name, _stdout, *args, **kwargs):
- for dictarg in args: kwargs.update(dictarg)
- if _name not in self.cache:
- self.cache[_name] = self.__class__(name=_name, lookup=self.lookup)
- return self.cache[_name].execute(_stdout, kwargs)
-
- def execute(self, _stdout, *args, **kwargs):
- for dictarg in args: kwargs.update(dictarg)
- env = self.defaults.copy()
- env.update({'_stdout': _stdout, '_printlist': _stdout.extend,
- '_include': self.subtemplate, '_str': self._str,
- '_escape': self._escape, 'get': env.get,
- 'setdefault': env.setdefault, 'defined': env.__contains__})
- env.update(kwargs)
- eval(self.co, env)
- if '_rebase' in env:
- subtpl, rargs = env['_rebase']
- rargs['_base'] = _stdout[:] #copy stdout
- del _stdout[:] # clear stdout
- return self.subtemplate(subtpl,_stdout,rargs)
- return env
-
- def render(self, *args, **kwargs):
- """ Render the template using keyword arguments as local variables. """
- for dictarg in args: kwargs.update(dictarg)
- stdout = []
- self.execute(stdout, kwargs)
- return ''.join(stdout)
-
-
-def template(*args, **kwargs):
- '''
- Get a rendered template as a string iterator.
- You can use a name, a filename or a template string as first parameter.
- Template rendering arguments can be passed as dictionaries
- or directly (as keyword arguments).
- '''
- tpl = args[0] if args else None
- template_adapter = kwargs.pop('template_adapter', SimpleTemplate)
- if tpl not in TEMPLATES or DEBUG:
- settings = kwargs.pop('template_settings', {})
- lookup = kwargs.pop('template_lookup', TEMPLATE_PATH)
- if isinstance(tpl, template_adapter):
- TEMPLATES[tpl] = tpl
- if settings: TEMPLATES[tpl].prepare(**settings)
- elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl:
- TEMPLATES[tpl] = template_adapter(source=tpl, lookup=lookup, **settings)
- else:
- TEMPLATES[tpl] = template_adapter(name=tpl, lookup=lookup, **settings)
- if not TEMPLATES[tpl]:
- abort(500, 'Template (%s) not found' % tpl)
- for dictarg in args[1:]: kwargs.update(dictarg)
- return TEMPLATES[tpl].render(kwargs)
-
-mako_template = functools.partial(template, template_adapter=MakoTemplate)
-cheetah_template = functools.partial(template, template_adapter=CheetahTemplate)
-jinja2_template = functools.partial(template, template_adapter=Jinja2Template)
-simpletal_template = functools.partial(template, template_adapter=SimpleTALTemplate)
-
-
-def view(tpl_name, **defaults):
- ''' Decorator: renders a template for a handler.
- The handler can control its behavior like that:
-
- - return a dict of template vars to fill out the template
- - return something other than a dict and the view decorator will not
- process the template, but return the handler result as is.
- This includes returning a HTTPResponse(dict) to get,
- for instance, JSON with autojson or other castfilters.
- '''
- def decorator(func):
- @functools.wraps(func)
- def wrapper(*args, **kwargs):
- result = func(*args, **kwargs)
- if isinstance(result, (dict, DictMixin)):
- tplvars = defaults.copy()
- tplvars.update(result)
- return template(tpl_name, **tplvars)
- return result
- return wrapper
- return decorator
-
-mako_view = functools.partial(view, template_adapter=MakoTemplate)
-cheetah_view = functools.partial(view, template_adapter=CheetahTemplate)
-jinja2_view = functools.partial(view, template_adapter=Jinja2Template)
-simpletal_view = functools.partial(view, template_adapter=SimpleTALTemplate)
-
-
-
-
-
-
-###############################################################################
-# Constants and Globals ########################################################
-###############################################################################
-
-
-TEMPLATE_PATH = ['./', './views/']
-TEMPLATES = {}
-DEBUG = False
-NORUN = False # If set, run() does nothing. Used by load_app()
-
-#: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found')
-HTTP_CODES = httplib.responses
-HTTP_CODES[418] = "I'm a teapot" # RFC 2324
-HTTP_CODES[428] = "Precondition Required"
-HTTP_CODES[429] = "Too Many Requests"
-HTTP_CODES[431] = "Request Header Fields Too Large"
-HTTP_CODES[511] = "Network Authentication Required"
-_HTTP_STATUS_LINES = dict((k, '%d %s'%(k,v)) for (k,v) in HTTP_CODES.iteritems())
-
-#: The default template used for error pages. Override with @error()
-ERROR_PAGE_TEMPLATE = """
-%try:
- %from bottle import DEBUG, HTTP_CODES, request, touni
- %status_name = HTTP_CODES.get(e.status, 'Unknown').title()
- <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
- <html>
- <head>
- <title>Error {{e.status}}: {{status_name}}</title>
- <style type="text/css">
- html {background-color: #eee; font-family: sans;}
- body {background-color: #fff; border: 1px solid #ddd;
- padding: 15px; margin: 15px;}
- pre {background-color: #eee; border: 1px solid #ddd; padding: 5px;}
- </style>
- </head>
- <body>
- <h1>Error {{e.status}}: {{status_name}}</h1>
- <p>Sorry, the requested URL <tt>{{repr(request.url)}}</tt>
- caused an error:</p>
- <pre>{{e.output}}</pre>
- %if DEBUG and e.exception:
- <h2>Exception:</h2>
- <pre>{{repr(e.exception)}}</pre>
- %end
- %if DEBUG and e.traceback:
- <h2>Traceback:</h2>
- <pre>{{e.traceback}}</pre>
- %end
- </body>
- </html>
-%except ImportError:
- <b>ImportError:</b> Could not generate the error page. Please add bottle to
- the import path.
-%end
-"""
-
-#: A thread-safe instance of :class:`Request` representing the `current` request.
-request = Request()
-
-#: A thread-safe instance of :class:`Response` used to build the HTTP response.
-response = Response()
-
-#: A thread-safe namespace. Not used by Bottle.
-local = threading.local()
-
-# Initialize app stack (create first empty Bottle app)
-# BC: 0.6.4 and needed for run()
-app = default_app = AppStack()
-app.push()
-
-#: A virtual package that redirects import statements.
-#: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`.
-ext = _ImportRedirect(__name__+'.ext', 'bottle_%s').module
-
-if __name__ == '__main__':
- opt, args, parser = _cmd_options, _cmd_args, _cmd_parser
- if opt.version:
- print 'Bottle', __version__; sys.exit(0)
- if not args:
- parser.print_help()
- print '\nError: No application specified.\n'
- sys.exit(1)
-
- try:
- sys.path.insert(0, '.')
- sys.modules.setdefault('bottle', sys.modules['__main__'])
- except (AttributeError, ImportError), e:
- parser.error(e.args[0])
-
- if opt.bind and ':' in opt.bind:
- host, port = opt.bind.rsplit(':', 1)
- else:
- host, port = (opt.bind or 'localhost'), 8080
-
- debug(opt.debug)
- run(args[0], host=host, port=port, server=opt.server, reloader=opt.reload, plugins=opt.plugin)
-
-# THE END
diff --git a/module/lib/feedparser.py b/module/lib/feedparser.py
deleted file mode 100644
index a746ed8f5..000000000
--- a/module/lib/feedparser.py
+++ /dev/null
@@ -1,3885 +0,0 @@
-#!/usr/bin/env python
-"""Universal feed parser
-
-Handles RSS 0.9x, RSS 1.0, RSS 2.0, CDF, Atom 0.3, and Atom 1.0 feeds
-
-Visit http://feedparser.org/ for the latest version
-Visit http://feedparser.org/docs/ for the latest documentation
-
-Required: Python 2.4 or later
-Recommended: CJKCodecs and iconv_codec <http://cjkpython.i18n.org/>
-"""
-
-__version__ = "5.0"
-__license__ = """Copyright (c) 2002-2008, Mark Pilgrim, All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE."""
-__author__ = "Mark Pilgrim <http://diveintomark.org/>"
-__contributors__ = ["Jason Diamond <http://injektilo.org/>",
- "John Beimler <http://john.beimler.org/>",
- "Fazal Majid <http://www.majid.info/mylos/weblog/>",
- "Aaron Swartz <http://aaronsw.com/>",
- "Kevin Marks <http://epeus.blogspot.com/>",
- "Sam Ruby <http://intertwingly.net/>",
- "Ade Oshineye <http://blog.oshineye.com/>",
- "Martin Pool <http://sourcefrog.net/>",
- "Kurt McKee <http://kurtmckee.org/>"]
-_debug = 0
-
-# HTTP "User-Agent" header to send to servers when downloading feeds.
-# If you are embedding feedparser in a larger application, you should
-# change this to your application name and URL.
-USER_AGENT = "UniversalFeedParser/%s +http://feedparser.org/" % __version__
-
-# HTTP "Accept" header to send to servers when downloading feeds. If you don't
-# want to send an Accept header, set this to None.
-ACCEPT_HEADER = "application/atom+xml,application/rdf+xml,application/rss+xml,application/x-netcdf,application/xml;q=0.9,text/xml;q=0.2,*/*;q=0.1"
-
-# List of preferred XML parsers, by SAX driver name. These will be tried first,
-# but if they're not installed, Python will keep searching through its own list
-# of pre-installed parsers until it finds one that supports everything we need.
-PREFERRED_XML_PARSERS = ["drv_libxml2"]
-
-# If you want feedparser to automatically run HTML markup through HTML Tidy, set
-# this to 1. Requires mxTidy <http://www.egenix.com/files/python/mxTidy.html>
-# or utidylib <http://utidylib.berlios.de/>.
-TIDY_MARKUP = 0
-
-# List of Python interfaces for HTML Tidy, in order of preference. Only useful
-# if TIDY_MARKUP = 1
-PREFERRED_TIDY_INTERFACES = ["uTidy", "mxTidy"]
-
-# If you want feedparser to automatically resolve all relative URIs, set this
-# to 1.
-RESOLVE_RELATIVE_URIS = 1
-
-# If you want feedparser to automatically sanitize all potentially unsafe
-# HTML content, set this to 1.
-SANITIZE_HTML = 1
-
-# ---------- Python 3 modules (make it work if possible) ----------
-try:
- import rfc822
-except ImportError:
- from email import _parseaddr as rfc822
-
-try:
- # Python 3.1 introduces bytes.maketrans and simultaneously
- # deprecates string.maketrans; use bytes.maketrans if possible
- _maketrans = bytes.maketrans
-except (NameError, AttributeError):
- import string
- _maketrans = string.maketrans
-
-# base64 support for Atom feeds that contain embedded binary data
-try:
- import base64, binascii
- # Python 3.1 deprecates decodestring in favor of decodebytes
- _base64decode = getattr(base64, 'decodebytes', base64.decodestring)
-except:
- base64 = binascii = None
-
-def _s2bytes(s):
- # Convert a UTF-8 str to bytes if the interpreter is Python 3
- try:
- return bytes(s, 'utf8')
- except (NameError, TypeError):
- # In Python 2.5 and below, bytes doesn't exist (NameError)
- # In Python 2.6 and above, bytes and str are the same (TypeError)
- return s
-
-def _l2bytes(l):
- # Convert a list of ints to bytes if the interpreter is Python 3
- try:
- if bytes is not str:
- # In Python 2.6 and above, this call won't raise an exception
- # but it will return bytes([65]) as '[65]' instead of 'A'
- return bytes(l)
- raise NameError
- except NameError:
- return ''.join(map(chr, l))
-
-# If you want feedparser to allow all URL schemes, set this to ()
-# List culled from Python's urlparse documentation at:
-# http://docs.python.org/library/urlparse.html
-# as well as from "URI scheme" at Wikipedia:
-# https://secure.wikimedia.org/wikipedia/en/wiki/URI_scheme
-# Many more will likely need to be added!
-ACCEPTABLE_URI_SCHEMES = (
- 'file', 'ftp', 'gopher', 'h323', 'hdl', 'http', 'https', 'imap', 'mailto',
- 'mms', 'news', 'nntp', 'prospero', 'rsync', 'rtsp', 'rtspu', 'sftp',
- 'shttp', 'sip', 'sips', 'snews', 'svn', 'svn+ssh', 'telnet', 'wais',
- # Additional common-but-unofficial schemes
- 'aim', 'callto', 'cvs', 'facetime', 'feed', 'git', 'gtalk', 'irc', 'ircs',
- 'irc6', 'itms', 'mms', 'msnim', 'skype', 'ssh', 'smb', 'svn', 'ymsg',
-)
-#ACCEPTABLE_URI_SCHEMES = ()
-
-# ---------- required modules (should come with any Python distribution) ----------
-import sgmllib, re, sys, copy, urlparse, time, types, cgi, urllib, urllib2, datetime
-try:
- from io import BytesIO as _StringIO
-except ImportError:
- try:
- from cStringIO import StringIO as _StringIO
- except:
- from StringIO import StringIO as _StringIO
-
-# ---------- optional modules (feedparser will work without these, but with reduced functionality) ----------
-
-# gzip is included with most Python distributions, but may not be available if you compiled your own
-try:
- import gzip
-except:
- gzip = None
-try:
- import zlib
-except:
- zlib = None
-
-# If a real XML parser is available, feedparser will attempt to use it. feedparser has
-# been tested with the built-in SAX parser, PyXML, and libxml2. On platforms where the
-# Python distribution does not come with an XML parser (such as Mac OS X 10.2 and some
-# versions of FreeBSD), feedparser will quietly fall back on regex-based parsing.
-try:
- import xml.sax
- xml.sax.make_parser(PREFERRED_XML_PARSERS) # test for valid parsers
- from xml.sax.saxutils import escape as _xmlescape
- _XML_AVAILABLE = 1
-except:
- _XML_AVAILABLE = 0
- def _xmlescape(data,entities={}):
- data = data.replace('&', '&amp;')
- data = data.replace('>', '&gt;')
- data = data.replace('<', '&lt;')
- for char, entity in entities:
- data = data.replace(char, entity)
- return data
-
-# cjkcodecs and iconv_codec provide support for more character encodings.
-# Both are available from http://cjkpython.i18n.org/
-try:
- import cjkcodecs.aliases
-except:
- pass
-try:
- import iconv_codec
-except:
- pass
-
-# chardet library auto-detects character encodings
-# Download from http://chardet.feedparser.org/
-try:
- import chardet
- if _debug:
- import chardet.constants
- chardet.constants._debug = 1
-except:
- chardet = None
-
-# reversable htmlentitydefs mappings for Python 2.2
-try:
- from htmlentitydefs import name2codepoint, codepoint2name
-except:
- import htmlentitydefs
- name2codepoint={}
- codepoint2name={}
- for (name,codepoint) in htmlentitydefs.entitydefs.iteritems():
- if codepoint.startswith('&#'): codepoint=unichr(int(codepoint[2:-1]))
- name2codepoint[name]=ord(codepoint)
- codepoint2name[ord(codepoint)]=name
-
-# BeautifulSoup parser used for parsing microformats from embedded HTML content
-# http://www.crummy.com/software/BeautifulSoup/
-# feedparser is tested with BeautifulSoup 3.0.x, but it might work with the
-# older 2.x series. If it doesn't, and you can figure out why, I'll accept a
-# patch and modify the compatibility statement accordingly.
-try:
- import BeautifulSoup
-except:
- BeautifulSoup = None
-
-# ---------- don't touch these ----------
-class ThingsNobodyCaresAboutButMe(Exception): pass
-class CharacterEncodingOverride(ThingsNobodyCaresAboutButMe): pass
-class CharacterEncodingUnknown(ThingsNobodyCaresAboutButMe): pass
-class NonXMLContentType(ThingsNobodyCaresAboutButMe): pass
-class UndeclaredNamespace(Exception): pass
-
-sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*')
-sgmllib.special = re.compile('<!')
-sgmllib.charref = re.compile('&#(\d+|[xX][0-9a-fA-F]+);')
-
-if sgmllib.endbracket.search(' <').start(0):
- class EndBracketRegEx:
- def __init__(self):
- # Overriding the built-in sgmllib.endbracket regex allows the
- # parser to find angle brackets embedded in element attributes.
- self.endbracket = re.compile('''([^'"<>]|"[^"]*"(?=>|/|\s|\w+=)|'[^']*'(?=>|/|\s|\w+=))*(?=[<>])|.*?(?=[<>])''')
- def search(self,string,index=0):
- match = self.endbracket.match(string,index)
- if match is not None:
- # Returning a new object in the calling thread's context
- # resolves a thread-safety.
- return EndBracketMatch(match)
- return None
- class EndBracketMatch:
- def __init__(self, match):
- self.match = match
- def start(self, n):
- return self.match.end(n)
- sgmllib.endbracket = EndBracketRegEx()
-
-SUPPORTED_VERSIONS = {'': 'unknown',
- 'rss090': 'RSS 0.90',
- 'rss091n': 'RSS 0.91 (Netscape)',
- 'rss091u': 'RSS 0.91 (Userland)',
- 'rss092': 'RSS 0.92',
- 'rss093': 'RSS 0.93',
- 'rss094': 'RSS 0.94',
- 'rss20': 'RSS 2.0',
- 'rss10': 'RSS 1.0',
- 'rss': 'RSS (unknown version)',
- 'atom01': 'Atom 0.1',
- 'atom02': 'Atom 0.2',
- 'atom03': 'Atom 0.3',
- 'atom10': 'Atom 1.0',
- 'atom': 'Atom (unknown version)',
- 'cdf': 'CDF',
- 'hotrss': 'Hot RSS'
- }
-
-try:
- UserDict = dict
-except NameError:
- # Python 2.1 does not have dict
- from UserDict import UserDict
- def dict(aList):
- rc = {}
- for k, v in aList:
- rc[k] = v
- return rc
-
-class FeedParserDict(UserDict):
- keymap = {'channel': 'feed',
- 'items': 'entries',
- 'guid': 'id',
- 'date': 'updated',
- 'date_parsed': 'updated_parsed',
- 'description': ['summary', 'subtitle'],
- 'url': ['href'],
- 'modified': 'updated',
- 'modified_parsed': 'updated_parsed',
- 'issued': 'published',
- 'issued_parsed': 'published_parsed',
- 'copyright': 'rights',
- 'copyright_detail': 'rights_detail',
- 'tagline': 'subtitle',
- 'tagline_detail': 'subtitle_detail'}
- def __getitem__(self, key):
- if key == 'category':
- return UserDict.__getitem__(self, 'tags')[0]['term']
- if key == 'enclosures':
- norel = lambda link: FeedParserDict([(name,value) for (name,value) in link.items() if name!='rel'])
- return [norel(link) for link in UserDict.__getitem__(self, 'links') if link['rel']=='enclosure']
- if key == 'license':
- for link in UserDict.__getitem__(self, 'links'):
- if link['rel']=='license' and link.has_key('href'):
- return link['href']
- if key == 'categories':
- return [(tag['scheme'], tag['term']) for tag in UserDict.__getitem__(self, 'tags')]
- realkey = self.keymap.get(key, key)
- if type(realkey) == types.ListType:
- for k in realkey:
- if UserDict.__contains__(self, k):
- return UserDict.__getitem__(self, k)
- if UserDict.__contains__(self, key):
- return UserDict.__getitem__(self, key)
- return UserDict.__getitem__(self, realkey)
-
- def __setitem__(self, key, value):
- for k in self.keymap.keys():
- if key == k:
- key = self.keymap[k]
- if type(key) == types.ListType:
- key = key[0]
- return UserDict.__setitem__(self, key, value)
-
- def get(self, key, default=None):
- if self.has_key(key):
- return self[key]
- else:
- return default
-
- def setdefault(self, key, value):
- if not self.has_key(key):
- self[key] = value
- return self[key]
-
- def has_key(self, key):
- try:
- return hasattr(self, key) or UserDict.__contains__(self, key)
- except AttributeError:
- return False
- # This alias prevents the 2to3 tool from changing the semantics of the
- # __contains__ function below and exhausting the maximum recursion depth
- __has_key = has_key
-
- def __getattr__(self, key):
- try:
- return self.__dict__[key]
- except KeyError:
- pass
- try:
- assert not key.startswith('_')
- return self.__getitem__(key)
- except:
- raise AttributeError, "object has no attribute '%s'" % key
-
- def __setattr__(self, key, value):
- if key.startswith('_') or key == 'data':
- self.__dict__[key] = value
- else:
- return self.__setitem__(key, value)
-
- def __contains__(self, key):
- return self.__has_key(key)
-
-def zopeCompatibilityHack():
- global FeedParserDict
- del FeedParserDict
- def FeedParserDict(aDict=None):
- rc = {}
- if aDict:
- rc.update(aDict)
- return rc
-
-_ebcdic_to_ascii_map = None
-def _ebcdic_to_ascii(s):
- global _ebcdic_to_ascii_map
- if not _ebcdic_to_ascii_map:
- emap = (
- 0,1,2,3,156,9,134,127,151,141,142,11,12,13,14,15,
- 16,17,18,19,157,133,8,135,24,25,146,143,28,29,30,31,
- 128,129,130,131,132,10,23,27,136,137,138,139,140,5,6,7,
- 144,145,22,147,148,149,150,4,152,153,154,155,20,21,158,26,
- 32,160,161,162,163,164,165,166,167,168,91,46,60,40,43,33,
- 38,169,170,171,172,173,174,175,176,177,93,36,42,41,59,94,
- 45,47,178,179,180,181,182,183,184,185,124,44,37,95,62,63,
- 186,187,188,189,190,191,192,193,194,96,58,35,64,39,61,34,
- 195,97,98,99,100,101,102,103,104,105,196,197,198,199,200,201,
- 202,106,107,108,109,110,111,112,113,114,203,204,205,206,207,208,
- 209,126,115,116,117,118,119,120,121,122,210,211,212,213,214,215,
- 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,
- 123,65,66,67,68,69,70,71,72,73,232,233,234,235,236,237,
- 125,74,75,76,77,78,79,80,81,82,238,239,240,241,242,243,
- 92,159,83,84,85,86,87,88,89,90,244,245,246,247,248,249,
- 48,49,50,51,52,53,54,55,56,57,250,251,252,253,254,255
- )
- _ebcdic_to_ascii_map = _maketrans( \
- _l2bytes(range(256)), _l2bytes(emap))
- return s.translate(_ebcdic_to_ascii_map)
-
-_cp1252 = {
- unichr(128): unichr(8364), # euro sign
- unichr(130): unichr(8218), # single low-9 quotation mark
- unichr(131): unichr( 402), # latin small letter f with hook
- unichr(132): unichr(8222), # double low-9 quotation mark
- unichr(133): unichr(8230), # horizontal ellipsis
- unichr(134): unichr(8224), # dagger
- unichr(135): unichr(8225), # double dagger
- unichr(136): unichr( 710), # modifier letter circumflex accent
- unichr(137): unichr(8240), # per mille sign
- unichr(138): unichr( 352), # latin capital letter s with caron
- unichr(139): unichr(8249), # single left-pointing angle quotation mark
- unichr(140): unichr( 338), # latin capital ligature oe
- unichr(142): unichr( 381), # latin capital letter z with caron
- unichr(145): unichr(8216), # left single quotation mark
- unichr(146): unichr(8217), # right single quotation mark
- unichr(147): unichr(8220), # left double quotation mark
- unichr(148): unichr(8221), # right double quotation mark
- unichr(149): unichr(8226), # bullet
- unichr(150): unichr(8211), # en dash
- unichr(151): unichr(8212), # em dash
- unichr(152): unichr( 732), # small tilde
- unichr(153): unichr(8482), # trade mark sign
- unichr(154): unichr( 353), # latin small letter s with caron
- unichr(155): unichr(8250), # single right-pointing angle quotation mark
- unichr(156): unichr( 339), # latin small ligature oe
- unichr(158): unichr( 382), # latin small letter z with caron
- unichr(159): unichr( 376)} # latin capital letter y with diaeresis
-
-_urifixer = re.compile('^([A-Za-z][A-Za-z0-9+-.]*://)(/*)(.*?)')
-def _urljoin(base, uri):
- uri = _urifixer.sub(r'\1\3', uri)
- try:
- return urlparse.urljoin(base, uri)
- except:
- uri = urlparse.urlunparse([urllib.quote(part) for part in urlparse.urlparse(uri)])
- return urlparse.urljoin(base, uri)
-
-class _FeedParserMixin:
- namespaces = {'': '',
- 'http://backend.userland.com/rss': '',
- 'http://blogs.law.harvard.edu/tech/rss': '',
- 'http://purl.org/rss/1.0/': '',
- 'http://my.netscape.com/rdf/simple/0.9/': '',
- 'http://example.com/newformat#': '',
- 'http://example.com/necho': '',
- 'http://purl.org/echo/': '',
- 'uri/of/echo/namespace#': '',
- 'http://purl.org/pie/': '',
- 'http://purl.org/atom/ns#': '',
- 'http://www.w3.org/2005/Atom': '',
- 'http://purl.org/rss/1.0/modules/rss091#': '',
-
- 'http://webns.net/mvcb/': 'admin',
- 'http://purl.org/rss/1.0/modules/aggregation/': 'ag',
- 'http://purl.org/rss/1.0/modules/annotate/': 'annotate',
- 'http://media.tangent.org/rss/1.0/': 'audio',
- 'http://backend.userland.com/blogChannelModule': 'blogChannel',
- 'http://web.resource.org/cc/': 'cc',
- 'http://backend.userland.com/creativeCommonsRssModule': 'creativeCommons',
- 'http://purl.org/rss/1.0/modules/company': 'co',
- 'http://purl.org/rss/1.0/modules/content/': 'content',
- 'http://my.theinfo.org/changed/1.0/rss/': 'cp',
- 'http://purl.org/dc/elements/1.1/': 'dc',
- 'http://purl.org/dc/terms/': 'dcterms',
- 'http://purl.org/rss/1.0/modules/email/': 'email',
- 'http://purl.org/rss/1.0/modules/event/': 'ev',
- 'http://rssnamespace.org/feedburner/ext/1.0': 'feedburner',
- 'http://freshmeat.net/rss/fm/': 'fm',
- 'http://xmlns.com/foaf/0.1/': 'foaf',
- 'http://www.w3.org/2003/01/geo/wgs84_pos#': 'geo',
- 'http://postneo.com/icbm/': 'icbm',
- 'http://purl.org/rss/1.0/modules/image/': 'image',
- 'http://www.itunes.com/DTDs/PodCast-1.0.dtd': 'itunes',
- 'http://example.com/DTDs/PodCast-1.0.dtd': 'itunes',
- 'http://purl.org/rss/1.0/modules/link/': 'l',
- 'http://search.yahoo.com/mrss': 'media',
- #Version 1.1.2 of the Media RSS spec added the trailing slash on the namespace
- 'http://search.yahoo.com/mrss/': 'media',
- 'http://madskills.com/public/xml/rss/module/pingback/': 'pingback',
- 'http://prismstandard.org/namespaces/1.2/basic/': 'prism',
- 'http://www.w3.org/1999/02/22-rdf-syntax-ns#': 'rdf',
- 'http://www.w3.org/2000/01/rdf-schema#': 'rdfs',
- 'http://purl.org/rss/1.0/modules/reference/': 'ref',
- 'http://purl.org/rss/1.0/modules/richequiv/': 'reqv',
- 'http://purl.org/rss/1.0/modules/search/': 'search',
- 'http://purl.org/rss/1.0/modules/slash/': 'slash',
- 'http://schemas.xmlsoap.org/soap/envelope/': 'soap',
- 'http://purl.org/rss/1.0/modules/servicestatus/': 'ss',
- 'http://hacks.benhammersley.com/rss/streaming/': 'str',
- 'http://purl.org/rss/1.0/modules/subscription/': 'sub',
- 'http://purl.org/rss/1.0/modules/syndication/': 'sy',
- 'http://schemas.pocketsoap.com/rss/myDescModule/': 'szf',
- 'http://purl.org/rss/1.0/modules/taxonomy/': 'taxo',
- 'http://purl.org/rss/1.0/modules/threading/': 'thr',
- 'http://purl.org/rss/1.0/modules/textinput/': 'ti',
- 'http://madskills.com/public/xml/rss/module/trackback/':'trackback',
- 'http://wellformedweb.org/commentAPI/': 'wfw',
- 'http://purl.org/rss/1.0/modules/wiki/': 'wiki',
- 'http://www.w3.org/1999/xhtml': 'xhtml',
- 'http://www.w3.org/1999/xlink': 'xlink',
- 'http://www.w3.org/XML/1998/namespace': 'xml'
-}
- _matchnamespaces = {}
-
- can_be_relative_uri = ['link', 'id', 'wfw_comment', 'wfw_commentrss', 'docs', 'url', 'href', 'comments', 'icon', 'logo']
- can_contain_relative_uris = ['content', 'title', 'summary', 'info', 'tagline', 'subtitle', 'copyright', 'rights', 'description']
- can_contain_dangerous_markup = ['content', 'title', 'summary', 'info', 'tagline', 'subtitle', 'copyright', 'rights', 'description']
- html_types = ['text/html', 'application/xhtml+xml']
-
- def __init__(self, baseuri=None, baselang=None, encoding='utf-8'):
- if _debug: sys.stderr.write('initializing FeedParser\n')
- if not self._matchnamespaces:
- for k, v in self.namespaces.items():
- self._matchnamespaces[k.lower()] = v
- self.feeddata = FeedParserDict() # feed-level data
- self.encoding = encoding # character encoding
- self.entries = [] # list of entry-level data
- self.version = '' # feed type/version, see SUPPORTED_VERSIONS
- self.namespacesInUse = {} # dictionary of namespaces defined by the feed
-
- # the following are used internally to track state;
- # this is really out of control and should be refactored
- self.infeed = 0
- self.inentry = 0
- self.incontent = 0
- self.intextinput = 0
- self.inimage = 0
- self.inauthor = 0
- self.incontributor = 0
- self.inpublisher = 0
- self.insource = 0
- self.sourcedata = FeedParserDict()
- self.contentparams = FeedParserDict()
- self._summaryKey = None
- self.namespacemap = {}
- self.elementstack = []
- self.basestack = []
- self.langstack = []
- self.baseuri = baseuri or ''
- self.lang = baselang or None
- self.svgOK = 0
- self.hasTitle = 0
- if baselang:
- self.feeddata['language'] = baselang.replace('_','-')
-
- def unknown_starttag(self, tag, attrs):
- if _debug: sys.stderr.write('start %s with %s\n' % (tag, attrs))
- # normalize attrs
- attrs = [(k.lower(), v) for k, v in attrs]
- attrs = [(k, k in ('rel', 'type') and v.lower() or v) for k, v in attrs]
- # the sgml parser doesn't handle entities in attributes, but
- # strict xml parsers do -- account for this difference
- if isinstance(self, _LooseFeedParser):
- attrs = [(k, v.replace('&amp;', '&')) for k, v in attrs]
-
- # track xml:base and xml:lang
- attrsD = dict(attrs)
- baseuri = attrsD.get('xml:base', attrsD.get('base')) or self.baseuri
- if type(baseuri) != type(u''):
- try:
- baseuri = unicode(baseuri, self.encoding)
- except:
- baseuri = unicode(baseuri, 'iso-8859-1')
- # ensure that self.baseuri is always an absolute URI that
- # uses a whitelisted URI scheme (e.g. not `javscript:`)
- if self.baseuri:
- self.baseuri = _makeSafeAbsoluteURI(self.baseuri, baseuri) or self.baseuri
- else:
- self.baseuri = _urljoin(self.baseuri, baseuri)
- lang = attrsD.get('xml:lang', attrsD.get('lang'))
- if lang == '':
- # xml:lang could be explicitly set to '', we need to capture that
- lang = None
- elif lang is None:
- # if no xml:lang is specified, use parent lang
- lang = self.lang
- if lang:
- if tag in ('feed', 'rss', 'rdf:RDF'):
- self.feeddata['language'] = lang.replace('_','-')
- self.lang = lang
- self.basestack.append(self.baseuri)
- self.langstack.append(lang)
-
- # track namespaces
- for prefix, uri in attrs:
- if prefix.startswith('xmlns:'):
- self.trackNamespace(prefix[6:], uri)
- elif prefix == 'xmlns':
- self.trackNamespace(None, uri)
-
- # track inline content
- if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
- if tag in ['xhtml:div', 'div']: return # typepad does this 10/2007
- # element declared itself as escaped markup, but it isn't really
- self.contentparams['type'] = 'application/xhtml+xml'
- if self.incontent and self.contentparams.get('type') == 'application/xhtml+xml':
- if tag.find(':') <> -1:
- prefix, tag = tag.split(':', 1)
- namespace = self.namespacesInUse.get(prefix, '')
- if tag=='math' and namespace=='http://www.w3.org/1998/Math/MathML':
- attrs.append(('xmlns',namespace))
- if tag=='svg' and namespace=='http://www.w3.org/2000/svg':
- attrs.append(('xmlns',namespace))
- if tag == 'svg': self.svgOK += 1
- return self.handle_data('<%s%s>' % (tag, self.strattrs(attrs)), escape=0)
-
- # match namespaces
- if tag.find(':') <> -1:
- prefix, suffix = tag.split(':', 1)
- else:
- prefix, suffix = '', tag
- prefix = self.namespacemap.get(prefix, prefix)
- if prefix:
- prefix = prefix + '_'
-
- # special hack for better tracking of empty textinput/image elements in illformed feeds
- if (not prefix) and tag not in ('title', 'link', 'description', 'name'):
- self.intextinput = 0
- if (not prefix) and tag not in ('title', 'link', 'description', 'url', 'href', 'width', 'height'):
- self.inimage = 0
-
- # call special handler (if defined) or default handler
- methodname = '_start_' + prefix + suffix
- try:
- method = getattr(self, methodname)
- return method(attrsD)
- except AttributeError:
- # Since there's no handler or something has gone wrong we explicitly add the element and its attributes
- unknown_tag = prefix + suffix
- if len(attrsD) == 0:
- # No attributes so merge it into the encosing dictionary
- return self.push(unknown_tag, 1)
- else:
- # Has attributes so create it in its own dictionary
- context = self._getContext()
- context[unknown_tag] = attrsD
-
- def unknown_endtag(self, tag):
- if _debug: sys.stderr.write('end %s\n' % tag)
- # match namespaces
- if tag.find(':') <> -1:
- prefix, suffix = tag.split(':', 1)
- else:
- prefix, suffix = '', tag
- prefix = self.namespacemap.get(prefix, prefix)
- if prefix:
- prefix = prefix + '_'
- if suffix == 'svg' and self.svgOK: self.svgOK -= 1
-
- # call special handler (if defined) or default handler
- methodname = '_end_' + prefix + suffix
- try:
- if self.svgOK: raise AttributeError()
- method = getattr(self, methodname)
- method()
- except AttributeError:
- self.pop(prefix + suffix)
-
- # track inline content
- if self.incontent and self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
- # element declared itself as escaped markup, but it isn't really
- if tag in ['xhtml:div', 'div']: return # typepad does this 10/2007
- self.contentparams['type'] = 'application/xhtml+xml'
- if self.incontent and self.contentparams.get('type') == 'application/xhtml+xml':
- tag = tag.split(':')[-1]
- self.handle_data('</%s>' % tag, escape=0)
-
- # track xml:base and xml:lang going out of scope
- if self.basestack:
- self.basestack.pop()
- if self.basestack and self.basestack[-1]:
- self.baseuri = self.basestack[-1]
- if self.langstack:
- self.langstack.pop()
- if self.langstack: # and (self.langstack[-1] is not None):
- self.lang = self.langstack[-1]
-
- def handle_charref(self, ref):
- # called for each character reference, e.g. for '&#160;', ref will be '160'
- if not self.elementstack: return
- ref = ref.lower()
- if ref in ('34', '38', '39', '60', '62', 'x22', 'x26', 'x27', 'x3c', 'x3e'):
- text = '&#%s;' % ref
- else:
- if ref[0] == 'x':
- c = int(ref[1:], 16)
- else:
- c = int(ref)
- text = unichr(c).encode('utf-8')
- self.elementstack[-1][2].append(text)
-
- def handle_entityref(self, ref):
- # called for each entity reference, e.g. for '&copy;', ref will be 'copy'
- if not self.elementstack: return
- if _debug: sys.stderr.write('entering handle_entityref with %s\n' % ref)
- if ref in ('lt', 'gt', 'quot', 'amp', 'apos'):
- text = '&%s;' % ref
- elif ref in self.entities.keys():
- text = self.entities[ref]
- if text.startswith('&#') and text.endswith(';'):
- return self.handle_entityref(text)
- else:
- try: name2codepoint[ref]
- except KeyError: text = '&%s;' % ref
- else: text = unichr(name2codepoint[ref]).encode('utf-8')
- self.elementstack[-1][2].append(text)
-
- def handle_data(self, text, escape=1):
- # called for each block of plain text, i.e. outside of any tag and
- # not containing any character or entity references
- if not self.elementstack: return
- if escape and self.contentparams.get('type') == 'application/xhtml+xml':
- text = _xmlescape(text)
- self.elementstack[-1][2].append(text)
-
- def handle_comment(self, text):
- # called for each comment, e.g. <!-- insert message here -->
- pass
-
- def handle_pi(self, text):
- # called for each processing instruction, e.g. <?instruction>
- pass
-
- def handle_decl(self, text):
- pass
-
- def parse_declaration(self, i):
- # override internal declaration handler to handle CDATA blocks
- if _debug: sys.stderr.write('entering parse_declaration\n')
- if self.rawdata[i:i+9] == '<![CDATA[':
- k = self.rawdata.find(']]>', i)
- if k == -1:
- # CDATA block began but didn't finish
- k = len(self.rawdata)
- return k
- self.handle_data(_xmlescape(self.rawdata[i+9:k]), 0)
- return k+3
- else:
- k = self.rawdata.find('>', i)
- if k >= 0:
- return k+1
- else:
- # We have an incomplete CDATA block.
- return k
-
- def mapContentType(self, contentType):
- contentType = contentType.lower()
- if contentType == 'text':
- contentType = 'text/plain'
- elif contentType == 'html':
- contentType = 'text/html'
- elif contentType == 'xhtml':
- contentType = 'application/xhtml+xml'
- return contentType
-
- def trackNamespace(self, prefix, uri):
- loweruri = uri.lower()
- if (prefix, loweruri) == (None, 'http://my.netscape.com/rdf/simple/0.9/') and not self.version:
- self.version = 'rss090'
- if loweruri == 'http://purl.org/rss/1.0/' and not self.version:
- self.version = 'rss10'
- if loweruri == 'http://www.w3.org/2005/atom' and not self.version:
- self.version = 'atom10'
- if loweruri.find('backend.userland.com/rss') <> -1:
- # match any backend.userland.com namespace
- uri = 'http://backend.userland.com/rss'
- loweruri = uri
- if self._matchnamespaces.has_key(loweruri):
- self.namespacemap[prefix] = self._matchnamespaces[loweruri]
- self.namespacesInUse[self._matchnamespaces[loweruri]] = uri
- else:
- self.namespacesInUse[prefix or ''] = uri
-
- def resolveURI(self, uri):
- return _urljoin(self.baseuri or '', uri)
-
- def decodeEntities(self, element, data):
- return data
-
- def strattrs(self, attrs):
- return ''.join([' %s="%s"' % (t[0],_xmlescape(t[1],{'"':'&quot;'})) for t in attrs])
-
- def push(self, element, expectingText):
- self.elementstack.append([element, expectingText, []])
-
- def pop(self, element, stripWhitespace=1):
- if not self.elementstack: return
- if self.elementstack[-1][0] != element: return
-
- element, expectingText, pieces = self.elementstack.pop()
-
- if self.version == 'atom10' and self.contentparams.get('type','text') == 'application/xhtml+xml':
- # remove enclosing child element, but only if it is a <div> and
- # only if all the remaining content is nested underneath it.
- # This means that the divs would be retained in the following:
- # <div>foo</div><div>bar</div>
- while pieces and len(pieces)>1 and not pieces[-1].strip():
- del pieces[-1]
- while pieces and len(pieces)>1 and not pieces[0].strip():
- del pieces[0]
- if pieces and (pieces[0] == '<div>' or pieces[0].startswith('<div ')) and pieces[-1]=='</div>':
- depth = 0
- for piece in pieces[:-1]:
- if piece.startswith('</'):
- depth -= 1
- if depth == 0: break
- elif piece.startswith('<') and not piece.endswith('/>'):
- depth += 1
- else:
- pieces = pieces[1:-1]
-
- # Ensure each piece is a str for Python 3
- for (i, v) in enumerate(pieces):
- if not isinstance(v, basestring):
- pieces[i] = v.decode('utf-8')
-
- output = ''.join(pieces)
- if stripWhitespace:
- output = output.strip()
- if not expectingText: return output
-
- # decode base64 content
- if base64 and self.contentparams.get('base64', 0):
- try:
- output = _base64decode(output)
- except binascii.Error:
- pass
- except binascii.Incomplete:
- pass
- except TypeError:
- # In Python 3, base64 takes and outputs bytes, not str
- # This may not be the most correct way to accomplish this
- output = _base64decode(output.encode('utf-8')).decode('utf-8')
-
- # resolve relative URIs
- if (element in self.can_be_relative_uri) and output:
- output = self.resolveURI(output)
-
- # decode entities within embedded markup
- if not self.contentparams.get('base64', 0):
- output = self.decodeEntities(element, output)
-
- if self.lookslikehtml(output):
- self.contentparams['type']='text/html'
-
- # remove temporary cruft from contentparams
- try:
- del self.contentparams['mode']
- except KeyError:
- pass
- try:
- del self.contentparams['base64']
- except KeyError:
- pass
-
- is_htmlish = self.mapContentType(self.contentparams.get('type', 'text/html')) in self.html_types
- # resolve relative URIs within embedded markup
- if is_htmlish and RESOLVE_RELATIVE_URIS:
- if element in self.can_contain_relative_uris:
- output = _resolveRelativeURIs(output, self.baseuri, self.encoding, self.contentparams.get('type', 'text/html'))
-
- # parse microformats
- # (must do this before sanitizing because some microformats
- # rely on elements that we sanitize)
- if is_htmlish and element in ['content', 'description', 'summary']:
- mfresults = _parseMicroformats(output, self.baseuri, self.encoding)
- if mfresults:
- for tag in mfresults.get('tags', []):
- self._addTag(tag['term'], tag['scheme'], tag['label'])
- for enclosure in mfresults.get('enclosures', []):
- self._start_enclosure(enclosure)
- for xfn in mfresults.get('xfn', []):
- self._addXFN(xfn['relationships'], xfn['href'], xfn['name'])
- vcard = mfresults.get('vcard')
- if vcard:
- self._getContext()['vcard'] = vcard
-
- # sanitize embedded markup
- if is_htmlish and SANITIZE_HTML:
- if element in self.can_contain_dangerous_markup:
- output = _sanitizeHTML(output, self.encoding, self.contentparams.get('type', 'text/html'))
-
- if self.encoding and type(output) != type(u''):
- try:
- output = unicode(output, self.encoding)
- except:
- pass
-
- # address common error where people take data that is already
- # utf-8, presume that it is iso-8859-1, and re-encode it.
- if self.encoding in ('utf-8', 'utf-8_INVALID_PYTHON_3') and type(output) == type(u''):
- try:
- output = unicode(output.encode('iso-8859-1'), 'utf-8')
- except:
- pass
-
- # map win-1252 extensions to the proper code points
- if type(output) == type(u''):
- output = u''.join([c in _cp1252.keys() and _cp1252[c] or c for c in output])
-
- # categories/tags/keywords/whatever are handled in _end_category
- if element == 'category':
- return output
-
- if element == 'title' and self.hasTitle:
- return output
-
- # store output in appropriate place(s)
- if self.inentry and not self.insource:
- if element == 'content':
- self.entries[-1].setdefault(element, [])
- contentparams = copy.deepcopy(self.contentparams)
- contentparams['value'] = output
- self.entries[-1][element].append(contentparams)
- elif element == 'link':
- if not self.inimage:
- # query variables in urls in link elements are improperly
- # converted from `?a=1&b=2` to `?a=1&b;=2` as if they're
- # unhandled character references. fix this special case.
- output = re.sub("&([A-Za-z0-9_]+);", "&\g<1>", output)
- self.entries[-1][element] = output
- if output:
- self.entries[-1]['links'][-1]['href'] = output
- else:
- if element == 'description':
- element = 'summary'
- self.entries[-1][element] = output
- if self.incontent:
- contentparams = copy.deepcopy(self.contentparams)
- contentparams['value'] = output
- self.entries[-1][element + '_detail'] = contentparams
- elif (self.infeed or self.insource):# and (not self.intextinput) and (not self.inimage):
- context = self._getContext()
- if element == 'description':
- element = 'subtitle'
- context[element] = output
- if element == 'link':
- # fix query variables; see above for the explanation
- output = re.sub("&([A-Za-z0-9_]+);", "&\g<1>", output)
- context[element] = output
- context['links'][-1]['href'] = output
- elif self.incontent:
- contentparams = copy.deepcopy(self.contentparams)
- contentparams['value'] = output
- context[element + '_detail'] = contentparams
- return output
-
- def pushContent(self, tag, attrsD, defaultContentType, expectingText):
- self.incontent += 1
- if self.lang: self.lang=self.lang.replace('_','-')
- self.contentparams = FeedParserDict({
- 'type': self.mapContentType(attrsD.get('type', defaultContentType)),
- 'language': self.lang,
- 'base': self.baseuri})
- self.contentparams['base64'] = self._isBase64(attrsD, self.contentparams)
- self.push(tag, expectingText)
-
- def popContent(self, tag):
- value = self.pop(tag)
- self.incontent -= 1
- self.contentparams.clear()
- return value
-
- # a number of elements in a number of RSS variants are nominally plain
- # text, but this is routinely ignored. This is an attempt to detect
- # the most common cases. As false positives often result in silent
- # data loss, this function errs on the conservative side.
- def lookslikehtml(self, s):
- if self.version.startswith('atom'): return
- if self.contentparams.get('type','text/html') != 'text/plain': return
-
- # must have a close tag or a entity reference to qualify
- if not (re.search(r'</(\w+)>',s) or re.search("&#?\w+;",s)): return
-
- # all tags must be in a restricted subset of valid HTML tags
- if filter(lambda t: t.lower() not in _HTMLSanitizer.acceptable_elements,
- re.findall(r'</?(\w+)',s)): return
-
- # all entities must have been defined as valid HTML entities
- from htmlentitydefs import entitydefs
- if filter(lambda e: e not in entitydefs.keys(),
- re.findall(r'&(\w+);',s)): return
-
- return 1
-
- def _mapToStandardPrefix(self, name):
- colonpos = name.find(':')
- if colonpos <> -1:
- prefix = name[:colonpos]
- suffix = name[colonpos+1:]
- prefix = self.namespacemap.get(prefix, prefix)
- name = prefix + ':' + suffix
- return name
-
- def _getAttribute(self, attrsD, name):
- return attrsD.get(self._mapToStandardPrefix(name))
-
- def _isBase64(self, attrsD, contentparams):
- if attrsD.get('mode', '') == 'base64':
- return 1
- if self.contentparams['type'].startswith('text/'):
- return 0
- if self.contentparams['type'].endswith('+xml'):
- return 0
- if self.contentparams['type'].endswith('/xml'):
- return 0
- return 1
-
- def _itsAnHrefDamnIt(self, attrsD):
- href = attrsD.get('url', attrsD.get('uri', attrsD.get('href', None)))
- if href:
- try:
- del attrsD['url']
- except KeyError:
- pass
- try:
- del attrsD['uri']
- except KeyError:
- pass
- attrsD['href'] = href
- return attrsD
-
- def _save(self, key, value, overwrite=False):
- context = self._getContext()
- if overwrite:
- context[key] = value
- else:
- context.setdefault(key, value)
-
- def _start_rss(self, attrsD):
- versionmap = {'0.91': 'rss091u',
- '0.92': 'rss092',
- '0.93': 'rss093',
- '0.94': 'rss094'}
- #If we're here then this is an RSS feed.
- #If we don't have a version or have a version that starts with something
- #other than RSS then there's been a mistake. Correct it.
- if not self.version or not self.version.startswith('rss'):
- attr_version = attrsD.get('version', '')
- version = versionmap.get(attr_version)
- if version:
- self.version = version
- elif attr_version.startswith('2.'):
- self.version = 'rss20'
- else:
- self.version = 'rss'
-
- def _start_dlhottitles(self, attrsD):
- self.version = 'hotrss'
-
- def _start_channel(self, attrsD):
- self.infeed = 1
- self._cdf_common(attrsD)
- _start_feedinfo = _start_channel
-
- def _cdf_common(self, attrsD):
- if attrsD.has_key('lastmod'):
- self._start_modified({})
- self.elementstack[-1][-1] = attrsD['lastmod']
- self._end_modified()
- if attrsD.has_key('href'):
- self._start_link({})
- self.elementstack[-1][-1] = attrsD['href']
- self._end_link()
-
- def _start_feed(self, attrsD):
- self.infeed = 1
- versionmap = {'0.1': 'atom01',
- '0.2': 'atom02',
- '0.3': 'atom03'}
- if not self.version:
- attr_version = attrsD.get('version')
- version = versionmap.get(attr_version)
- if version:
- self.version = version
- else:
- self.version = 'atom'
-
- def _end_channel(self):
- self.infeed = 0
- _end_feed = _end_channel
-
- def _start_image(self, attrsD):
- context = self._getContext()
- if not self.inentry:
- context.setdefault('image', FeedParserDict())
- self.inimage = 1
- self.hasTitle = 0
- self.push('image', 0)
-
- def _end_image(self):
- self.pop('image')
- self.inimage = 0
-
- def _start_textinput(self, attrsD):
- context = self._getContext()
- context.setdefault('textinput', FeedParserDict())
- self.intextinput = 1
- self.hasTitle = 0
- self.push('textinput', 0)
- _start_textInput = _start_textinput
-
- def _end_textinput(self):
- self.pop('textinput')
- self.intextinput = 0
- _end_textInput = _end_textinput
-
- def _start_author(self, attrsD):
- self.inauthor = 1
- self.push('author', 1)
- # Append a new FeedParserDict when expecting an author
- context = self._getContext()
- context.setdefault('authors', [])
- context['authors'].append(FeedParserDict())
- _start_managingeditor = _start_author
- _start_dc_author = _start_author
- _start_dc_creator = _start_author
- _start_itunes_author = _start_author
-
- def _end_author(self):
- self.pop('author')
- self.inauthor = 0
- self._sync_author_detail()
- _end_managingeditor = _end_author
- _end_dc_author = _end_author
- _end_dc_creator = _end_author
- _end_itunes_author = _end_author
-
- def _start_itunes_owner(self, attrsD):
- self.inpublisher = 1
- self.push('publisher', 0)
-
- def _end_itunes_owner(self):
- self.pop('publisher')
- self.inpublisher = 0
- self._sync_author_detail('publisher')
-
- def _start_contributor(self, attrsD):
- self.incontributor = 1
- context = self._getContext()
- context.setdefault('contributors', [])
- context['contributors'].append(FeedParserDict())
- self.push('contributor', 0)
-
- def _end_contributor(self):
- self.pop('contributor')
- self.incontributor = 0
-
- def _start_dc_contributor(self, attrsD):
- self.incontributor = 1
- context = self._getContext()
- context.setdefault('contributors', [])
- context['contributors'].append(FeedParserDict())
- self.push('name', 0)
-
- def _end_dc_contributor(self):
- self._end_name()
- self.incontributor = 0
-
- def _start_name(self, attrsD):
- self.push('name', 0)
- _start_itunes_name = _start_name
-
- def _end_name(self):
- value = self.pop('name')
- if self.inpublisher:
- self._save_author('name', value, 'publisher')
- elif self.inauthor:
- self._save_author('name', value)
- elif self.incontributor:
- self._save_contributor('name', value)
- elif self.intextinput:
- context = self._getContext()
- context['name'] = value
- _end_itunes_name = _end_name
-
- def _start_width(self, attrsD):
- self.push('width', 0)
-
- def _end_width(self):
- value = self.pop('width')
- try:
- value = int(value)
- except:
- value = 0
- if self.inimage:
- context = self._getContext()
- context['width'] = value
-
- def _start_height(self, attrsD):
- self.push('height', 0)
-
- def _end_height(self):
- value = self.pop('height')
- try:
- value = int(value)
- except:
- value = 0
- if self.inimage:
- context = self._getContext()
- context['height'] = value
-
- def _start_url(self, attrsD):
- self.push('href', 1)
- _start_homepage = _start_url
- _start_uri = _start_url
-
- def _end_url(self):
- value = self.pop('href')
- if self.inauthor:
- self._save_author('href', value)
- elif self.incontributor:
- self._save_contributor('href', value)
- _end_homepage = _end_url
- _end_uri = _end_url
-
- def _start_email(self, attrsD):
- self.push('email', 0)
- _start_itunes_email = _start_email
-
- def _end_email(self):
- value = self.pop('email')
- if self.inpublisher:
- self._save_author('email', value, 'publisher')
- elif self.inauthor:
- self._save_author('email', value)
- elif self.incontributor:
- self._save_contributor('email', value)
- _end_itunes_email = _end_email
-
- def _getContext(self):
- if self.insource:
- context = self.sourcedata
- elif self.inimage and self.feeddata.has_key('image'):
- context = self.feeddata['image']
- elif self.intextinput:
- context = self.feeddata['textinput']
- elif self.inentry:
- context = self.entries[-1]
- else:
- context = self.feeddata
- return context
-
- def _save_author(self, key, value, prefix='author'):
- context = self._getContext()
- context.setdefault(prefix + '_detail', FeedParserDict())
- context[prefix + '_detail'][key] = value
- self._sync_author_detail()
- context.setdefault('authors', [FeedParserDict()])
- context['authors'][-1][key] = value
-
- def _save_contributor(self, key, value):
- context = self._getContext()
- context.setdefault('contributors', [FeedParserDict()])
- context['contributors'][-1][key] = value
-
- def _sync_author_detail(self, key='author'):
- context = self._getContext()
- detail = context.get('%s_detail' % key)
- if detail:
- name = detail.get('name')
- email = detail.get('email')
- if name and email:
- context[key] = '%s (%s)' % (name, email)
- elif name:
- context[key] = name
- elif email:
- context[key] = email
- else:
- author, email = context.get(key), None
- if not author: return
- emailmatch = re.search(r'''(([a-zA-Z0-9\_\-\.\+]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?))(\?subject=\S+)?''', author)
- if emailmatch:
- email = emailmatch.group(0)
- # probably a better way to do the following, but it passes all the tests
- author = author.replace(email, '')
- author = author.replace('()', '')
- author = author.replace('<>', '')
- author = author.replace('&lt;&gt;', '')
- author = author.strip()
- if author and (author[0] == '('):
- author = author[1:]
- if author and (author[-1] == ')'):
- author = author[:-1]
- author = author.strip()
- if author or email:
- context.setdefault('%s_detail' % key, FeedParserDict())
- if author:
- context['%s_detail' % key]['name'] = author
- if email:
- context['%s_detail' % key]['email'] = email
-
- def _start_subtitle(self, attrsD):
- self.pushContent('subtitle', attrsD, 'text/plain', 1)
- _start_tagline = _start_subtitle
- _start_itunes_subtitle = _start_subtitle
-
- def _end_subtitle(self):
- self.popContent('subtitle')
- _end_tagline = _end_subtitle
- _end_itunes_subtitle = _end_subtitle
-
- def _start_rights(self, attrsD):
- self.pushContent('rights', attrsD, 'text/plain', 1)
- _start_dc_rights = _start_rights
- _start_copyright = _start_rights
-
- def _end_rights(self):
- self.popContent('rights')
- _end_dc_rights = _end_rights
- _end_copyright = _end_rights
-
- def _start_item(self, attrsD):
- self.entries.append(FeedParserDict())
- self.push('item', 0)
- self.inentry = 1
- self.guidislink = 0
- self.hasTitle = 0
- id = self._getAttribute(attrsD, 'rdf:about')
- if id:
- context = self._getContext()
- context['id'] = id
- self._cdf_common(attrsD)
- _start_entry = _start_item
- _start_product = _start_item
-
- def _end_item(self):
- self.pop('item')
- self.inentry = 0
- _end_entry = _end_item
-
- def _start_dc_language(self, attrsD):
- self.push('language', 1)
- _start_language = _start_dc_language
-
- def _end_dc_language(self):
- self.lang = self.pop('language')
- _end_language = _end_dc_language
-
- def _start_dc_publisher(self, attrsD):
- self.push('publisher', 1)
- _start_webmaster = _start_dc_publisher
-
- def _end_dc_publisher(self):
- self.pop('publisher')
- self._sync_author_detail('publisher')
- _end_webmaster = _end_dc_publisher
-
- def _start_published(self, attrsD):
- self.push('published', 1)
- _start_dcterms_issued = _start_published
- _start_issued = _start_published
-
- def _end_published(self):
- value = self.pop('published')
- self._save('published_parsed', _parse_date(value), overwrite=True)
- _end_dcterms_issued = _end_published
- _end_issued = _end_published
-
- def _start_updated(self, attrsD):
- self.push('updated', 1)
- _start_modified = _start_updated
- _start_dcterms_modified = _start_updated
- _start_pubdate = _start_updated
- _start_dc_date = _start_updated
- _start_lastbuilddate = _start_updated
-
- def _end_updated(self):
- value = self.pop('updated')
- parsed_value = _parse_date(value)
- self._save('updated_parsed', parsed_value, overwrite=True)
- _end_modified = _end_updated
- _end_dcterms_modified = _end_updated
- _end_pubdate = _end_updated
- _end_dc_date = _end_updated
- _end_lastbuilddate = _end_updated
-
- def _start_created(self, attrsD):
- self.push('created', 1)
- _start_dcterms_created = _start_created
-
- def _end_created(self):
- value = self.pop('created')
- self._save('created_parsed', _parse_date(value), overwrite=True)
- _end_dcterms_created = _end_created
-
- def _start_expirationdate(self, attrsD):
- self.push('expired', 1)
-
- def _end_expirationdate(self):
- self._save('expired_parsed', _parse_date(self.pop('expired')), overwrite=True)
-
- def _start_cc_license(self, attrsD):
- context = self._getContext()
- value = self._getAttribute(attrsD, 'rdf:resource')
- attrsD = FeedParserDict()
- attrsD['rel']='license'
- if value: attrsD['href']=value
- context.setdefault('links', []).append(attrsD)
-
- def _start_creativecommons_license(self, attrsD):
- self.push('license', 1)
- _start_creativeCommons_license = _start_creativecommons_license
-
- def _end_creativecommons_license(self):
- value = self.pop('license')
- context = self._getContext()
- attrsD = FeedParserDict()
- attrsD['rel']='license'
- if value: attrsD['href']=value
- context.setdefault('links', []).append(attrsD)
- del context['license']
- _end_creativeCommons_license = _end_creativecommons_license
-
- def _addXFN(self, relationships, href, name):
- context = self._getContext()
- xfn = context.setdefault('xfn', [])
- value = FeedParserDict({'relationships': relationships, 'href': href, 'name': name})
- if value not in xfn:
- xfn.append(value)
-
- def _addTag(self, term, scheme, label):
- context = self._getContext()
- tags = context.setdefault('tags', [])
- if (not term) and (not scheme) and (not label): return
- value = FeedParserDict({'term': term, 'scheme': scheme, 'label': label})
- if value not in tags:
- tags.append(value)
-
- def _start_category(self, attrsD):
- if _debug: sys.stderr.write('entering _start_category with %s\n' % repr(attrsD))
- term = attrsD.get('term')
- scheme = attrsD.get('scheme', attrsD.get('domain'))
- label = attrsD.get('label')
- self._addTag(term, scheme, label)
- self.push('category', 1)
- _start_dc_subject = _start_category
- _start_keywords = _start_category
-
- def _start_media_category(self, attrsD):
- attrsD.setdefault('scheme', 'http://search.yahoo.com/mrss/category_schema')
- self._start_category(attrsD)
-
- def _end_itunes_keywords(self):
- for term in self.pop('itunes_keywords').split():
- self._addTag(term, 'http://www.itunes.com/', None)
-
- def _start_itunes_category(self, attrsD):
- self._addTag(attrsD.get('text'), 'http://www.itunes.com/', None)
- self.push('category', 1)
-
- def _end_category(self):
- value = self.pop('category')
- if not value: return
- context = self._getContext()
- tags = context['tags']
- if value and len(tags) and not tags[-1]['term']:
- tags[-1]['term'] = value
- else:
- self._addTag(value, None, None)
- _end_dc_subject = _end_category
- _end_keywords = _end_category
- _end_itunes_category = _end_category
- _end_media_category = _end_category
-
- def _start_cloud(self, attrsD):
- self._getContext()['cloud'] = FeedParserDict(attrsD)
-
- def _start_link(self, attrsD):
- attrsD.setdefault('rel', 'alternate')
- if attrsD['rel'] == 'self':
- attrsD.setdefault('type', 'application/atom+xml')
- else:
- attrsD.setdefault('type', 'text/html')
- context = self._getContext()
- attrsD = self._itsAnHrefDamnIt(attrsD)
- if attrsD.has_key('href'):
- attrsD['href'] = self.resolveURI(attrsD['href'])
- expectingText = self.infeed or self.inentry or self.insource
- context.setdefault('links', [])
- if not (self.inentry and self.inimage):
- context['links'].append(FeedParserDict(attrsD))
- if attrsD.has_key('href'):
- expectingText = 0
- if (attrsD.get('rel') == 'alternate') and (self.mapContentType(attrsD.get('type')) in self.html_types):
- context['link'] = attrsD['href']
- else:
- self.push('link', expectingText)
- _start_producturl = _start_link
-
- def _end_link(self):
- value = self.pop('link')
- context = self._getContext()
- _end_producturl = _end_link
-
- def _start_guid(self, attrsD):
- self.guidislink = (attrsD.get('ispermalink', 'true') == 'true')
- self.push('id', 1)
-
- def _end_guid(self):
- value = self.pop('id')
- self._save('guidislink', self.guidislink and not self._getContext().has_key('link'))
- if self.guidislink:
- # guid acts as link, but only if 'ispermalink' is not present or is 'true',
- # and only if the item doesn't already have a link element
- self._save('link', value)
-
- def _start_title(self, attrsD):
- if self.svgOK: return self.unknown_starttag('title', attrsD.items())
- self.pushContent('title', attrsD, 'text/plain', self.infeed or self.inentry or self.insource)
- _start_dc_title = _start_title
- _start_media_title = _start_title
-
- def _end_title(self):
- if self.svgOK: return
- value = self.popContent('title')
- if not value: return
- context = self._getContext()
- self.hasTitle = 1
- _end_dc_title = _end_title
-
- def _end_media_title(self):
- hasTitle = self.hasTitle
- self._end_title()
- self.hasTitle = hasTitle
-
- def _start_description(self, attrsD):
- context = self._getContext()
- if context.has_key('summary'):
- self._summaryKey = 'content'
- self._start_content(attrsD)
- else:
- self.pushContent('description', attrsD, 'text/html', self.infeed or self.inentry or self.insource)
- _start_dc_description = _start_description
-
- def _start_abstract(self, attrsD):
- self.pushContent('description', attrsD, 'text/plain', self.infeed or self.inentry or self.insource)
-
- def _end_description(self):
- if self._summaryKey == 'content':
- self._end_content()
- else:
- value = self.popContent('description')
- self._summaryKey = None
- _end_abstract = _end_description
- _end_dc_description = _end_description
-
- def _start_info(self, attrsD):
- self.pushContent('info', attrsD, 'text/plain', 1)
- _start_feedburner_browserfriendly = _start_info
-
- def _end_info(self):
- self.popContent('info')
- _end_feedburner_browserfriendly = _end_info
-
- def _start_generator(self, attrsD):
- if attrsD:
- attrsD = self._itsAnHrefDamnIt(attrsD)
- if attrsD.has_key('href'):
- attrsD['href'] = self.resolveURI(attrsD['href'])
- self._getContext()['generator_detail'] = FeedParserDict(attrsD)
- self.push('generator', 1)
-
- def _end_generator(self):
- value = self.pop('generator')
- context = self._getContext()
- if context.has_key('generator_detail'):
- context['generator_detail']['name'] = value
-
- def _start_admin_generatoragent(self, attrsD):
- self.push('generator', 1)
- value = self._getAttribute(attrsD, 'rdf:resource')
- if value:
- self.elementstack[-1][2].append(value)
- self.pop('generator')
- self._getContext()['generator_detail'] = FeedParserDict({'href': value})
-
- def _start_admin_errorreportsto(self, attrsD):
- self.push('errorreportsto', 1)
- value = self._getAttribute(attrsD, 'rdf:resource')
- if value:
- self.elementstack[-1][2].append(value)
- self.pop('errorreportsto')
-
- def _start_summary(self, attrsD):
- context = self._getContext()
- if context.has_key('summary'):
- self._summaryKey = 'content'
- self._start_content(attrsD)
- else:
- self._summaryKey = 'summary'
- self.pushContent(self._summaryKey, attrsD, 'text/plain', 1)
- _start_itunes_summary = _start_summary
-
- def _end_summary(self):
- if self._summaryKey == 'content':
- self._end_content()
- else:
- self.popContent(self._summaryKey or 'summary')
- self._summaryKey = None
- _end_itunes_summary = _end_summary
-
- def _start_enclosure(self, attrsD):
- attrsD = self._itsAnHrefDamnIt(attrsD)
- context = self._getContext()
- attrsD['rel']='enclosure'
- context.setdefault('links', []).append(FeedParserDict(attrsD))
-
- def _start_source(self, attrsD):
- if 'url' in attrsD:
- # This means that we're processing a source element from an RSS 2.0 feed
- self.sourcedata['href'] = attrsD[u'url']
- self.push('source', 1)
- self.insource = 1
- self.hasTitle = 0
-
- def _end_source(self):
- self.insource = 0
- value = self.pop('source')
- if value:
- self.sourcedata['title'] = value
- self._getContext()['source'] = copy.deepcopy(self.sourcedata)
- self.sourcedata.clear()
-
- def _start_content(self, attrsD):
- self.pushContent('content', attrsD, 'text/plain', 1)
- src = attrsD.get('src')
- if src:
- self.contentparams['src'] = src
- self.push('content', 1)
-
- def _start_prodlink(self, attrsD):
- self.pushContent('content', attrsD, 'text/html', 1)
-
- def _start_body(self, attrsD):
- self.pushContent('content', attrsD, 'application/xhtml+xml', 1)
- _start_xhtml_body = _start_body
-
- def _start_content_encoded(self, attrsD):
- self.pushContent('content', attrsD, 'text/html', 1)
- _start_fullitem = _start_content_encoded
-
- def _end_content(self):
- copyToSummary = self.mapContentType(self.contentparams.get('type')) in (['text/plain'] + self.html_types)
- value = self.popContent('content')
- if copyToSummary:
- self._save('summary', value)
-
- _end_body = _end_content
- _end_xhtml_body = _end_content
- _end_content_encoded = _end_content
- _end_fullitem = _end_content
- _end_prodlink = _end_content
-
- def _start_itunes_image(self, attrsD):
- self.push('itunes_image', 0)
- if attrsD.get('href'):
- self._getContext()['image'] = FeedParserDict({'href': attrsD.get('href')})
- _start_itunes_link = _start_itunes_image
-
- def _end_itunes_block(self):
- value = self.pop('itunes_block', 0)
- self._getContext()['itunes_block'] = (value == 'yes') and 1 or 0
-
- def _end_itunes_explicit(self):
- value = self.pop('itunes_explicit', 0)
- # Convert 'yes' -> True, 'clean' to False, and any other value to None
- # False and None both evaluate as False, so the difference can be ignored
- # by applications that only need to know if the content is explicit.
- self._getContext()['itunes_explicit'] = (None, False, True)[(value == 'yes' and 2) or value == 'clean' or 0]
-
- def _start_media_content(self, attrsD):
- context = self._getContext()
- context.setdefault('media_content', [])
- context['media_content'].append(attrsD)
-
- def _start_media_thumbnail(self, attrsD):
- context = self._getContext()
- context.setdefault('media_thumbnail', [])
- self.push('url', 1) # new
- context['media_thumbnail'].append(attrsD)
-
- def _end_media_thumbnail(self):
- url = self.pop('url')
- context = self._getContext()
- if url is not None and len(url.strip()) != 0:
- if not context['media_thumbnail'][-1].has_key('url'):
- context['media_thumbnail'][-1]['url'] = url
-
- def _start_media_player(self, attrsD):
- self.push('media_player', 0)
- self._getContext()['media_player'] = FeedParserDict(attrsD)
-
- def _end_media_player(self):
- value = self.pop('media_player')
- context = self._getContext()
- context['media_player']['content'] = value
-
- def _start_newlocation(self, attrsD):
- self.push('newlocation', 1)
-
- def _end_newlocation(self):
- url = self.pop('newlocation')
- context = self._getContext()
- # don't set newlocation if the context isn't right
- if context is not self.feeddata:
- return
- context['newlocation'] = _makeSafeAbsoluteURI(self.baseuri, url.strip())
-
-if _XML_AVAILABLE:
- class _StrictFeedParser(_FeedParserMixin, xml.sax.handler.ContentHandler):
- def __init__(self, baseuri, baselang, encoding):
- if _debug: sys.stderr.write('trying StrictFeedParser\n')
- xml.sax.handler.ContentHandler.__init__(self)
- _FeedParserMixin.__init__(self, baseuri, baselang, encoding)
- self.bozo = 0
- self.exc = None
- self.decls = {}
-
- def startPrefixMapping(self, prefix, uri):
- self.trackNamespace(prefix, uri)
- if uri == 'http://www.w3.org/1999/xlink':
- self.decls['xmlns:'+prefix] = uri
-
- def startElementNS(self, name, qname, attrs):
- namespace, localname = name
- lowernamespace = str(namespace or '').lower()
- if lowernamespace.find('backend.userland.com/rss') <> -1:
- # match any backend.userland.com namespace
- namespace = 'http://backend.userland.com/rss'
- lowernamespace = namespace
- if qname and qname.find(':') > 0:
- givenprefix = qname.split(':')[0]
- else:
- givenprefix = None
- prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
- if givenprefix and (prefix is None or (prefix == '' and lowernamespace == '')) and not self.namespacesInUse.has_key(givenprefix):
- raise UndeclaredNamespace, "'%s' is not associated with a namespace" % givenprefix
- localname = str(localname).lower()
-
- # qname implementation is horribly broken in Python 2.1 (it
- # doesn't report any), and slightly broken in Python 2.2 (it
- # doesn't report the xml: namespace). So we match up namespaces
- # with a known list first, and then possibly override them with
- # the qnames the SAX parser gives us (if indeed it gives us any
- # at all). Thanks to MatejC for helping me test this and
- # tirelessly telling me that it didn't work yet.
- attrsD, self.decls = self.decls, {}
- if localname=='math' and namespace=='http://www.w3.org/1998/Math/MathML':
- attrsD['xmlns']=namespace
- if localname=='svg' and namespace=='http://www.w3.org/2000/svg':
- attrsD['xmlns']=namespace
-
- if prefix:
- localname = prefix.lower() + ':' + localname
- elif namespace and not qname: #Expat
- for name,value in self.namespacesInUse.items():
- if name and value == namespace:
- localname = name + ':' + localname
- break
- if _debug: sys.stderr.write('startElementNS: qname = %s, namespace = %s, givenprefix = %s, prefix = %s, attrs = %s, localname = %s\n' % (qname, namespace, givenprefix, prefix, attrs.items(), localname))
-
- for (namespace, attrlocalname), attrvalue in attrs._attrs.items():
- lowernamespace = (namespace or '').lower()
- prefix = self._matchnamespaces.get(lowernamespace, '')
- if prefix:
- attrlocalname = prefix + ':' + attrlocalname
- attrsD[str(attrlocalname).lower()] = attrvalue
- for qname in attrs.getQNames():
- attrsD[str(qname).lower()] = attrs.getValueByQName(qname)
- self.unknown_starttag(localname, attrsD.items())
-
- def characters(self, text):
- self.handle_data(text)
-
- def endElementNS(self, name, qname):
- namespace, localname = name
- lowernamespace = str(namespace or '').lower()
- if qname and qname.find(':') > 0:
- givenprefix = qname.split(':')[0]
- else:
- givenprefix = ''
- prefix = self._matchnamespaces.get(lowernamespace, givenprefix)
- if prefix:
- localname = prefix + ':' + localname
- elif namespace and not qname: #Expat
- for name,value in self.namespacesInUse.items():
- if name and value == namespace:
- localname = name + ':' + localname
- break
- localname = str(localname).lower()
- self.unknown_endtag(localname)
-
- def error(self, exc):
- self.bozo = 1
- self.exc = exc
-
- def fatalError(self, exc):
- self.error(exc)
- raise exc
-
-class _BaseHTMLProcessor(sgmllib.SGMLParser):
- special = re.compile('''[<>'"]''')
- bare_ampersand = re.compile("&(?!#\d+;|#x[0-9a-fA-F]+;|\w+;)")
- elements_no_end_tag = [
- 'area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame',
- 'hr', 'img', 'input', 'isindex', 'keygen', 'link', 'meta', 'param',
- 'source', 'track', 'wbr'
- ]
-
- def __init__(self, encoding, _type):
- self.encoding = encoding
- self._type = _type
- if _debug: sys.stderr.write('entering BaseHTMLProcessor, encoding=%s\n' % self.encoding)
- sgmllib.SGMLParser.__init__(self)
-
- def reset(self):
- self.pieces = []
- sgmllib.SGMLParser.reset(self)
-
- def _shorttag_replace(self, match):
- tag = match.group(1)
- if tag in self.elements_no_end_tag:
- return '<' + tag + ' />'
- else:
- return '<' + tag + '></' + tag + '>'
-
- def parse_starttag(self,i):
- j=sgmllib.SGMLParser.parse_starttag(self, i)
- if self._type == 'application/xhtml+xml':
- if j>2 and self.rawdata[j-2:j]=='/>':
- self.unknown_endtag(self.lasttag)
- return j
-
- def feed(self, data):
- data = re.compile(r'<!((?!DOCTYPE|--|\[))', re.IGNORECASE).sub(r'&lt;!\1', data)
- #data = re.sub(r'<(\S+?)\s*?/>', self._shorttag_replace, data) # bug [ 1399464 ] Bad regexp for _shorttag_replace
- data = re.sub(r'<([^<>\s]+?)\s*/>', self._shorttag_replace, data)
- data = data.replace('&#39;', "'")
- data = data.replace('&#34;', '"')
- try:
- bytes
- if bytes is str:
- raise NameError
- self.encoding = self.encoding + '_INVALID_PYTHON_3'
- except NameError:
- if self.encoding and type(data) == type(u''):
- data = data.encode(self.encoding)
- sgmllib.SGMLParser.feed(self, data)
- sgmllib.SGMLParser.close(self)
-
- def normalize_attrs(self, attrs):
- if not attrs: return attrs
- # utility method to be called by descendants
- attrs = dict([(k.lower(), v) for k, v in attrs]).items()
- attrs = [(k, k in ('rel', 'type') and v.lower() or v) for k, v in attrs]
- attrs.sort()
- return attrs
-
- def unknown_starttag(self, tag, attrs):
- # called for each start tag
- # attrs is a list of (attr, value) tuples
- # e.g. for <pre class='screen'>, tag='pre', attrs=[('class', 'screen')]
- if _debug: sys.stderr.write('_BaseHTMLProcessor, unknown_starttag, tag=%s\n' % tag)
- uattrs = []
- strattrs=''
- if attrs:
- for key, value in attrs:
- value=value.replace('>','&gt;').replace('<','&lt;').replace('"','&quot;')
- value = self.bare_ampersand.sub("&amp;", value)
- # thanks to Kevin Marks for this breathtaking hack to deal with (valid) high-bit attribute values in UTF-8 feeds
- if type(value) != type(u''):
- try:
- value = unicode(value, self.encoding)
- except:
- value = unicode(value, 'iso-8859-1')
- try:
- # Currently, in Python 3 the key is already a str, and cannot be decoded again
- uattrs.append((unicode(key, self.encoding), value))
- except TypeError:
- uattrs.append((key, value))
- strattrs = u''.join([u' %s="%s"' % (key, value) for key, value in uattrs])
- if self.encoding:
- try:
- strattrs=strattrs.encode(self.encoding)
- except:
- pass
- if tag in self.elements_no_end_tag:
- self.pieces.append('<%(tag)s%(strattrs)s />' % locals())
- else:
- self.pieces.append('<%(tag)s%(strattrs)s>' % locals())
-
- def unknown_endtag(self, tag):
- # called for each end tag, e.g. for </pre>, tag will be 'pre'
- # Reconstruct the original end tag.
- if tag not in self.elements_no_end_tag:
- self.pieces.append("</%(tag)s>" % locals())
-
- def handle_charref(self, ref):
- # called for each character reference, e.g. for '&#160;', ref will be '160'
- # Reconstruct the original character reference.
- if ref.startswith('x'):
- value = unichr(int(ref[1:],16))
- else:
- value = unichr(int(ref))
-
- if value in _cp1252.keys():
- self.pieces.append('&#%s;' % hex(ord(_cp1252[value]))[1:])
- else:
- self.pieces.append('&#%(ref)s;' % locals())
-
- def handle_entityref(self, ref):
- # called for each entity reference, e.g. for '&copy;', ref will be 'copy'
- # Reconstruct the original entity reference.
- if name2codepoint.has_key(ref):
- self.pieces.append('&%(ref)s;' % locals())
- else:
- self.pieces.append('&amp;%(ref)s' % locals())
-
- def handle_data(self, text):
- # called for each block of plain text, i.e. outside of any tag and
- # not containing any character or entity references
- # Store the original text verbatim.
- if _debug: sys.stderr.write('_BaseHTMLProcessor, handle_data, text=%s\n' % text)
- self.pieces.append(text)
-
- def handle_comment(self, text):
- # called for each HTML comment, e.g. <!-- insert Javascript code here -->
- # Reconstruct the original comment.
- self.pieces.append('<!--%(text)s-->' % locals())
-
- def handle_pi(self, text):
- # called for each processing instruction, e.g. <?instruction>
- # Reconstruct original processing instruction.
- self.pieces.append('<?%(text)s>' % locals())
-
- def handle_decl(self, text):
- # called for the DOCTYPE, if present, e.g.
- # <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
- # "http://www.w3.org/TR/html4/loose.dtd">
- # Reconstruct original DOCTYPE
- self.pieces.append('<!%(text)s>' % locals())
-
- _new_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9:]*\s*').match
- def _scan_name(self, i, declstartpos):
- rawdata = self.rawdata
- n = len(rawdata)
- if i == n:
- return None, -1
- m = self._new_declname_match(rawdata, i)
- if m:
- s = m.group()
- name = s.strip()
- if (i + len(s)) == n:
- return None, -1 # end of buffer
- return name.lower(), m.end()
- else:
- self.handle_data(rawdata)
-# self.updatepos(declstartpos, i)
- return None, -1
-
- def convert_charref(self, name):
- return '&#%s;' % name
-
- def convert_entityref(self, name):
- return '&%s;' % name
-
- def output(self):
- '''Return processed HTML as a single string'''
- return ''.join([str(p) for p in self.pieces])
-
-class _LooseFeedParser(_FeedParserMixin, _BaseHTMLProcessor):
- def __init__(self, baseuri, baselang, encoding, entities):
- sgmllib.SGMLParser.__init__(self)
- _FeedParserMixin.__init__(self, baseuri, baselang, encoding)
- _BaseHTMLProcessor.__init__(self, encoding, 'application/xhtml+xml')
- self.entities=entities
-
- def decodeEntities(self, element, data):
- data = data.replace('&#60;', '&lt;')
- data = data.replace('&#x3c;', '&lt;')
- data = data.replace('&#x3C;', '&lt;')
- data = data.replace('&#62;', '&gt;')
- data = data.replace('&#x3e;', '&gt;')
- data = data.replace('&#x3E;', '&gt;')
- data = data.replace('&#38;', '&amp;')
- data = data.replace('&#x26;', '&amp;')
- data = data.replace('&#34;', '&quot;')
- data = data.replace('&#x22;', '&quot;')
- data = data.replace('&#39;', '&apos;')
- data = data.replace('&#x27;', '&apos;')
- if self.contentparams.has_key('type') and not self.contentparams.get('type', 'xml').endswith('xml'):
- data = data.replace('&lt;', '<')
- data = data.replace('&gt;', '>')
- data = data.replace('&amp;', '&')
- data = data.replace('&quot;', '"')
- data = data.replace('&apos;', "'")
- return data
-
- def strattrs(self, attrs):
- return ''.join([' %s="%s"' % (n,v.replace('"','&quot;')) for n,v in attrs])
-
-class _MicroformatsParser:
- STRING = 1
- DATE = 2
- URI = 3
- NODE = 4
- EMAIL = 5
-
- known_xfn_relationships = ['contact', 'acquaintance', 'friend', 'met', 'co-worker', 'coworker', 'colleague', 'co-resident', 'coresident', 'neighbor', 'child', 'parent', 'sibling', 'brother', 'sister', 'spouse', 'wife', 'husband', 'kin', 'relative', 'muse', 'crush', 'date', 'sweetheart', 'me']
- known_binary_extensions = ['zip','rar','exe','gz','tar','tgz','tbz2','bz2','z','7z','dmg','img','sit','sitx','hqx','deb','rpm','bz2','jar','rar','iso','bin','msi','mp2','mp3','ogg','ogm','mp4','m4v','m4a','avi','wma','wmv']
-
- def __init__(self, data, baseuri, encoding):
- self.document = BeautifulSoup.BeautifulSoup(data)
- self.baseuri = baseuri
- self.encoding = encoding
- if type(data) == type(u''):
- data = data.encode(encoding)
- self.tags = []
- self.enclosures = []
- self.xfn = []
- self.vcard = None
-
- def vcardEscape(self, s):
- if type(s) in (type(''), type(u'')):
- s = s.replace(',', '\\,').replace(';', '\\;').replace('\n', '\\n')
- return s
-
- def vcardFold(self, s):
- s = re.sub(';+$', '', s)
- sFolded = ''
- iMax = 75
- sPrefix = ''
- while len(s) > iMax:
- sFolded += sPrefix + s[:iMax] + '\n'
- s = s[iMax:]
- sPrefix = ' '
- iMax = 74
- sFolded += sPrefix + s
- return sFolded
-
- def normalize(self, s):
- return re.sub(r'\s+', ' ', s).strip()
-
- def unique(self, aList):
- results = []
- for element in aList:
- if element not in results:
- results.append(element)
- return results
-
- def toISO8601(self, dt):
- return time.strftime('%Y-%m-%dT%H:%M:%SZ', dt)
-
- def getPropertyValue(self, elmRoot, sProperty, iPropertyType=4, bAllowMultiple=0, bAutoEscape=0):
- all = lambda x: 1
- sProperty = sProperty.lower()
- bFound = 0
- bNormalize = 1
- propertyMatch = {'class': re.compile(r'\b%s\b' % sProperty)}
- if bAllowMultiple and (iPropertyType != self.NODE):
- snapResults = []
- containers = elmRoot(['ul', 'ol'], propertyMatch)
- for container in containers:
- snapResults.extend(container('li'))
- bFound = (len(snapResults) != 0)
- if not bFound:
- snapResults = elmRoot(all, propertyMatch)
- bFound = (len(snapResults) != 0)
- if (not bFound) and (sProperty == 'value'):
- snapResults = elmRoot('pre')
- bFound = (len(snapResults) != 0)
- bNormalize = not bFound
- if not bFound:
- snapResults = [elmRoot]
- bFound = (len(snapResults) != 0)
- arFilter = []
- if sProperty == 'vcard':
- snapFilter = elmRoot(all, propertyMatch)
- for node in snapFilter:
- if node.findParent(all, propertyMatch):
- arFilter.append(node)
- arResults = []
- for node in snapResults:
- if node not in arFilter:
- arResults.append(node)
- bFound = (len(arResults) != 0)
- if not bFound:
- if bAllowMultiple: return []
- elif iPropertyType == self.STRING: return ''
- elif iPropertyType == self.DATE: return None
- elif iPropertyType == self.URI: return ''
- elif iPropertyType == self.NODE: return None
- else: return None
- arValues = []
- for elmResult in arResults:
- sValue = None
- if iPropertyType == self.NODE:
- if bAllowMultiple:
- arValues.append(elmResult)
- continue
- else:
- return elmResult
- sNodeName = elmResult.name.lower()
- if (iPropertyType == self.EMAIL) and (sNodeName == 'a'):
- sValue = (elmResult.get('href') or '').split('mailto:').pop().split('?')[0]
- if sValue:
- sValue = bNormalize and self.normalize(sValue) or sValue.strip()
- if (not sValue) and (sNodeName == 'abbr'):
- sValue = elmResult.get('title')
- if sValue:
- sValue = bNormalize and self.normalize(sValue) or sValue.strip()
- if (not sValue) and (iPropertyType == self.URI):
- if sNodeName == 'a': sValue = elmResult.get('href')
- elif sNodeName == 'img': sValue = elmResult.get('src')
- elif sNodeName == 'object': sValue = elmResult.get('data')
- if sValue:
- sValue = bNormalize and self.normalize(sValue) or sValue.strip()
- if (not sValue) and (sNodeName == 'img'):
- sValue = elmResult.get('alt')
- if sValue:
- sValue = bNormalize and self.normalize(sValue) or sValue.strip()
- if not sValue:
- sValue = elmResult.renderContents()
- sValue = re.sub(r'<\S[^>]*>', '', sValue)
- sValue = sValue.replace('\r\n', '\n')
- sValue = sValue.replace('\r', '\n')
- if sValue:
- sValue = bNormalize and self.normalize(sValue) or sValue.strip()
- if not sValue: continue
- if iPropertyType == self.DATE:
- sValue = _parse_date_iso8601(sValue)
- if bAllowMultiple:
- arValues.append(bAutoEscape and self.vcardEscape(sValue) or sValue)
- else:
- return bAutoEscape and self.vcardEscape(sValue) or sValue
- return arValues
-
- def findVCards(self, elmRoot, bAgentParsing=0):
- sVCards = ''
-
- if not bAgentParsing:
- arCards = self.getPropertyValue(elmRoot, 'vcard', bAllowMultiple=1)
- else:
- arCards = [elmRoot]
-
- for elmCard in arCards:
- arLines = []
-
- def processSingleString(sProperty):
- sValue = self.getPropertyValue(elmCard, sProperty, self.STRING, bAutoEscape=1).decode(self.encoding)
- if sValue:
- arLines.append(self.vcardFold(sProperty.upper() + ':' + sValue))
- return sValue or u''
-
- def processSingleURI(sProperty):
- sValue = self.getPropertyValue(elmCard, sProperty, self.URI)
- if sValue:
- sContentType = ''
- sEncoding = ''
- sValueKey = ''
- if sValue.startswith('data:'):
- sEncoding = ';ENCODING=b'
- sContentType = sValue.split(';')[0].split('/').pop()
- sValue = sValue.split(',', 1).pop()
- else:
- elmValue = self.getPropertyValue(elmCard, sProperty)
- if elmValue:
- if sProperty != 'url':
- sValueKey = ';VALUE=uri'
- sContentType = elmValue.get('type', '').strip().split('/').pop().strip()
- sContentType = sContentType.upper()
- if sContentType == 'OCTET-STREAM':
- sContentType = ''
- if sContentType:
- sContentType = ';TYPE=' + sContentType.upper()
- arLines.append(self.vcardFold(sProperty.upper() + sEncoding + sContentType + sValueKey + ':' + sValue))
-
- def processTypeValue(sProperty, arDefaultType, arForceType=None):
- arResults = self.getPropertyValue(elmCard, sProperty, bAllowMultiple=1)
- for elmResult in arResults:
- arType = self.getPropertyValue(elmResult, 'type', self.STRING, 1, 1)
- if arForceType:
- arType = self.unique(arForceType + arType)
- if not arType:
- arType = arDefaultType
- sValue = self.getPropertyValue(elmResult, 'value', self.EMAIL, 0)
- if sValue:
- arLines.append(self.vcardFold(sProperty.upper() + ';TYPE=' + ','.join(arType) + ':' + sValue))
-
- # AGENT
- # must do this before all other properties because it is destructive
- # (removes nested class="vcard" nodes so they don't interfere with
- # this vcard's other properties)
- arAgent = self.getPropertyValue(elmCard, 'agent', bAllowMultiple=1)
- for elmAgent in arAgent:
- if re.compile(r'\bvcard\b').search(elmAgent.get('class')):
- sAgentValue = self.findVCards(elmAgent, 1) + '\n'
- sAgentValue = sAgentValue.replace('\n', '\\n')
- sAgentValue = sAgentValue.replace(';', '\\;')
- if sAgentValue:
- arLines.append(self.vcardFold('AGENT:' + sAgentValue))
- # Completely remove the agent element from the parse tree
- elmAgent.extract()
- else:
- sAgentValue = self.getPropertyValue(elmAgent, 'value', self.URI, bAutoEscape=1);
- if sAgentValue:
- arLines.append(self.vcardFold('AGENT;VALUE=uri:' + sAgentValue))
-
- # FN (full name)
- sFN = processSingleString('fn')
-
- # N (name)
- elmName = self.getPropertyValue(elmCard, 'n')
- if elmName:
- sFamilyName = self.getPropertyValue(elmName, 'family-name', self.STRING, bAutoEscape=1)
- sGivenName = self.getPropertyValue(elmName, 'given-name', self.STRING, bAutoEscape=1)
- arAdditionalNames = self.getPropertyValue(elmName, 'additional-name', self.STRING, 1, 1) + self.getPropertyValue(elmName, 'additional-names', self.STRING, 1, 1)
- arHonorificPrefixes = self.getPropertyValue(elmName, 'honorific-prefix', self.STRING, 1, 1) + self.getPropertyValue(elmName, 'honorific-prefixes', self.STRING, 1, 1)
- arHonorificSuffixes = self.getPropertyValue(elmName, 'honorific-suffix', self.STRING, 1, 1) + self.getPropertyValue(elmName, 'honorific-suffixes', self.STRING, 1, 1)
- arLines.append(self.vcardFold('N:' + sFamilyName + ';' +
- sGivenName + ';' +
- ','.join(arAdditionalNames) + ';' +
- ','.join(arHonorificPrefixes) + ';' +
- ','.join(arHonorificSuffixes)))
- elif sFN:
- # implied "N" optimization
- # http://microformats.org/wiki/hcard#Implied_.22N.22_Optimization
- arNames = self.normalize(sFN).split()
- if len(arNames) == 2:
- bFamilyNameFirst = (arNames[0].endswith(',') or
- len(arNames[1]) == 1 or
- ((len(arNames[1]) == 2) and (arNames[1].endswith('.'))))
- if bFamilyNameFirst:
- arLines.append(self.vcardFold('N:' + arNames[0] + ';' + arNames[1]))
- else:
- arLines.append(self.vcardFold('N:' + arNames[1] + ';' + arNames[0]))
-
- # SORT-STRING
- sSortString = self.getPropertyValue(elmCard, 'sort-string', self.STRING, bAutoEscape=1)
- if sSortString:
- arLines.append(self.vcardFold('SORT-STRING:' + sSortString))
-
- # NICKNAME
- arNickname = self.getPropertyValue(elmCard, 'nickname', self.STRING, 1, 1)
- if arNickname:
- arLines.append(self.vcardFold('NICKNAME:' + ','.join(arNickname)))
-
- # PHOTO
- processSingleURI('photo')
-
- # BDAY
- dtBday = self.getPropertyValue(elmCard, 'bday', self.DATE)
- if dtBday:
- arLines.append(self.vcardFold('BDAY:' + self.toISO8601(dtBday)))
-
- # ADR (address)
- arAdr = self.getPropertyValue(elmCard, 'adr', bAllowMultiple=1)
- for elmAdr in arAdr:
- arType = self.getPropertyValue(elmAdr, 'type', self.STRING, 1, 1)
- if not arType:
- arType = ['intl','postal','parcel','work'] # default adr types, see RFC 2426 section 3.2.1
- sPostOfficeBox = self.getPropertyValue(elmAdr, 'post-office-box', self.STRING, 0, 1)
- sExtendedAddress = self.getPropertyValue(elmAdr, 'extended-address', self.STRING, 0, 1)
- sStreetAddress = self.getPropertyValue(elmAdr, 'street-address', self.STRING, 0, 1)
- sLocality = self.getPropertyValue(elmAdr, 'locality', self.STRING, 0, 1)
- sRegion = self.getPropertyValue(elmAdr, 'region', self.STRING, 0, 1)
- sPostalCode = self.getPropertyValue(elmAdr, 'postal-code', self.STRING, 0, 1)
- sCountryName = self.getPropertyValue(elmAdr, 'country-name', self.STRING, 0, 1)
- arLines.append(self.vcardFold('ADR;TYPE=' + ','.join(arType) + ':' +
- sPostOfficeBox + ';' +
- sExtendedAddress + ';' +
- sStreetAddress + ';' +
- sLocality + ';' +
- sRegion + ';' +
- sPostalCode + ';' +
- sCountryName))
-
- # LABEL
- processTypeValue('label', ['intl','postal','parcel','work'])
-
- # TEL (phone number)
- processTypeValue('tel', ['voice'])
-
- # EMAIL
- processTypeValue('email', ['internet'], ['internet'])
-
- # MAILER
- processSingleString('mailer')
-
- # TZ (timezone)
- processSingleString('tz')
-
- # GEO (geographical information)
- elmGeo = self.getPropertyValue(elmCard, 'geo')
- if elmGeo:
- sLatitude = self.getPropertyValue(elmGeo, 'latitude', self.STRING, 0, 1)
- sLongitude = self.getPropertyValue(elmGeo, 'longitude', self.STRING, 0, 1)
- arLines.append(self.vcardFold('GEO:' + sLatitude + ';' + sLongitude))
-
- # TITLE
- processSingleString('title')
-
- # ROLE
- processSingleString('role')
-
- # LOGO
- processSingleURI('logo')
-
- # ORG (organization)
- elmOrg = self.getPropertyValue(elmCard, 'org')
- if elmOrg:
- sOrganizationName = self.getPropertyValue(elmOrg, 'organization-name', self.STRING, 0, 1)
- if not sOrganizationName:
- # implied "organization-name" optimization
- # http://microformats.org/wiki/hcard#Implied_.22organization-name.22_Optimization
- sOrganizationName = self.getPropertyValue(elmCard, 'org', self.STRING, 0, 1)
- if sOrganizationName:
- arLines.append(self.vcardFold('ORG:' + sOrganizationName))
- else:
- arOrganizationUnit = self.getPropertyValue(elmOrg, 'organization-unit', self.STRING, 1, 1)
- arLines.append(self.vcardFold('ORG:' + sOrganizationName + ';' + ';'.join(arOrganizationUnit)))
-
- # CATEGORY
- arCategory = self.getPropertyValue(elmCard, 'category', self.STRING, 1, 1) + self.getPropertyValue(elmCard, 'categories', self.STRING, 1, 1)
- if arCategory:
- arLines.append(self.vcardFold('CATEGORIES:' + ','.join(arCategory)))
-
- # NOTE
- processSingleString('note')
-
- # REV
- processSingleString('rev')
-
- # SOUND
- processSingleURI('sound')
-
- # UID
- processSingleString('uid')
-
- # URL
- processSingleURI('url')
-
- # CLASS
- processSingleString('class')
-
- # KEY
- processSingleURI('key')
-
- if arLines:
- arLines = [u'BEGIN:vCard',u'VERSION:3.0'] + arLines + [u'END:vCard']
- sVCards += u'\n'.join(arLines) + u'\n'
-
- return sVCards.strip()
-
- def isProbablyDownloadable(self, elm):
- attrsD = elm.attrMap
- if not attrsD.has_key('href'): return 0
- linktype = attrsD.get('type', '').strip()
- if linktype.startswith('audio/') or \
- linktype.startswith('video/') or \
- (linktype.startswith('application/') and not linktype.endswith('xml')):
- return 1
- path = urlparse.urlparse(attrsD['href'])[2]
- if path.find('.') == -1: return 0
- fileext = path.split('.').pop().lower()
- return fileext in self.known_binary_extensions
-
- def findTags(self):
- all = lambda x: 1
- for elm in self.document(all, {'rel': re.compile(r'\btag\b')}):
- href = elm.get('href')
- if not href: continue
- urlscheme, domain, path, params, query, fragment = \
- urlparse.urlparse(_urljoin(self.baseuri, href))
- segments = path.split('/')
- tag = segments.pop()
- if not tag:
- tag = segments.pop()
- tagscheme = urlparse.urlunparse((urlscheme, domain, '/'.join(segments), '', '', ''))
- if not tagscheme.endswith('/'):
- tagscheme += '/'
- self.tags.append(FeedParserDict({"term": tag, "scheme": tagscheme, "label": elm.string or ''}))
-
- def findEnclosures(self):
- all = lambda x: 1
- enclosure_match = re.compile(r'\benclosure\b')
- for elm in self.document(all, {'href': re.compile(r'.+')}):
- if not enclosure_match.search(elm.get('rel', '')) and not self.isProbablyDownloadable(elm): continue
- if elm.attrMap not in self.enclosures:
- self.enclosures.append(elm.attrMap)
- if elm.string and not elm.get('title'):
- self.enclosures[-1]['title'] = elm.string
-
- def findXFN(self):
- all = lambda x: 1
- for elm in self.document(all, {'rel': re.compile('.+'), 'href': re.compile('.+')}):
- rels = elm.get('rel', '').split()
- xfn_rels = []
- for rel in rels:
- if rel in self.known_xfn_relationships:
- xfn_rels.append(rel)
- if xfn_rels:
- self.xfn.append({"relationships": xfn_rels, "href": elm.get('href', ''), "name": elm.string})
-
-def _parseMicroformats(htmlSource, baseURI, encoding):
- if not BeautifulSoup: return
- if _debug: sys.stderr.write('entering _parseMicroformats\n')
- try:
- p = _MicroformatsParser(htmlSource, baseURI, encoding)
- except UnicodeEncodeError:
- # sgmllib throws this exception when performing lookups of tags
- # with non-ASCII characters in them.
- return
- p.vcard = p.findVCards(p.document)
- p.findTags()
- p.findEnclosures()
- p.findXFN()
- return {"tags": p.tags, "enclosures": p.enclosures, "xfn": p.xfn, "vcard": p.vcard}
-
-class _RelativeURIResolver(_BaseHTMLProcessor):
- relative_uris = [('a', 'href'),
- ('applet', 'codebase'),
- ('area', 'href'),
- ('blockquote', 'cite'),
- ('body', 'background'),
- ('del', 'cite'),
- ('form', 'action'),
- ('frame', 'longdesc'),
- ('frame', 'src'),
- ('iframe', 'longdesc'),
- ('iframe', 'src'),
- ('head', 'profile'),
- ('img', 'longdesc'),
- ('img', 'src'),
- ('img', 'usemap'),
- ('input', 'src'),
- ('input', 'usemap'),
- ('ins', 'cite'),
- ('link', 'href'),
- ('object', 'classid'),
- ('object', 'codebase'),
- ('object', 'data'),
- ('object', 'usemap'),
- ('q', 'cite'),
- ('script', 'src')]
-
- def __init__(self, baseuri, encoding, _type):
- _BaseHTMLProcessor.__init__(self, encoding, _type)
- self.baseuri = baseuri
-
- def resolveURI(self, uri):
- return _makeSafeAbsoluteURI(_urljoin(self.baseuri, uri.strip()))
-
- def unknown_starttag(self, tag, attrs):
- if _debug:
- sys.stderr.write('tag: [%s] with attributes: [%s]\n' % (tag, str(attrs)))
- attrs = self.normalize_attrs(attrs)
- attrs = [(key, ((tag, key) in self.relative_uris) and self.resolveURI(value) or value) for key, value in attrs]
- _BaseHTMLProcessor.unknown_starttag(self, tag, attrs)
-
-def _resolveRelativeURIs(htmlSource, baseURI, encoding, _type):
- if _debug:
- sys.stderr.write('entering _resolveRelativeURIs\n')
-
- p = _RelativeURIResolver(baseURI, encoding, _type)
- p.feed(htmlSource)
- return p.output()
-
-def _makeSafeAbsoluteURI(base, rel=None):
- # bail if ACCEPTABLE_URI_SCHEMES is empty
- if not ACCEPTABLE_URI_SCHEMES:
- return _urljoin(base, rel or u'')
- if not base:
- return rel or u''
- if not rel:
- if base.strip().split(':', 1)[0] not in ACCEPTABLE_URI_SCHEMES:
- return u''
- return base
- uri = _urljoin(base, rel)
- if uri.strip().split(':', 1)[0] not in ACCEPTABLE_URI_SCHEMES:
- return u''
- return uri
-
-class _HTMLSanitizer(_BaseHTMLProcessor):
- acceptable_elements = ['a', 'abbr', 'acronym', 'address', 'area',
- 'article', 'aside', 'audio', 'b', 'big', 'blockquote', 'br', 'button',
- 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup',
- 'command', 'datagrid', 'datalist', 'dd', 'del', 'details', 'dfn',
- 'dialog', 'dir', 'div', 'dl', 'dt', 'em', 'event-source', 'fieldset',
- 'figcaption', 'figure', 'footer', 'font', 'form', 'header', 'h1',
- 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'input', 'ins',
- 'keygen', 'kbd', 'label', 'legend', 'li', 'm', 'map', 'menu', 'meter',
- 'multicol', 'nav', 'nextid', 'ol', 'output', 'optgroup', 'option',
- 'p', 'pre', 'progress', 'q', 's', 'samp', 'section', 'select',
- 'small', 'sound', 'source', 'spacer', 'span', 'strike', 'strong',
- 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'time', 'tfoot',
- 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'video', 'noscript']
-
- acceptable_attributes = ['abbr', 'accept', 'accept-charset', 'accesskey',
- 'action', 'align', 'alt', 'autocomplete', 'autofocus', 'axis',
- 'background', 'balance', 'bgcolor', 'bgproperties', 'border',
- 'bordercolor', 'bordercolordark', 'bordercolorlight', 'bottompadding',
- 'cellpadding', 'cellspacing', 'ch', 'challenge', 'char', 'charoff',
- 'choff', 'charset', 'checked', 'cite', 'class', 'clear', 'color', 'cols',
- 'colspan', 'compact', 'contenteditable', 'controls', 'coords', 'data',
- 'datafld', 'datapagesize', 'datasrc', 'datetime', 'default', 'delay',
- 'dir', 'disabled', 'draggable', 'dynsrc', 'enctype', 'end', 'face', 'for',
- 'form', 'frame', 'galleryimg', 'gutter', 'headers', 'height', 'hidefocus',
- 'hidden', 'high', 'href', 'hreflang', 'hspace', 'icon', 'id', 'inputmode',
- 'ismap', 'keytype', 'label', 'leftspacing', 'lang', 'list', 'longdesc',
- 'loop', 'loopcount', 'loopend', 'loopstart', 'low', 'lowsrc', 'max',
- 'maxlength', 'media', 'method', 'min', 'multiple', 'name', 'nohref',
- 'noshade', 'nowrap', 'open', 'optimum', 'pattern', 'ping', 'point-size',
- 'prompt', 'pqg', 'radiogroup', 'readonly', 'rel', 'repeat-max',
- 'repeat-min', 'replace', 'required', 'rev', 'rightspacing', 'rows',
- 'rowspan', 'rules', 'scope', 'selected', 'shape', 'size', 'span', 'src',
- 'start', 'step', 'summary', 'suppress', 'tabindex', 'target', 'template',
- 'title', 'toppadding', 'type', 'unselectable', 'usemap', 'urn', 'valign',
- 'value', 'variable', 'volume', 'vspace', 'vrml', 'width', 'wrap',
- 'xml:lang']
-
- unacceptable_elements_with_end_tag = ['script', 'applet', 'style']
-
- acceptable_css_properties = ['azimuth', 'background-color',
- 'border-bottom-color', 'border-collapse', 'border-color',
- 'border-left-color', 'border-right-color', 'border-top-color', 'clear',
- 'color', 'cursor', 'direction', 'display', 'elevation', 'float', 'font',
- 'font-family', 'font-size', 'font-style', 'font-variant', 'font-weight',
- 'height', 'letter-spacing', 'line-height', 'overflow', 'pause',
- 'pause-after', 'pause-before', 'pitch', 'pitch-range', 'richness',
- 'speak', 'speak-header', 'speak-numeral', 'speak-punctuation',
- 'speech-rate', 'stress', 'text-align', 'text-decoration', 'text-indent',
- 'unicode-bidi', 'vertical-align', 'voice-family', 'volume',
- 'white-space', 'width']
-
- # survey of common keywords found in feeds
- acceptable_css_keywords = ['auto', 'aqua', 'black', 'block', 'blue',
- 'bold', 'both', 'bottom', 'brown', 'center', 'collapse', 'dashed',
- 'dotted', 'fuchsia', 'gray', 'green', '!important', 'italic', 'left',
- 'lime', 'maroon', 'medium', 'none', 'navy', 'normal', 'nowrap', 'olive',
- 'pointer', 'purple', 'red', 'right', 'solid', 'silver', 'teal', 'top',
- 'transparent', 'underline', 'white', 'yellow']
-
- valid_css_values = re.compile('^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|' +
- '\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$')
-
- mathml_elements = ['annotation', 'annotation-xml', 'maction', 'math',
- 'merror', 'mfenced', 'mfrac', 'mi', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded',
- 'mphantom', 'mprescripts', 'mroot', 'mrow', 'mspace', 'msqrt', 'mstyle',
- 'msub', 'msubsup', 'msup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder',
- 'munderover', 'none', 'semantics']
-
- mathml_attributes = ['actiontype', 'align', 'columnalign', 'columnalign',
- 'columnalign', 'close', 'columnlines', 'columnspacing', 'columnspan', 'depth',
- 'display', 'displaystyle', 'encoding', 'equalcolumns', 'equalrows',
- 'fence', 'fontstyle', 'fontweight', 'frame', 'height', 'linethickness',
- 'lspace', 'mathbackground', 'mathcolor', 'mathvariant', 'mathvariant',
- 'maxsize', 'minsize', 'open', 'other', 'rowalign', 'rowalign', 'rowalign',
- 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'scriptlevel', 'selection',
- 'separator', 'separators', 'stretchy', 'width', 'width', 'xlink:href',
- 'xlink:show', 'xlink:type', 'xmlns', 'xmlns:xlink']
-
- # svgtiny - foreignObject + linearGradient + radialGradient + stop
- svg_elements = ['a', 'animate', 'animateColor', 'animateMotion',
- 'animateTransform', 'circle', 'defs', 'desc', 'ellipse', 'foreignObject',
- 'font-face', 'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern',
- 'linearGradient', 'line', 'marker', 'metadata', 'missing-glyph', 'mpath',
- 'path', 'polygon', 'polyline', 'radialGradient', 'rect', 'set', 'stop',
- 'svg', 'switch', 'text', 'title', 'tspan', 'use']
-
- # svgtiny + class + opacity + offset + xmlns + xmlns:xlink
- svg_attributes = ['accent-height', 'accumulate', 'additive', 'alphabetic',
- 'arabic-form', 'ascent', 'attributeName', 'attributeType',
- 'baseProfile', 'bbox', 'begin', 'by', 'calcMode', 'cap-height',
- 'class', 'color', 'color-rendering', 'content', 'cx', 'cy', 'd', 'dx',
- 'dy', 'descent', 'display', 'dur', 'end', 'fill', 'fill-opacity',
- 'fill-rule', 'font-family', 'font-size', 'font-stretch', 'font-style',
- 'font-variant', 'font-weight', 'from', 'fx', 'fy', 'g1', 'g2',
- 'glyph-name', 'gradientUnits', 'hanging', 'height', 'horiz-adv-x',
- 'horiz-origin-x', 'id', 'ideographic', 'k', 'keyPoints', 'keySplines',
- 'keyTimes', 'lang', 'mathematical', 'marker-end', 'marker-mid',
- 'marker-start', 'markerHeight', 'markerUnits', 'markerWidth', 'max',
- 'min', 'name', 'offset', 'opacity', 'orient', 'origin',
- 'overline-position', 'overline-thickness', 'panose-1', 'path',
- 'pathLength', 'points', 'preserveAspectRatio', 'r', 'refX', 'refY',
- 'repeatCount', 'repeatDur', 'requiredExtensions', 'requiredFeatures',
- 'restart', 'rotate', 'rx', 'ry', 'slope', 'stemh', 'stemv',
- 'stop-color', 'stop-opacity', 'strikethrough-position',
- 'strikethrough-thickness', 'stroke', 'stroke-dasharray',
- 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin',
- 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'systemLanguage',
- 'target', 'text-anchor', 'to', 'transform', 'type', 'u1', 'u2',
- 'underline-position', 'underline-thickness', 'unicode', 'unicode-range',
- 'units-per-em', 'values', 'version', 'viewBox', 'visibility', 'width',
- 'widths', 'x', 'x-height', 'x1', 'x2', 'xlink:actuate', 'xlink:arcrole',
- 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type',
- 'xml:base', 'xml:lang', 'xml:space', 'xmlns', 'xmlns:xlink', 'y', 'y1',
- 'y2', 'zoomAndPan']
-
- svg_attr_map = None
- svg_elem_map = None
-
- acceptable_svg_properties = [ 'fill', 'fill-opacity', 'fill-rule',
- 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin',
- 'stroke-opacity']
-
- def reset(self):
- _BaseHTMLProcessor.reset(self)
- self.unacceptablestack = 0
- self.mathmlOK = 0
- self.svgOK = 0
-
- def unknown_starttag(self, tag, attrs):
- acceptable_attributes = self.acceptable_attributes
- keymap = {}
- if not tag in self.acceptable_elements or self.svgOK:
- if tag in self.unacceptable_elements_with_end_tag:
- self.unacceptablestack += 1
-
- # add implicit namespaces to html5 inline svg/mathml
- if self._type.endswith('html'):
- if not dict(attrs).get('xmlns'):
- if tag=='svg':
- attrs.append( ('xmlns','http://www.w3.org/2000/svg') )
- if tag=='math':
- attrs.append( ('xmlns','http://www.w3.org/1998/Math/MathML') )
-
- # not otherwise acceptable, perhaps it is MathML or SVG?
- if tag=='math' and ('xmlns','http://www.w3.org/1998/Math/MathML') in attrs:
- self.mathmlOK += 1
- if tag=='svg' and ('xmlns','http://www.w3.org/2000/svg') in attrs:
- self.svgOK += 1
-
- # chose acceptable attributes based on tag class, else bail
- if self.mathmlOK and tag in self.mathml_elements:
- acceptable_attributes = self.mathml_attributes
- elif self.svgOK and tag in self.svg_elements:
- # for most vocabularies, lowercasing is a good idea. Many
- # svg elements, however, are camel case
- if not self.svg_attr_map:
- lower=[attr.lower() for attr in self.svg_attributes]
- mix=[a for a in self.svg_attributes if a not in lower]
- self.svg_attributes = lower
- self.svg_attr_map = dict([(a.lower(),a) for a in mix])
-
- lower=[attr.lower() for attr in self.svg_elements]
- mix=[a for a in self.svg_elements if a not in lower]
- self.svg_elements = lower
- self.svg_elem_map = dict([(a.lower(),a) for a in mix])
- acceptable_attributes = self.svg_attributes
- tag = self.svg_elem_map.get(tag,tag)
- keymap = self.svg_attr_map
- elif not tag in self.acceptable_elements:
- return
-
- # declare xlink namespace, if needed
- if self.mathmlOK or self.svgOK:
- if filter(lambda (n,v): n.startswith('xlink:'),attrs):
- if not ('xmlns:xlink','http://www.w3.org/1999/xlink') in attrs:
- attrs.append(('xmlns:xlink','http://www.w3.org/1999/xlink'))
-
- clean_attrs = []
- for key, value in self.normalize_attrs(attrs):
- if key in acceptable_attributes:
- key=keymap.get(key,key)
- clean_attrs.append((key,value))
- elif key=='style':
- clean_value = self.sanitize_style(value)
- if clean_value: clean_attrs.append((key,clean_value))
- _BaseHTMLProcessor.unknown_starttag(self, tag, clean_attrs)
-
- def unknown_endtag(self, tag):
- if not tag in self.acceptable_elements:
- if tag in self.unacceptable_elements_with_end_tag:
- self.unacceptablestack -= 1
- if self.mathmlOK and tag in self.mathml_elements:
- if tag == 'math' and self.mathmlOK: self.mathmlOK -= 1
- elif self.svgOK and tag in self.svg_elements:
- tag = self.svg_elem_map.get(tag,tag)
- if tag == 'svg' and self.svgOK: self.svgOK -= 1
- else:
- return
- _BaseHTMLProcessor.unknown_endtag(self, tag)
-
- def handle_pi(self, text):
- pass
-
- def handle_decl(self, text):
- pass
-
- def handle_data(self, text):
- if not self.unacceptablestack:
- _BaseHTMLProcessor.handle_data(self, text)
-
- def sanitize_style(self, style):
- # disallow urls
- style=re.compile('url\s*\(\s*[^\s)]+?\s*\)\s*').sub(' ',style)
-
- # gauntlet
- if not re.match("""^([:,;#%.\sa-zA-Z0-9!]|\w-\w|'[\s\w]+'|"[\s\w]+"|\([\d,\s]+\))*$""", style): return ''
- # This replaced a regexp that used re.match and was prone to pathological back-tracking.
- if re.sub("\s*[-\w]+\s*:\s*[^:;]*;?", '', style).strip(): return ''
-
- clean = []
- for prop,value in re.findall("([-\w]+)\s*:\s*([^:;]*)",style):
- if not value: continue
- if prop.lower() in self.acceptable_css_properties:
- clean.append(prop + ': ' + value + ';')
- elif prop.split('-')[0].lower() in ['background','border','margin','padding']:
- for keyword in value.split():
- if not keyword in self.acceptable_css_keywords and \
- not self.valid_css_values.match(keyword):
- break
- else:
- clean.append(prop + ': ' + value + ';')
- elif self.svgOK and prop.lower() in self.acceptable_svg_properties:
- clean.append(prop + ': ' + value + ';')
-
- return ' '.join(clean)
-
-
-def _sanitizeHTML(htmlSource, encoding, _type):
- p = _HTMLSanitizer(encoding, _type)
- htmlSource = htmlSource.replace('<![CDATA[', '&lt;![CDATA[')
- p.feed(htmlSource)
- data = p.output()
- if TIDY_MARKUP:
- # loop through list of preferred Tidy interfaces looking for one that's installed,
- # then set up a common _tidy function to wrap the interface-specific API.
- _tidy = None
- for tidy_interface in PREFERRED_TIDY_INTERFACES:
- try:
- if tidy_interface == "uTidy":
- from tidy import parseString as _utidy
- def _tidy(data, **kwargs):
- return str(_utidy(data, **kwargs))
- break
- elif tidy_interface == "mxTidy":
- from mx.Tidy import Tidy as _mxtidy
- def _tidy(data, **kwargs):
- nerrors, nwarnings, data, errordata = _mxtidy.tidy(data, **kwargs)
- return data
- break
- except:
- pass
- if _tidy:
- utf8 = type(data) == type(u'')
- if utf8:
- data = data.encode('utf-8')
- data = _tidy(data, output_xhtml=1, numeric_entities=1, wrap=0, char_encoding="utf8")
- if utf8:
- data = unicode(data, 'utf-8')
- if data.count('<body'):
- data = data.split('<body', 1)[1]
- if data.count('>'):
- data = data.split('>', 1)[1]
- if data.count('</body'):
- data = data.split('</body', 1)[0]
- data = data.strip().replace('\r\n', '\n')
- return data
-
-class _FeedURLHandler(urllib2.HTTPDigestAuthHandler, urllib2.HTTPRedirectHandler, urllib2.HTTPDefaultErrorHandler):
- def http_error_default(self, req, fp, code, msg, headers):
- if ((code / 100) == 3) and (code != 304):
- return self.http_error_302(req, fp, code, msg, headers)
- infourl = urllib.addinfourl(fp, headers, req.get_full_url())
- infourl.status = code
- return infourl
-
- def http_error_302(self, req, fp, code, msg, headers):
- if headers.dict.has_key('location'):
- infourl = urllib2.HTTPRedirectHandler.http_error_302(self, req, fp, code, msg, headers)
- else:
- infourl = urllib.addinfourl(fp, headers, req.get_full_url())
- if not hasattr(infourl, 'status'):
- infourl.status = code
- return infourl
-
- def http_error_301(self, req, fp, code, msg, headers):
- if headers.dict.has_key('location'):
- infourl = urllib2.HTTPRedirectHandler.http_error_301(self, req, fp, code, msg, headers)
- else:
- infourl = urllib.addinfourl(fp, headers, req.get_full_url())
- if not hasattr(infourl, 'status'):
- infourl.status = code
- return infourl
-
- http_error_300 = http_error_302
- http_error_303 = http_error_302
- http_error_307 = http_error_302
-
- def http_error_401(self, req, fp, code, msg, headers):
- # Check if
- # - server requires digest auth, AND
- # - we tried (unsuccessfully) with basic auth, AND
- # - we're using Python 2.3.3 or later (digest auth is irreparably broken in earlier versions)
- # If all conditions hold, parse authentication information
- # out of the Authorization header we sent the first time
- # (for the username and password) and the WWW-Authenticate
- # header the server sent back (for the realm) and retry
- # the request with the appropriate digest auth headers instead.
- # This evil genius hack has been brought to you by Aaron Swartz.
- host = urlparse.urlparse(req.get_full_url())[1]
- try:
- assert sys.version.split()[0] >= '2.3.3'
- assert base64 is not None
- user, passw = _base64decode(req.headers['Authorization'].split(' ')[1]).split(':')
- realm = re.findall('realm="([^"]*)"', headers['WWW-Authenticate'])[0]
- self.add_password(realm, host, user, passw)
- retry = self.http_error_auth_reqed('www-authenticate', host, req, headers)
- self.reset_retry_count()
- return retry
- except:
- return self.http_error_default(req, fp, code, msg, headers)
-
-def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, handlers, request_headers):
- """URL, filename, or string --> stream
-
- This function lets you define parsers that take any input source
- (URL, pathname to local or network file, or actual data as a string)
- and deal with it in a uniform manner. Returned object is guaranteed
- to have all the basic stdio read methods (read, readline, readlines).
- Just .close() the object when you're done with it.
-
- If the etag argument is supplied, it will be used as the value of an
- If-None-Match request header.
-
- If the modified argument is supplied, it can be a tuple of 9 integers
- (as returned by gmtime() in the standard Python time module) or a date
- string in any format supported by feedparser. Regardless, it MUST
- be in GMT (Greenwich Mean Time). It will be reformatted into an
- RFC 1123-compliant date and used as the value of an If-Modified-Since
- request header.
-
- If the agent argument is supplied, it will be used as the value of a
- User-Agent request header.
-
- If the referrer argument is supplied, it will be used as the value of a
- Referer[sic] request header.
-
- If handlers is supplied, it is a list of handlers used to build a
- urllib2 opener.
-
- if request_headers is supplied it is a dictionary of HTTP request headers
- that will override the values generated by FeedParser.
- """
-
- if hasattr(url_file_stream_or_string, 'read'):
- return url_file_stream_or_string
-
- if url_file_stream_or_string == '-':
- return sys.stdin
-
- if urlparse.urlparse(url_file_stream_or_string)[0] in ('http', 'https', 'ftp', 'file', 'feed'):
- # Deal with the feed URI scheme
- if url_file_stream_or_string.startswith('feed:http'):
- url_file_stream_or_string = url_file_stream_or_string[5:]
- elif url_file_stream_or_string.startswith('feed:'):
- url_file_stream_or_string = 'http:' + url_file_stream_or_string[5:]
- if not agent:
- agent = USER_AGENT
- # test for inline user:password for basic auth
- auth = None
- if base64:
- urltype, rest = urllib.splittype(url_file_stream_or_string)
- realhost, rest = urllib.splithost(rest)
- if realhost:
- user_passwd, realhost = urllib.splituser(realhost)
- if user_passwd:
- url_file_stream_or_string = '%s://%s%s' % (urltype, realhost, rest)
- auth = base64.standard_b64encode(user_passwd).strip()
-
- # iri support
- try:
- if isinstance(url_file_stream_or_string,unicode):
- url_file_stream_or_string = url_file_stream_or_string.encode('idna').decode('utf-8')
- else:
- url_file_stream_or_string = url_file_stream_or_string.decode('utf-8').encode('idna').decode('utf-8')
- except:
- pass
-
- # try to open with urllib2 (to use optional headers)
- request = _build_urllib2_request(url_file_stream_or_string, agent, etag, modified, referrer, auth, request_headers)
- opener = apply(urllib2.build_opener, tuple(handlers + [_FeedURLHandler()]))
- opener.addheaders = [] # RMK - must clear so we only send our custom User-Agent
- try:
- return opener.open(request)
- finally:
- opener.close() # JohnD
-
- # try to open with native open function (if url_file_stream_or_string is a filename)
- try:
- return open(url_file_stream_or_string, 'rb')
- except:
- pass
-
- # treat url_file_stream_or_string as string
- return _StringIO(str(url_file_stream_or_string))
-
-def _build_urllib2_request(url, agent, etag, modified, referrer, auth, request_headers):
- request = urllib2.Request(url)
- request.add_header('User-Agent', agent)
- if etag:
- request.add_header('If-None-Match', etag)
- if type(modified) == type(''):
- modified = _parse_date(modified)
- elif isinstance(modified, datetime.datetime):
- modified = modified.utctimetuple()
- if modified:
- # format into an RFC 1123-compliant timestamp. We can't use
- # time.strftime() since the %a and %b directives can be affected
- # by the current locale, but RFC 2616 states that dates must be
- # in English.
- short_weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
- months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
- request.add_header('If-Modified-Since', '%s, %02d %s %04d %02d:%02d:%02d GMT' % (short_weekdays[modified[6]], modified[2], months[modified[1] - 1], modified[0], modified[3], modified[4], modified[5]))
- if referrer:
- request.add_header('Referer', referrer)
- if gzip and zlib:
- request.add_header('Accept-encoding', 'gzip, deflate')
- elif gzip:
- request.add_header('Accept-encoding', 'gzip')
- elif zlib:
- request.add_header('Accept-encoding', 'deflate')
- else:
- request.add_header('Accept-encoding', '')
- if auth:
- request.add_header('Authorization', 'Basic %s' % auth)
- if ACCEPT_HEADER:
- request.add_header('Accept', ACCEPT_HEADER)
- # use this for whatever -- cookies, special headers, etc
- # [('Cookie','Something'),('x-special-header','Another Value')]
- for header_name, header_value in request_headers.items():
- request.add_header(header_name, header_value)
- request.add_header('A-IM', 'feed') # RFC 3229 support
- return request
-
-_date_handlers = []
-def registerDateHandler(func):
- '''Register a date handler function (takes string, returns 9-tuple date in GMT)'''
- _date_handlers.insert(0, func)
-
-# ISO-8601 date parsing routines written by Fazal Majid.
-# The ISO 8601 standard is very convoluted and irregular - a full ISO 8601
-# parser is beyond the scope of feedparser and would be a worthwhile addition
-# to the Python library.
-# A single regular expression cannot parse ISO 8601 date formats into groups
-# as the standard is highly irregular (for instance is 030104 2003-01-04 or
-# 0301-04-01), so we use templates instead.
-# Please note the order in templates is significant because we need a
-# greedy match.
-_iso8601_tmpl = ['YYYY-?MM-?DD', 'YYYY-0MM?-?DD', 'YYYY-MM', 'YYYY-?OOO',
- 'YY-?MM-?DD', 'YY-?OOO', 'YYYY',
- '-YY-?MM', '-OOO', '-YY',
- '--MM-?DD', '--MM',
- '---DD',
- 'CC', '']
-_iso8601_re = [
- tmpl.replace(
- 'YYYY', r'(?P<year>\d{4})').replace(
- 'YY', r'(?P<year>\d\d)').replace(
- 'MM', r'(?P<month>[01]\d)').replace(
- 'DD', r'(?P<day>[0123]\d)').replace(
- 'OOO', r'(?P<ordinal>[0123]\d\d)').replace(
- 'CC', r'(?P<century>\d\d$)')
- + r'(T?(?P<hour>\d{2}):(?P<minute>\d{2})'
- + r'(:(?P<second>\d{2}))?'
- + r'(\.(?P<fracsecond>\d+))?'
- + r'(?P<tz>[+-](?P<tzhour>\d{2})(:(?P<tzmin>\d{2}))?|Z)?)?'
- for tmpl in _iso8601_tmpl]
-try:
- del tmpl
-except NameError:
- pass
-_iso8601_matches = [re.compile(regex).match for regex in _iso8601_re]
-try:
- del regex
-except NameError:
- pass
-def _parse_date_iso8601(dateString):
- '''Parse a variety of ISO-8601-compatible formats like 20040105'''
- m = None
- for _iso8601_match in _iso8601_matches:
- m = _iso8601_match(dateString)
- if m: break
- if not m: return
- if m.span() == (0, 0): return
- params = m.groupdict()
- ordinal = params.get('ordinal', 0)
- if ordinal:
- ordinal = int(ordinal)
- else:
- ordinal = 0
- year = params.get('year', '--')
- if not year or year == '--':
- year = time.gmtime()[0]
- elif len(year) == 2:
- # ISO 8601 assumes current century, i.e. 93 -> 2093, NOT 1993
- year = 100 * int(time.gmtime()[0] / 100) + int(year)
- else:
- year = int(year)
- month = params.get('month', '-')
- if not month or month == '-':
- # ordinals are NOT normalized by mktime, we simulate them
- # by setting month=1, day=ordinal
- if ordinal:
- month = 1
- else:
- month = time.gmtime()[1]
- month = int(month)
- day = params.get('day', 0)
- if not day:
- # see above
- if ordinal:
- day = ordinal
- elif params.get('century', 0) or \
- params.get('year', 0) or params.get('month', 0):
- day = 1
- else:
- day = time.gmtime()[2]
- else:
- day = int(day)
- # special case of the century - is the first year of the 21st century
- # 2000 or 2001 ? The debate goes on...
- if 'century' in params.keys():
- year = (int(params['century']) - 1) * 100 + 1
- # in ISO 8601 most fields are optional
- for field in ['hour', 'minute', 'second', 'tzhour', 'tzmin']:
- if not params.get(field, None):
- params[field] = 0
- hour = int(params.get('hour', 0))
- minute = int(params.get('minute', 0))
- second = int(float(params.get('second', 0)))
- # weekday is normalized by mktime(), we can ignore it
- weekday = 0
- daylight_savings_flag = -1
- tm = [year, month, day, hour, minute, second, weekday,
- ordinal, daylight_savings_flag]
- # ISO 8601 time zone adjustments
- tz = params.get('tz')
- if tz and tz != 'Z':
- if tz[0] == '-':
- tm[3] += int(params.get('tzhour', 0))
- tm[4] += int(params.get('tzmin', 0))
- elif tz[0] == '+':
- tm[3] -= int(params.get('tzhour', 0))
- tm[4] -= int(params.get('tzmin', 0))
- else:
- return None
- # Python's time.mktime() is a wrapper around the ANSI C mktime(3c)
- # which is guaranteed to normalize d/m/y/h/m/s.
- # Many implementations have bugs, but we'll pretend they don't.
- return time.localtime(time.mktime(tuple(tm)))
-registerDateHandler(_parse_date_iso8601)
-
-# 8-bit date handling routines written by ytrewq1.
-_korean_year = u'\ub144' # b3e2 in euc-kr
-_korean_month = u'\uc6d4' # bff9 in euc-kr
-_korean_day = u'\uc77c' # c0cf in euc-kr
-_korean_am = u'\uc624\uc804' # bfc0 c0fc in euc-kr
-_korean_pm = u'\uc624\ud6c4' # bfc0 c8c4 in euc-kr
-
-_korean_onblog_date_re = \
- re.compile('(\d{4})%s\s+(\d{2})%s\s+(\d{2})%s\s+(\d{2}):(\d{2}):(\d{2})' % \
- (_korean_year, _korean_month, _korean_day))
-_korean_nate_date_re = \
- re.compile(u'(\d{4})-(\d{2})-(\d{2})\s+(%s|%s)\s+(\d{,2}):(\d{,2}):(\d{,2})' % \
- (_korean_am, _korean_pm))
-def _parse_date_onblog(dateString):
- '''Parse a string according to the OnBlog 8-bit date format'''
- m = _korean_onblog_date_re.match(dateString)
- if not m: return
- w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % \
- {'year': m.group(1), 'month': m.group(2), 'day': m.group(3),\
- 'hour': m.group(4), 'minute': m.group(5), 'second': m.group(6),\
- 'zonediff': '+09:00'}
- if _debug: sys.stderr.write('OnBlog date parsed as: %s\n' % w3dtfdate)
- return _parse_date_w3dtf(w3dtfdate)
-registerDateHandler(_parse_date_onblog)
-
-def _parse_date_nate(dateString):
- '''Parse a string according to the Nate 8-bit date format'''
- m = _korean_nate_date_re.match(dateString)
- if not m: return
- hour = int(m.group(5))
- ampm = m.group(4)
- if (ampm == _korean_pm):
- hour += 12
- hour = str(hour)
- if len(hour) == 1:
- hour = '0' + hour
- w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % \
- {'year': m.group(1), 'month': m.group(2), 'day': m.group(3),\
- 'hour': hour, 'minute': m.group(6), 'second': m.group(7),\
- 'zonediff': '+09:00'}
- if _debug: sys.stderr.write('Nate date parsed as: %s\n' % w3dtfdate)
- return _parse_date_w3dtf(w3dtfdate)
-registerDateHandler(_parse_date_nate)
-
-_mssql_date_re = \
- re.compile('(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})(\.\d+)?')
-def _parse_date_mssql(dateString):
- '''Parse a string according to the MS SQL date format'''
- m = _mssql_date_re.match(dateString)
- if not m: return
- w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s:%(second)s%(zonediff)s' % \
- {'year': m.group(1), 'month': m.group(2), 'day': m.group(3),\
- 'hour': m.group(4), 'minute': m.group(5), 'second': m.group(6),\
- 'zonediff': '+09:00'}
- if _debug: sys.stderr.write('MS SQL date parsed as: %s\n' % w3dtfdate)
- return _parse_date_w3dtf(w3dtfdate)
-registerDateHandler(_parse_date_mssql)
-
-# Unicode strings for Greek date strings
-_greek_months = \
- { \
- u'\u0399\u03b1\u03bd': u'Jan', # c9e1ed in iso-8859-7
- u'\u03a6\u03b5\u03b2': u'Feb', # d6e5e2 in iso-8859-7
- u'\u039c\u03ac\u03ce': u'Mar', # ccdcfe in iso-8859-7
- u'\u039c\u03b1\u03ce': u'Mar', # cce1fe in iso-8859-7
- u'\u0391\u03c0\u03c1': u'Apr', # c1f0f1 in iso-8859-7
- u'\u039c\u03ac\u03b9': u'May', # ccdce9 in iso-8859-7
- u'\u039c\u03b1\u03ca': u'May', # cce1fa in iso-8859-7
- u'\u039c\u03b1\u03b9': u'May', # cce1e9 in iso-8859-7
- u'\u0399\u03bf\u03cd\u03bd': u'Jun', # c9effded in iso-8859-7
- u'\u0399\u03bf\u03bd': u'Jun', # c9efed in iso-8859-7
- u'\u0399\u03bf\u03cd\u03bb': u'Jul', # c9effdeb in iso-8859-7
- u'\u0399\u03bf\u03bb': u'Jul', # c9f9eb in iso-8859-7
- u'\u0391\u03cd\u03b3': u'Aug', # c1fde3 in iso-8859-7
- u'\u0391\u03c5\u03b3': u'Aug', # c1f5e3 in iso-8859-7
- u'\u03a3\u03b5\u03c0': u'Sep', # d3e5f0 in iso-8859-7
- u'\u039f\u03ba\u03c4': u'Oct', # cfeaf4 in iso-8859-7
- u'\u039d\u03bf\u03ad': u'Nov', # cdefdd in iso-8859-7
- u'\u039d\u03bf\u03b5': u'Nov', # cdefe5 in iso-8859-7
- u'\u0394\u03b5\u03ba': u'Dec', # c4e5ea in iso-8859-7
- }
-
-_greek_wdays = \
- { \
- u'\u039a\u03c5\u03c1': u'Sun', # caf5f1 in iso-8859-7
- u'\u0394\u03b5\u03c5': u'Mon', # c4e5f5 in iso-8859-7
- u'\u03a4\u03c1\u03b9': u'Tue', # d4f1e9 in iso-8859-7
- u'\u03a4\u03b5\u03c4': u'Wed', # d4e5f4 in iso-8859-7
- u'\u03a0\u03b5\u03bc': u'Thu', # d0e5ec in iso-8859-7
- u'\u03a0\u03b1\u03c1': u'Fri', # d0e1f1 in iso-8859-7
- u'\u03a3\u03b1\u03b2': u'Sat', # d3e1e2 in iso-8859-7
- }
-
-_greek_date_format_re = \
- re.compile(u'([^,]+),\s+(\d{2})\s+([^\s]+)\s+(\d{4})\s+(\d{2}):(\d{2}):(\d{2})\s+([^\s]+)')
-
-def _parse_date_greek(dateString):
- '''Parse a string according to a Greek 8-bit date format.'''
- m = _greek_date_format_re.match(dateString)
- if not m: return
- try:
- wday = _greek_wdays[m.group(1)]
- month = _greek_months[m.group(3)]
- except:
- return
- rfc822date = '%(wday)s, %(day)s %(month)s %(year)s %(hour)s:%(minute)s:%(second)s %(zonediff)s' % \
- {'wday': wday, 'day': m.group(2), 'month': month, 'year': m.group(4),\
- 'hour': m.group(5), 'minute': m.group(6), 'second': m.group(7),\
- 'zonediff': m.group(8)}
- if _debug: sys.stderr.write('Greek date parsed as: %s\n' % rfc822date)
- return _parse_date_rfc822(rfc822date)
-registerDateHandler(_parse_date_greek)
-
-# Unicode strings for Hungarian date strings
-_hungarian_months = \
- { \
- u'janu\u00e1r': u'01', # e1 in iso-8859-2
- u'febru\u00e1ri': u'02', # e1 in iso-8859-2
- u'm\u00e1rcius': u'03', # e1 in iso-8859-2
- u'\u00e1prilis': u'04', # e1 in iso-8859-2
- u'm\u00e1ujus': u'05', # e1 in iso-8859-2
- u'j\u00fanius': u'06', # fa in iso-8859-2
- u'j\u00falius': u'07', # fa in iso-8859-2
- u'augusztus': u'08',
- u'szeptember': u'09',
- u'okt\u00f3ber': u'10', # f3 in iso-8859-2
- u'november': u'11',
- u'december': u'12',
- }
-
-_hungarian_date_format_re = \
- re.compile(u'(\d{4})-([^-]+)-(\d{,2})T(\d{,2}):(\d{2})((\+|-)(\d{,2}:\d{2}))')
-
-def _parse_date_hungarian(dateString):
- '''Parse a string according to a Hungarian 8-bit date format.'''
- m = _hungarian_date_format_re.match(dateString)
- if not m: return
- try:
- month = _hungarian_months[m.group(2)]
- day = m.group(3)
- if len(day) == 1:
- day = '0' + day
- hour = m.group(4)
- if len(hour) == 1:
- hour = '0' + hour
- except:
- return
- w3dtfdate = '%(year)s-%(month)s-%(day)sT%(hour)s:%(minute)s%(zonediff)s' % \
- {'year': m.group(1), 'month': month, 'day': day,\
- 'hour': hour, 'minute': m.group(5),\
- 'zonediff': m.group(6)}
- if _debug: sys.stderr.write('Hungarian date parsed as: %s\n' % w3dtfdate)
- return _parse_date_w3dtf(w3dtfdate)
-registerDateHandler(_parse_date_hungarian)
-
-# W3DTF-style date parsing adapted from PyXML xml.utils.iso8601, written by
-# Drake and licensed under the Python license. Removed all range checking
-# for month, day, hour, minute, and second, since mktime will normalize
-# these later
-def _parse_date_w3dtf(dateString):
- def __extract_date(m):
- year = int(m.group('year'))
- if year < 100:
- year = 100 * int(time.gmtime()[0] / 100) + int(year)
- if year < 1000:
- return 0, 0, 0
- julian = m.group('julian')
- if julian:
- julian = int(julian)
- month = julian / 30 + 1
- day = julian % 30 + 1
- jday = None
- while jday != julian:
- t = time.mktime((year, month, day, 0, 0, 0, 0, 0, 0))
- jday = time.gmtime(t)[-2]
- diff = abs(jday - julian)
- if jday > julian:
- if diff < day:
- day = day - diff
- else:
- month = month - 1
- day = 31
- elif jday < julian:
- if day + diff < 28:
- day = day + diff
- else:
- month = month + 1
- return year, month, day
- month = m.group('month')
- day = 1
- if month is None:
- month = 1
- else:
- month = int(month)
- day = m.group('day')
- if day:
- day = int(day)
- else:
- day = 1
- return year, month, day
-
- def __extract_time(m):
- if not m:
- return 0, 0, 0
- hours = m.group('hours')
- if not hours:
- return 0, 0, 0
- hours = int(hours)
- minutes = int(m.group('minutes'))
- seconds = m.group('seconds')
- if seconds:
- seconds = int(seconds)
- else:
- seconds = 0
- return hours, minutes, seconds
-
- def __extract_tzd(m):
- '''Return the Time Zone Designator as an offset in seconds from UTC.'''
- if not m:
- return 0
- tzd = m.group('tzd')
- if not tzd:
- return 0
- if tzd == 'Z':
- return 0
- hours = int(m.group('tzdhours'))
- minutes = m.group('tzdminutes')
- if minutes:
- minutes = int(minutes)
- else:
- minutes = 0
- offset = (hours*60 + minutes) * 60
- if tzd[0] == '+':
- return -offset
- return offset
-
- __date_re = ('(?P<year>\d\d\d\d)'
- '(?:(?P<dsep>-|)'
- '(?:(?P<month>\d\d)(?:(?P=dsep)(?P<day>\d\d))?'
- '|(?P<julian>\d\d\d)))?')
- __tzd_re = '(?P<tzd>[-+](?P<tzdhours>\d\d)(?::?(?P<tzdminutes>\d\d))|Z)'
- __tzd_rx = re.compile(__tzd_re)
- __time_re = ('(?P<hours>\d\d)(?P<tsep>:|)(?P<minutes>\d\d)'
- '(?:(?P=tsep)(?P<seconds>\d\d)(?:[.,]\d+)?)?'
- + __tzd_re)
- __datetime_re = '%s(?:T%s)?' % (__date_re, __time_re)
- __datetime_rx = re.compile(__datetime_re)
- m = __datetime_rx.match(dateString)
- if (m is None) or (m.group() != dateString): return
- gmt = __extract_date(m) + __extract_time(m) + (0, 0, 0)
- if gmt[0] == 0: return
- return time.gmtime(time.mktime(gmt) + __extract_tzd(m) - time.timezone)
-registerDateHandler(_parse_date_w3dtf)
-
-def _parse_date_rfc822(dateString):
- '''Parse an RFC822, RFC1123, RFC2822, or asctime-style date'''
- data = dateString.split()
- if data[0][-1] in (',', '.') or data[0].lower() in rfc822._daynames:
- del data[0]
- if len(data) == 4:
- s = data[3]
- i = s.find('+')
- if i > 0:
- data[3:] = [s[:i], s[i+1:]]
- else:
- data.append('')
- dateString = " ".join(data)
- # Account for the Etc/GMT timezone by stripping 'Etc/'
- elif len(data) == 5 and data[4].lower().startswith('etc/'):
- data[4] = data[4][4:]
- dateString = " ".join(data)
- if len(data) < 5:
- dateString += ' 00:00:00 GMT'
- tm = rfc822.parsedate_tz(dateString)
- if tm:
- return time.gmtime(rfc822.mktime_tz(tm))
-# rfc822.py defines several time zones, but we define some extra ones.
-# 'ET' is equivalent to 'EST', etc.
-_additional_timezones = {'AT': -400, 'ET': -500, 'CT': -600, 'MT': -700, 'PT': -800}
-rfc822._timezones.update(_additional_timezones)
-registerDateHandler(_parse_date_rfc822)
-
-def _parse_date_perforce(aDateString):
- """parse a date in yyyy/mm/dd hh:mm:ss TTT format"""
- # Fri, 2006/09/15 08:19:53 EDT
- _my_date_pattern = re.compile( \
- r'(\w{,3}), (\d{,4})/(\d{,2})/(\d{2}) (\d{,2}):(\d{2}):(\d{2}) (\w{,3})')
-
- dow, year, month, day, hour, minute, second, tz = \
- _my_date_pattern.search(aDateString).groups()
- months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
- dateString = "%s, %s %s %s %s:%s:%s %s" % (dow, day, months[int(month) - 1], year, hour, minute, second, tz)
- tm = rfc822.parsedate_tz(dateString)
- if tm:
- return time.gmtime(rfc822.mktime_tz(tm))
-registerDateHandler(_parse_date_perforce)
-
-def _parse_date(dateString):
- '''Parses a variety of date formats into a 9-tuple in GMT'''
- for handler in _date_handlers:
- try:
- date9tuple = handler(dateString)
- if not date9tuple: continue
- if len(date9tuple) != 9:
- if _debug: sys.stderr.write('date handler function must return 9-tuple\n')
- raise ValueError
- map(int, date9tuple)
- return date9tuple
- except Exception, e:
- if _debug: sys.stderr.write('%s raised %s\n' % (handler.__name__, repr(e)))
- pass
- return None
-
-def _getCharacterEncoding(http_headers, xml_data):
- '''Get the character encoding of the XML document
-
- http_headers is a dictionary
- xml_data is a raw string (not Unicode)
-
- This is so much trickier than it sounds, it's not even funny.
- According to RFC 3023 ('XML Media Types'), if the HTTP Content-Type
- is application/xml, application/*+xml,
- application/xml-external-parsed-entity, or application/xml-dtd,
- the encoding given in the charset parameter of the HTTP Content-Type
- takes precedence over the encoding given in the XML prefix within the
- document, and defaults to 'utf-8' if neither are specified. But, if
- the HTTP Content-Type is text/xml, text/*+xml, or
- text/xml-external-parsed-entity, the encoding given in the XML prefix
- within the document is ALWAYS IGNORED and only the encoding given in
- the charset parameter of the HTTP Content-Type header should be
- respected, and it defaults to 'us-ascii' if not specified.
-
- Furthermore, discussion on the atom-syntax mailing list with the
- author of RFC 3023 leads me to the conclusion that any document
- served with a Content-Type of text/* and no charset parameter
- must be treated as us-ascii. (We now do this.) And also that it
- must always be flagged as non-well-formed. (We now do this too.)
-
- If Content-Type is unspecified (input was local file or non-HTTP source)
- or unrecognized (server just got it totally wrong), then go by the
- encoding given in the XML prefix of the document and default to
- 'iso-8859-1' as per the HTTP specification (RFC 2616).
-
- Then, assuming we didn't find a character encoding in the HTTP headers
- (and the HTTP Content-type allowed us to look in the body), we need
- to sniff the first few bytes of the XML data and try to determine
- whether the encoding is ASCII-compatible. Section F of the XML
- specification shows the way here:
- http://www.w3.org/TR/REC-xml/#sec-guessing-no-ext-info
-
- If the sniffed encoding is not ASCII-compatible, we need to make it
- ASCII compatible so that we can sniff further into the XML declaration
- to find the encoding attribute, which will tell us the true encoding.
-
- Of course, none of this guarantees that we will be able to parse the
- feed in the declared character encoding (assuming it was declared
- correctly, which many are not). CJKCodecs and iconv_codec help a lot;
- you should definitely install them if you can.
- http://cjkpython.i18n.org/
- '''
-
- def _parseHTTPContentType(content_type):
- '''takes HTTP Content-Type header and returns (content type, charset)
-
- If no charset is specified, returns (content type, '')
- If no content type is specified, returns ('', '')
- Both return parameters are guaranteed to be lowercase strings
- '''
- content_type = content_type or ''
- content_type, params = cgi.parse_header(content_type)
- return content_type, params.get('charset', '').replace("'", '')
-
- sniffed_xml_encoding = ''
- xml_encoding = ''
- true_encoding = ''
- http_content_type, http_encoding = _parseHTTPContentType(http_headers.get('content-type', http_headers.get('Content-type')))
- # Must sniff for non-ASCII-compatible character encodings before
- # searching for XML declaration. This heuristic is defined in
- # section F of the XML specification:
- # http://www.w3.org/TR/REC-xml/#sec-guessing-no-ext-info
- try:
- if xml_data[:4] == _l2bytes([0x4c, 0x6f, 0xa7, 0x94]):
- # EBCDIC
- xml_data = _ebcdic_to_ascii(xml_data)
- elif xml_data[:4] == _l2bytes([0x00, 0x3c, 0x00, 0x3f]):
- # UTF-16BE
- sniffed_xml_encoding = 'utf-16be'
- xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
- elif (len(xml_data) >= 4) and (xml_data[:2] == _l2bytes([0xfe, 0xff])) and (xml_data[2:4] != _l2bytes([0x00, 0x00])):
- # UTF-16BE with BOM
- sniffed_xml_encoding = 'utf-16be'
- xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
- elif xml_data[:4] == _l2bytes([0x3c, 0x00, 0x3f, 0x00]):
- # UTF-16LE
- sniffed_xml_encoding = 'utf-16le'
- xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
- elif (len(xml_data) >= 4) and (xml_data[:2] == _l2bytes([0xff, 0xfe])) and (xml_data[2:4] != _l2bytes([0x00, 0x00])):
- # UTF-16LE with BOM
- sniffed_xml_encoding = 'utf-16le'
- xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
- elif xml_data[:4] == _l2bytes([0x00, 0x00, 0x00, 0x3c]):
- # UTF-32BE
- sniffed_xml_encoding = 'utf-32be'
- xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
- elif xml_data[:4] == _l2bytes([0x3c, 0x00, 0x00, 0x00]):
- # UTF-32LE
- sniffed_xml_encoding = 'utf-32le'
- xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
- elif xml_data[:4] == _l2bytes([0x00, 0x00, 0xfe, 0xff]):
- # UTF-32BE with BOM
- sniffed_xml_encoding = 'utf-32be'
- xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
- elif xml_data[:4] == _l2bytes([0xff, 0xfe, 0x00, 0x00]):
- # UTF-32LE with BOM
- sniffed_xml_encoding = 'utf-32le'
- xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
- elif xml_data[:3] == _l2bytes([0xef, 0xbb, 0xbf]):
- # UTF-8 with BOM
- sniffed_xml_encoding = 'utf-8'
- xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
- else:
- # ASCII-compatible
- pass
- xml_encoding_match = re.compile(_s2bytes('^<\?.*encoding=[\'"](.*?)[\'"].*\?>')).match(xml_data)
- except:
- xml_encoding_match = None
- if xml_encoding_match:
- xml_encoding = xml_encoding_match.groups()[0].decode('utf-8').lower()
- if sniffed_xml_encoding and (xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode', 'iso-10646-ucs-4', 'ucs-4', 'csucs4', 'utf-16', 'utf-32', 'utf_16', 'utf_32', 'utf16', 'u16')):
- xml_encoding = sniffed_xml_encoding
- acceptable_content_type = 0
- application_content_types = ('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity')
- text_content_types = ('text/xml', 'text/xml-external-parsed-entity')
- if (http_content_type in application_content_types) or \
- (http_content_type.startswith('application/') and http_content_type.endswith('+xml')):
- acceptable_content_type = 1
- true_encoding = http_encoding or xml_encoding or 'utf-8'
- elif (http_content_type in text_content_types) or \
- (http_content_type.startswith('text/')) and http_content_type.endswith('+xml'):
- acceptable_content_type = 1
- true_encoding = http_encoding or 'us-ascii'
- elif http_content_type.startswith('text/'):
- true_encoding = http_encoding or 'us-ascii'
- elif http_headers and (not (http_headers.has_key('content-type') or http_headers.has_key('Content-type'))):
- true_encoding = xml_encoding or 'iso-8859-1'
- else:
- true_encoding = xml_encoding or 'utf-8'
- # some feeds claim to be gb2312 but are actually gb18030.
- # apparently MSIE and Firefox both do the following switch:
- if true_encoding.lower() == 'gb2312':
- true_encoding = 'gb18030'
- return true_encoding, http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type
-
-def _toUTF8(data, encoding):
- '''Changes an XML data stream on the fly to specify a new encoding
-
- data is a raw sequence of bytes (not Unicode) that is presumed to be in %encoding already
- encoding is a string recognized by encodings.aliases
- '''
- if _debug: sys.stderr.write('entering _toUTF8, trying encoding %s\n' % encoding)
- # strip Byte Order Mark (if present)
- if (len(data) >= 4) and (data[:2] == _l2bytes([0xfe, 0xff])) and (data[2:4] != _l2bytes([0x00, 0x00])):
- if _debug:
- sys.stderr.write('stripping BOM\n')
- if encoding != 'utf-16be':
- sys.stderr.write('trying utf-16be instead\n')
- encoding = 'utf-16be'
- data = data[2:]
- elif (len(data) >= 4) and (data[:2] == _l2bytes([0xff, 0xfe])) and (data[2:4] != _l2bytes([0x00, 0x00])):
- if _debug:
- sys.stderr.write('stripping BOM\n')
- if encoding != 'utf-16le':
- sys.stderr.write('trying utf-16le instead\n')
- encoding = 'utf-16le'
- data = data[2:]
- elif data[:3] == _l2bytes([0xef, 0xbb, 0xbf]):
- if _debug:
- sys.stderr.write('stripping BOM\n')
- if encoding != 'utf-8':
- sys.stderr.write('trying utf-8 instead\n')
- encoding = 'utf-8'
- data = data[3:]
- elif data[:4] == _l2bytes([0x00, 0x00, 0xfe, 0xff]):
- if _debug:
- sys.stderr.write('stripping BOM\n')
- if encoding != 'utf-32be':
- sys.stderr.write('trying utf-32be instead\n')
- encoding = 'utf-32be'
- data = data[4:]
- elif data[:4] == _l2bytes([0xff, 0xfe, 0x00, 0x00]):
- if _debug:
- sys.stderr.write('stripping BOM\n')
- if encoding != 'utf-32le':
- sys.stderr.write('trying utf-32le instead\n')
- encoding = 'utf-32le'
- data = data[4:]
- newdata = unicode(data, encoding)
- if _debug: sys.stderr.write('successfully converted %s data to unicode\n' % encoding)
- declmatch = re.compile('^<\?xml[^>]*?>')
- newdecl = '''<?xml version='1.0' encoding='utf-8'?>'''
- if declmatch.search(newdata):
- newdata = declmatch.sub(newdecl, newdata)
- else:
- newdata = newdecl + u'\n' + newdata
- return newdata.encode('utf-8')
-
-def _stripDoctype(data):
- '''Strips DOCTYPE from XML document, returns (rss_version, stripped_data)
-
- rss_version may be 'rss091n' or None
- stripped_data is the same XML document, minus the DOCTYPE
- '''
- start = re.search(_s2bytes('<\w'), data)
- start = start and start.start() or -1
- head,data = data[:start+1], data[start+1:]
-
- entity_pattern = re.compile(_s2bytes(r'^\s*<!ENTITY([^>]*?)>'), re.MULTILINE)
- entity_results=entity_pattern.findall(head)
- head = entity_pattern.sub(_s2bytes(''), head)
- doctype_pattern = re.compile(_s2bytes(r'^\s*<!DOCTYPE([^>]*?)>'), re.MULTILINE)
- doctype_results = doctype_pattern.findall(head)
- doctype = doctype_results and doctype_results[0] or _s2bytes('')
- if doctype.lower().count(_s2bytes('netscape')):
- version = 'rss091n'
- else:
- version = None
-
- # only allow in 'safe' inline entity definitions
- replacement=_s2bytes('')
- if len(doctype_results)==1 and entity_results:
- safe_pattern=re.compile(_s2bytes('\s+(\w+)\s+"(&#\w+;|[^&"]*)"'))
- safe_entities=filter(lambda e: safe_pattern.match(e),entity_results)
- if safe_entities:
- replacement=_s2bytes('<!DOCTYPE feed [\n <!ENTITY') + _s2bytes('>\n <!ENTITY ').join(safe_entities) + _s2bytes('>\n]>')
- data = doctype_pattern.sub(replacement, head) + data
-
- return version, data, dict(replacement and [(k.decode('utf-8'), v.decode('utf-8')) for k, v in safe_pattern.findall(replacement)])
-
-def parse(url_file_stream_or_string, etag=None, modified=None, agent=None, referrer=None, handlers=[], request_headers={}, response_headers={}):
- '''Parse a feed from a URL, file, stream, or string.
-
- request_headers, if given, is a dict from http header name to value to add
- to the request; this overrides internally generated values.
- '''
- result = FeedParserDict()
- result['feed'] = FeedParserDict()
- result['entries'] = []
- if _XML_AVAILABLE:
- result['bozo'] = 0
- if not isinstance(handlers, list):
- handlers = [handlers]
- try:
- f = _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, handlers, request_headers)
- data = f.read()
- except Exception, e:
- result['bozo'] = 1
- result['bozo_exception'] = e
- data = None
- f = None
-
- if hasattr(f, 'headers'):
- result['headers'] = dict(f.headers)
- # overwrite existing headers using response_headers
- if 'headers' in result:
- result['headers'].update(response_headers)
- elif response_headers:
- result['headers'] = copy.deepcopy(response_headers)
-
- # if feed is gzip-compressed, decompress it
- if f and data and 'headers' in result:
- if gzip and result['headers'].get('content-encoding') == 'gzip':
- try:
- data = gzip.GzipFile(fileobj=_StringIO(data)).read()
- except Exception, e:
- # Some feeds claim to be gzipped but they're not, so
- # we get garbage. Ideally, we should re-request the
- # feed without the 'Accept-encoding: gzip' header,
- # but we don't.
- result['bozo'] = 1
- result['bozo_exception'] = e
- data = ''
- elif zlib and result['headers'].get('content-encoding') == 'deflate':
- try:
- data = zlib.decompress(data, -zlib.MAX_WBITS)
- except Exception, e:
- result['bozo'] = 1
- result['bozo_exception'] = e
- data = ''
-
- # save HTTP headers
- if 'headers' in result:
- if 'etag' in result['headers'] or 'ETag' in result['headers']:
- etag = result['headers'].get('etag', result['headers'].get('ETag'))
- if etag:
- result['etag'] = etag
- if 'last-modified' in result['headers'] or 'Last-Modified' in result['headers']:
- modified = result['headers'].get('last-modified', result['headers'].get('Last-Modified'))
- if modified:
- result['modified'] = _parse_date(modified)
- if hasattr(f, 'url'):
- result['href'] = f.url
- result['status'] = 200
- if hasattr(f, 'status'):
- result['status'] = f.status
- if hasattr(f, 'close'):
- f.close()
-
- # there are four encodings to keep track of:
- # - http_encoding is the encoding declared in the Content-Type HTTP header
- # - xml_encoding is the encoding declared in the <?xml declaration
- # - sniffed_encoding is the encoding sniffed from the first 4 bytes of the XML data
- # - result['encoding'] is the actual encoding, as per RFC 3023 and a variety of other conflicting specifications
- http_headers = result.get('headers', {})
- result['encoding'], http_encoding, xml_encoding, sniffed_xml_encoding, acceptable_content_type = \
- _getCharacterEncoding(http_headers, data)
- if http_headers and (not acceptable_content_type):
- if http_headers.has_key('content-type') or http_headers.has_key('Content-type'):
- bozo_message = '%s is not an XML media type' % http_headers.get('content-type', http_headers.get('Content-type'))
- else:
- bozo_message = 'no Content-type specified'
- result['bozo'] = 1
- result['bozo_exception'] = NonXMLContentType(bozo_message)
-
- if data is not None:
- result['version'], data, entities = _stripDoctype(data)
-
- # ensure that baseuri is an absolute uri using an acceptable URI scheme
- contentloc = http_headers.get('content-location', http_headers.get('Content-Location', ''))
- href = result.get('href', '')
- baseuri = _makeSafeAbsoluteURI(href, contentloc) or _makeSafeAbsoluteURI(contentloc) or href
-
- baselang = http_headers.get('content-language', http_headers.get('Content-Language', None))
-
- # if server sent 304, we're done
- if result.get('status', 0) == 304:
- result['version'] = ''
- result['debug_message'] = 'The feed has not changed since you last checked, ' + \
- 'so the server sent no data. This is a feature, not a bug!'
- return result
-
- # if there was a problem downloading, we're done
- if data is None:
- return result
-
- # determine character encoding
- use_strict_parser = 0
- known_encoding = 0
- tried_encodings = []
- # try: HTTP encoding, declared XML encoding, encoding sniffed from BOM
- for proposed_encoding in (result['encoding'], xml_encoding, sniffed_xml_encoding):
- if not proposed_encoding: continue
- if proposed_encoding in tried_encodings: continue
- tried_encodings.append(proposed_encoding)
- try:
- data = _toUTF8(data, proposed_encoding)
- known_encoding = use_strict_parser = 1
- break
- except:
- pass
- # if no luck and we have auto-detection library, try that
- if (not known_encoding) and chardet:
- try:
- proposed_encoding = chardet.detect(data)['encoding']
- if proposed_encoding and (proposed_encoding not in tried_encodings):
- tried_encodings.append(proposed_encoding)
- data = _toUTF8(data, proposed_encoding)
- known_encoding = use_strict_parser = 1
- except:
- pass
- # if still no luck and we haven't tried utf-8 yet, try that
- if (not known_encoding) and ('utf-8' not in tried_encodings):
- try:
- proposed_encoding = 'utf-8'
- tried_encodings.append(proposed_encoding)
- data = _toUTF8(data, proposed_encoding)
- known_encoding = use_strict_parser = 1
- except:
- pass
- # if still no luck and we haven't tried windows-1252 yet, try that
- if (not known_encoding) and ('windows-1252' not in tried_encodings):
- try:
- proposed_encoding = 'windows-1252'
- tried_encodings.append(proposed_encoding)
- data = _toUTF8(data, proposed_encoding)
- known_encoding = use_strict_parser = 1
- except:
- pass
- # if still no luck and we haven't tried iso-8859-2 yet, try that.
- if (not known_encoding) and ('iso-8859-2' not in tried_encodings):
- try:
- proposed_encoding = 'iso-8859-2'
- tried_encodings.append(proposed_encoding)
- data = _toUTF8(data, proposed_encoding)
- known_encoding = use_strict_parser = 1
- except:
- pass
- # if still no luck, give up
- if not known_encoding:
- result['bozo'] = 1
- result['bozo_exception'] = CharacterEncodingUnknown( \
- 'document encoding unknown, I tried ' + \
- '%s, %s, utf-8, windows-1252, and iso-8859-2 but nothing worked' % \
- (result['encoding'], xml_encoding))
- result['encoding'] = ''
- elif proposed_encoding != result['encoding']:
- result['bozo'] = 1
- result['bozo_exception'] = CharacterEncodingOverride( \
- 'document declared as %s, but parsed as %s' % \
- (result['encoding'], proposed_encoding))
- result['encoding'] = proposed_encoding
-
- if not _XML_AVAILABLE:
- use_strict_parser = 0
- if use_strict_parser:
- # initialize the SAX parser
- feedparser = _StrictFeedParser(baseuri, baselang, 'utf-8')
- saxparser = xml.sax.make_parser(PREFERRED_XML_PARSERS)
- saxparser.setFeature(xml.sax.handler.feature_namespaces, 1)
- saxparser.setContentHandler(feedparser)
- saxparser.setErrorHandler(feedparser)
- source = xml.sax.xmlreader.InputSource()
- source.setByteStream(_StringIO(data))
- if hasattr(saxparser, '_ns_stack'):
- # work around bug in built-in SAX parser (doesn't recognize xml: namespace)
- # PyXML doesn't have this problem, and it doesn't have _ns_stack either
- saxparser._ns_stack.append({'http://www.w3.org/XML/1998/namespace':'xml'})
- try:
- saxparser.parse(source)
- except Exception, e:
- if _debug:
- import traceback
- traceback.print_stack()
- traceback.print_exc()
- sys.stderr.write('xml parsing failed\n')
- result['bozo'] = 1
- result['bozo_exception'] = feedparser.exc or e
- use_strict_parser = 0
- if not use_strict_parser:
- feedparser = _LooseFeedParser(baseuri, baselang, 'utf-8', entities)
- feedparser.feed(data.decode('utf-8', 'replace'))
- result['feed'] = feedparser.feeddata
- result['entries'] = feedparser.entries
- result['version'] = result['version'] or feedparser.version
- result['namespaces'] = feedparser.namespacesInUse
- return result
-
-class Serializer:
- def __init__(self, results):
- self.results = results
-
-class TextSerializer(Serializer):
- def write(self, stream=sys.stdout):
- self._writer(stream, self.results, '')
-
- def _writer(self, stream, node, prefix):
- if not node: return
- if hasattr(node, 'keys'):
- keys = node.keys()
- keys.sort()
- for k in keys:
- if k in ('description', 'link'): continue
- if node.has_key(k + '_detail'): continue
- if node.has_key(k + '_parsed'): continue
- self._writer(stream, node[k], prefix + k + '.')
- elif type(node) == types.ListType:
- index = 0
- for n in node:
- self._writer(stream, n, prefix[:-1] + '[' + str(index) + '].')
- index += 1
- else:
- try:
- s = str(node).encode('utf-8')
- s = s.replace('\\', '\\\\')
- s = s.replace('\r', '')
- s = s.replace('\n', r'\n')
- stream.write(prefix[:-1])
- stream.write('=')
- stream.write(s)
- stream.write('\n')
- except:
- pass
-
-class PprintSerializer(Serializer):
- def write(self, stream=sys.stdout):
- if self.results.has_key('href'):
- stream.write(self.results['href'] + '\n\n')
- from pprint import pprint
- pprint(self.results, stream)
- stream.write('\n')
-
-if __name__ == '__main__':
- try:
- from optparse import OptionParser
- except:
- OptionParser = None
-
- if OptionParser:
- optionParser = OptionParser(version=__version__, usage="%prog [options] url_or_filename_or_-")
- optionParser.set_defaults(format="pprint")
- optionParser.add_option("-A", "--user-agent", dest="agent", metavar="AGENT", help="User-Agent for HTTP URLs")
- optionParser.add_option("-e", "--referer", "--referrer", dest="referrer", metavar="URL", help="Referrer for HTTP URLs")
- optionParser.add_option("-t", "--etag", dest="etag", metavar="TAG", help="ETag/If-None-Match for HTTP URLs")
- optionParser.add_option("-m", "--last-modified", dest="modified", metavar="DATE", help="Last-modified/If-Modified-Since for HTTP URLs (any supported date format)")
- optionParser.add_option("-f", "--format", dest="format", metavar="FORMAT", help="output results in FORMAT (text, pprint)")
- optionParser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="write debugging information to stderr")
- (options, urls) = optionParser.parse_args()
- if options.verbose:
- _debug = 1
- if not urls:
- optionParser.print_help()
- sys.exit(0)
- else:
- if not sys.argv[1:]:
- print __doc__
- sys.exit(0)
- class _Options:
- etag = modified = agent = referrer = None
- format = 'pprint'
- options = _Options()
- urls = sys.argv[1:]
-
- zopeCompatibilityHack()
-
- serializer = globals().get(options.format.capitalize() + 'Serializer', Serializer)
- for url in urls:
- results = parse(url, etag=options.etag, modified=options.modified, agent=options.agent, referrer=options.referrer)
- serializer(results).write(sys.stdout)
diff --git a/module/lib/jinja2/__init__.py b/module/lib/jinja2/__init__.py
deleted file mode 100644
index f944e11b6..000000000
--- a/module/lib/jinja2/__init__.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2
- ~~~~~~
-
- Jinja2 is a template engine written in pure Python. It provides a
- Django inspired non-XML syntax but supports inline expressions and
- an optional sandboxed environment.
-
- Nutshell
- --------
-
- Here a small example of a Jinja2 template::
-
- {% extends 'base.html' %}
- {% block title %}Memberlist{% endblock %}
- {% block content %}
- <ul>
- {% for user in users %}
- <li><a href="{{ user.url }}">{{ user.username }}</a></li>
- {% endfor %}
- </ul>
- {% endblock %}
-
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-__docformat__ = 'restructuredtext en'
-try:
- __version__ = __import__('pkg_resources') \
- .get_distribution('Jinja2').version
-except:
- __version__ = 'unknown'
-
-# high level interface
-from jinja2.environment import Environment, Template
-
-# loaders
-from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \
- DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \
- ModuleLoader
-
-# bytecode caches
-from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \
- MemcachedBytecodeCache
-
-# undefined types
-from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined
-
-# exceptions
-from jinja2.exceptions import TemplateError, UndefinedError, \
- TemplateNotFound, TemplatesNotFound, TemplateSyntaxError, \
- TemplateAssertionError
-
-# decorators and public utilities
-from jinja2.filters import environmentfilter, contextfilter, \
- evalcontextfilter
-from jinja2.utils import Markup, escape, clear_caches, \
- environmentfunction, evalcontextfunction, contextfunction, \
- is_undefined
-
-__all__ = [
- 'Environment', 'Template', 'BaseLoader', 'FileSystemLoader',
- 'PackageLoader', 'DictLoader', 'FunctionLoader', 'PrefixLoader',
- 'ChoiceLoader', 'BytecodeCache', 'FileSystemBytecodeCache',
- 'MemcachedBytecodeCache', 'Undefined', 'DebugUndefined',
- 'StrictUndefined', 'TemplateError', 'UndefinedError', 'TemplateNotFound',
- 'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError',
- 'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape',
- 'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined',
- 'evalcontextfilter', 'evalcontextfunction'
-]
diff --git a/module/lib/jinja2/_markupsafe/__init__.py b/module/lib/jinja2/_markupsafe/__init__.py
deleted file mode 100644
index ec7bd572d..000000000
--- a/module/lib/jinja2/_markupsafe/__init__.py
+++ /dev/null
@@ -1,225 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- markupsafe
- ~~~~~~~~~~
-
- Implements a Markup string.
-
- :copyright: (c) 2010 by Armin Ronacher.
- :license: BSD, see LICENSE for more details.
-"""
-import re
-from itertools import imap
-
-
-__all__ = ['Markup', 'soft_unicode', 'escape', 'escape_silent']
-
-
-_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
-_entity_re = re.compile(r'&([^;]+);')
-
-
-class Markup(unicode):
- r"""Marks a string as being safe for inclusion in HTML/XML output without
- needing to be escaped. This implements the `__html__` interface a couple
- of frameworks and web applications use. :class:`Markup` is a direct
- subclass of `unicode` and provides all the methods of `unicode` just that
- it escapes arguments passed and always returns `Markup`.
-
- The `escape` function returns markup objects so that double escaping can't
- happen.
-
- The constructor of the :class:`Markup` class can be used for three
- different things: When passed an unicode object it's assumed to be safe,
- when passed an object with an HTML representation (has an `__html__`
- method) that representation is used, otherwise the object passed is
- converted into a unicode string and then assumed to be safe:
-
- >>> Markup("Hello <em>World</em>!")
- Markup(u'Hello <em>World</em>!')
- >>> class Foo(object):
- ... def __html__(self):
- ... return '<a href="#">foo</a>'
- ...
- >>> Markup(Foo())
- Markup(u'<a href="#">foo</a>')
-
- If you want object passed being always treated as unsafe you can use the
- :meth:`escape` classmethod to create a :class:`Markup` object:
-
- >>> Markup.escape("Hello <em>World</em>!")
- Markup(u'Hello &lt;em&gt;World&lt;/em&gt;!')
-
- Operations on a markup string are markup aware which means that all
- arguments are passed through the :func:`escape` function:
-
- >>> em = Markup("<em>%s</em>")
- >>> em % "foo & bar"
- Markup(u'<em>foo &amp; bar</em>')
- >>> strong = Markup("<strong>%(text)s</strong>")
- >>> strong % {'text': '<blink>hacker here</blink>'}
- Markup(u'<strong>&lt;blink&gt;hacker here&lt;/blink&gt;</strong>')
- >>> Markup("<em>Hello</em> ") + "<foo>"
- Markup(u'<em>Hello</em> &lt;foo&gt;')
- """
- __slots__ = ()
-
- def __new__(cls, base=u'', encoding=None, errors='strict'):
- if hasattr(base, '__html__'):
- base = base.__html__()
- if encoding is None:
- return unicode.__new__(cls, base)
- return unicode.__new__(cls, base, encoding, errors)
-
- def __html__(self):
- return self
-
- def __add__(self, other):
- if hasattr(other, '__html__') or isinstance(other, basestring):
- return self.__class__(unicode(self) + unicode(escape(other)))
- return NotImplemented
-
- def __radd__(self, other):
- if hasattr(other, '__html__') or isinstance(other, basestring):
- return self.__class__(unicode(escape(other)) + unicode(self))
- return NotImplemented
-
- def __mul__(self, num):
- if isinstance(num, (int, long)):
- return self.__class__(unicode.__mul__(self, num))
- return NotImplemented
- __rmul__ = __mul__
-
- def __mod__(self, arg):
- if isinstance(arg, tuple):
- arg = tuple(imap(_MarkupEscapeHelper, arg))
- else:
- arg = _MarkupEscapeHelper(arg)
- return self.__class__(unicode.__mod__(self, arg))
-
- def __repr__(self):
- return '%s(%s)' % (
- self.__class__.__name__,
- unicode.__repr__(self)
- )
-
- def join(self, seq):
- return self.__class__(unicode.join(self, imap(escape, seq)))
- join.__doc__ = unicode.join.__doc__
-
- def split(self, *args, **kwargs):
- return map(self.__class__, unicode.split(self, *args, **kwargs))
- split.__doc__ = unicode.split.__doc__
-
- def rsplit(self, *args, **kwargs):
- return map(self.__class__, unicode.rsplit(self, *args, **kwargs))
- rsplit.__doc__ = unicode.rsplit.__doc__
-
- def splitlines(self, *args, **kwargs):
- return map(self.__class__, unicode.splitlines(self, *args, **kwargs))
- splitlines.__doc__ = unicode.splitlines.__doc__
-
- def unescape(self):
- r"""Unescape markup again into an unicode string. This also resolves
- known HTML4 and XHTML entities:
-
- >>> Markup("Main &raquo; <em>About</em>").unescape()
- u'Main \xbb <em>About</em>'
- """
- from jinja2._markupsafe._constants import HTML_ENTITIES
- def handle_match(m):
- name = m.group(1)
- if name in HTML_ENTITIES:
- return unichr(HTML_ENTITIES[name])
- try:
- if name[:2] in ('#x', '#X'):
- return unichr(int(name[2:], 16))
- elif name.startswith('#'):
- return unichr(int(name[1:]))
- except ValueError:
- pass
- return u''
- return _entity_re.sub(handle_match, unicode(self))
-
- def striptags(self):
- r"""Unescape markup into an unicode string and strip all tags. This
- also resolves known HTML4 and XHTML entities. Whitespace is
- normalized to one:
-
- >>> Markup("Main &raquo; <em>About</em>").striptags()
- u'Main \xbb About'
- """
- stripped = u' '.join(_striptags_re.sub('', self).split())
- return Markup(stripped).unescape()
-
- @classmethod
- def escape(cls, s):
- """Escape the string. Works like :func:`escape` with the difference
- that for subclasses of :class:`Markup` this function would return the
- correct subclass.
- """
- rv = escape(s)
- if rv.__class__ is not cls:
- return cls(rv)
- return rv
-
- def make_wrapper(name):
- orig = getattr(unicode, name)
- def func(self, *args, **kwargs):
- args = _escape_argspec(list(args), enumerate(args))
- _escape_argspec(kwargs, kwargs.iteritems())
- return self.__class__(orig(self, *args, **kwargs))
- func.__name__ = orig.__name__
- func.__doc__ = orig.__doc__
- return func
-
- for method in '__getitem__', 'capitalize', \
- 'title', 'lower', 'upper', 'replace', 'ljust', \
- 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \
- 'translate', 'expandtabs', 'swapcase', 'zfill':
- locals()[method] = make_wrapper(method)
-
- # new in python 2.5
- if hasattr(unicode, 'partition'):
- partition = make_wrapper('partition'),
- rpartition = make_wrapper('rpartition')
-
- # new in python 2.6
- if hasattr(unicode, 'format'):
- format = make_wrapper('format')
-
- # not in python 3
- if hasattr(unicode, '__getslice__'):
- __getslice__ = make_wrapper('__getslice__')
-
- del method, make_wrapper
-
-
-def _escape_argspec(obj, iterable):
- """Helper for various string-wrapped functions."""
- for key, value in iterable:
- if hasattr(value, '__html__') or isinstance(value, basestring):
- obj[key] = escape(value)
- return obj
-
-
-class _MarkupEscapeHelper(object):
- """Helper for Markup.__mod__"""
-
- def __init__(self, obj):
- self.obj = obj
-
- __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x])
- __str__ = lambda s: str(escape(s.obj))
- __unicode__ = lambda s: unicode(escape(s.obj))
- __repr__ = lambda s: str(escape(repr(s.obj)))
- __int__ = lambda s: int(s.obj)
- __float__ = lambda s: float(s.obj)
-
-
-# we have to import it down here as the speedups and native
-# modules imports the markup type which is define above.
-try:
- from jinja2._markupsafe._speedups import escape, escape_silent, soft_unicode
-except ImportError:
- from jinja2._markupsafe._native import escape, escape_silent, soft_unicode
diff --git a/module/lib/jinja2/_markupsafe/_bundle.py b/module/lib/jinja2/_markupsafe/_bundle.py
deleted file mode 100644
index e694faf23..000000000
--- a/module/lib/jinja2/_markupsafe/_bundle.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2._markupsafe._bundle
- ~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- This script pulls in markupsafe from a source folder and
- bundles it with Jinja2. It does not pull in the speedups
- module though.
-
- :copyright: Copyright 2010 by the Jinja team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-import sys
-import os
-import re
-
-
-def rewrite_imports(lines):
- for idx, line in enumerate(lines):
- new_line = re.sub(r'(import|from)\s+markupsafe\b',
- r'\1 jinja2._markupsafe', line)
- if new_line != line:
- lines[idx] = new_line
-
-
-def main():
- if len(sys.argv) != 2:
- print 'error: only argument is path to markupsafe'
- sys.exit(1)
- basedir = os.path.dirname(__file__)
- markupdir = sys.argv[1]
- for filename in os.listdir(markupdir):
- if filename.endswith('.py'):
- f = open(os.path.join(markupdir, filename))
- try:
- lines = list(f)
- finally:
- f.close()
- rewrite_imports(lines)
- f = open(os.path.join(basedir, filename), 'w')
- try:
- for line in lines:
- f.write(line)
- finally:
- f.close()
-
-
-if __name__ == '__main__':
- main()
diff --git a/module/lib/jinja2/_markupsafe/_constants.py b/module/lib/jinja2/_markupsafe/_constants.py
deleted file mode 100644
index 919bf03c5..000000000
--- a/module/lib/jinja2/_markupsafe/_constants.py
+++ /dev/null
@@ -1,267 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- markupsafe._constants
- ~~~~~~~~~~~~~~~~~~~~~
-
- Highlevel implementation of the Markup string.
-
- :copyright: (c) 2010 by Armin Ronacher.
- :license: BSD, see LICENSE for more details.
-"""
-
-
-HTML_ENTITIES = {
- 'AElig': 198,
- 'Aacute': 193,
- 'Acirc': 194,
- 'Agrave': 192,
- 'Alpha': 913,
- 'Aring': 197,
- 'Atilde': 195,
- 'Auml': 196,
- 'Beta': 914,
- 'Ccedil': 199,
- 'Chi': 935,
- 'Dagger': 8225,
- 'Delta': 916,
- 'ETH': 208,
- 'Eacute': 201,
- 'Ecirc': 202,
- 'Egrave': 200,
- 'Epsilon': 917,
- 'Eta': 919,
- 'Euml': 203,
- 'Gamma': 915,
- 'Iacute': 205,
- 'Icirc': 206,
- 'Igrave': 204,
- 'Iota': 921,
- 'Iuml': 207,
- 'Kappa': 922,
- 'Lambda': 923,
- 'Mu': 924,
- 'Ntilde': 209,
- 'Nu': 925,
- 'OElig': 338,
- 'Oacute': 211,
- 'Ocirc': 212,
- 'Ograve': 210,
- 'Omega': 937,
- 'Omicron': 927,
- 'Oslash': 216,
- 'Otilde': 213,
- 'Ouml': 214,
- 'Phi': 934,
- 'Pi': 928,
- 'Prime': 8243,
- 'Psi': 936,
- 'Rho': 929,
- 'Scaron': 352,
- 'Sigma': 931,
- 'THORN': 222,
- 'Tau': 932,
- 'Theta': 920,
- 'Uacute': 218,
- 'Ucirc': 219,
- 'Ugrave': 217,
- 'Upsilon': 933,
- 'Uuml': 220,
- 'Xi': 926,
- 'Yacute': 221,
- 'Yuml': 376,
- 'Zeta': 918,
- 'aacute': 225,
- 'acirc': 226,
- 'acute': 180,
- 'aelig': 230,
- 'agrave': 224,
- 'alefsym': 8501,
- 'alpha': 945,
- 'amp': 38,
- 'and': 8743,
- 'ang': 8736,
- 'apos': 39,
- 'aring': 229,
- 'asymp': 8776,
- 'atilde': 227,
- 'auml': 228,
- 'bdquo': 8222,
- 'beta': 946,
- 'brvbar': 166,
- 'bull': 8226,
- 'cap': 8745,
- 'ccedil': 231,
- 'cedil': 184,
- 'cent': 162,
- 'chi': 967,
- 'circ': 710,
- 'clubs': 9827,
- 'cong': 8773,
- 'copy': 169,
- 'crarr': 8629,
- 'cup': 8746,
- 'curren': 164,
- 'dArr': 8659,
- 'dagger': 8224,
- 'darr': 8595,
- 'deg': 176,
- 'delta': 948,
- 'diams': 9830,
- 'divide': 247,
- 'eacute': 233,
- 'ecirc': 234,
- 'egrave': 232,
- 'empty': 8709,
- 'emsp': 8195,
- 'ensp': 8194,
- 'epsilon': 949,
- 'equiv': 8801,
- 'eta': 951,
- 'eth': 240,
- 'euml': 235,
- 'euro': 8364,
- 'exist': 8707,
- 'fnof': 402,
- 'forall': 8704,
- 'frac12': 189,
- 'frac14': 188,
- 'frac34': 190,
- 'frasl': 8260,
- 'gamma': 947,
- 'ge': 8805,
- 'gt': 62,
- 'hArr': 8660,
- 'harr': 8596,
- 'hearts': 9829,
- 'hellip': 8230,
- 'iacute': 237,
- 'icirc': 238,
- 'iexcl': 161,
- 'igrave': 236,
- 'image': 8465,
- 'infin': 8734,
- 'int': 8747,
- 'iota': 953,
- 'iquest': 191,
- 'isin': 8712,
- 'iuml': 239,
- 'kappa': 954,
- 'lArr': 8656,
- 'lambda': 955,
- 'lang': 9001,
- 'laquo': 171,
- 'larr': 8592,
- 'lceil': 8968,
- 'ldquo': 8220,
- 'le': 8804,
- 'lfloor': 8970,
- 'lowast': 8727,
- 'loz': 9674,
- 'lrm': 8206,
- 'lsaquo': 8249,
- 'lsquo': 8216,
- 'lt': 60,
- 'macr': 175,
- 'mdash': 8212,
- 'micro': 181,
- 'middot': 183,
- 'minus': 8722,
- 'mu': 956,
- 'nabla': 8711,
- 'nbsp': 160,
- 'ndash': 8211,
- 'ne': 8800,
- 'ni': 8715,
- 'not': 172,
- 'notin': 8713,
- 'nsub': 8836,
- 'ntilde': 241,
- 'nu': 957,
- 'oacute': 243,
- 'ocirc': 244,
- 'oelig': 339,
- 'ograve': 242,
- 'oline': 8254,
- 'omega': 969,
- 'omicron': 959,
- 'oplus': 8853,
- 'or': 8744,
- 'ordf': 170,
- 'ordm': 186,
- 'oslash': 248,
- 'otilde': 245,
- 'otimes': 8855,
- 'ouml': 246,
- 'para': 182,
- 'part': 8706,
- 'permil': 8240,
- 'perp': 8869,
- 'phi': 966,
- 'pi': 960,
- 'piv': 982,
- 'plusmn': 177,
- 'pound': 163,
- 'prime': 8242,
- 'prod': 8719,
- 'prop': 8733,
- 'psi': 968,
- 'quot': 34,
- 'rArr': 8658,
- 'radic': 8730,
- 'rang': 9002,
- 'raquo': 187,
- 'rarr': 8594,
- 'rceil': 8969,
- 'rdquo': 8221,
- 'real': 8476,
- 'reg': 174,
- 'rfloor': 8971,
- 'rho': 961,
- 'rlm': 8207,
- 'rsaquo': 8250,
- 'rsquo': 8217,
- 'sbquo': 8218,
- 'scaron': 353,
- 'sdot': 8901,
- 'sect': 167,
- 'shy': 173,
- 'sigma': 963,
- 'sigmaf': 962,
- 'sim': 8764,
- 'spades': 9824,
- 'sub': 8834,
- 'sube': 8838,
- 'sum': 8721,
- 'sup': 8835,
- 'sup1': 185,
- 'sup2': 178,
- 'sup3': 179,
- 'supe': 8839,
- 'szlig': 223,
- 'tau': 964,
- 'there4': 8756,
- 'theta': 952,
- 'thetasym': 977,
- 'thinsp': 8201,
- 'thorn': 254,
- 'tilde': 732,
- 'times': 215,
- 'trade': 8482,
- 'uArr': 8657,
- 'uacute': 250,
- 'uarr': 8593,
- 'ucirc': 251,
- 'ugrave': 249,
- 'uml': 168,
- 'upsih': 978,
- 'upsilon': 965,
- 'uuml': 252,
- 'weierp': 8472,
- 'xi': 958,
- 'yacute': 253,
- 'yen': 165,
- 'yuml': 255,
- 'zeta': 950,
- 'zwj': 8205,
- 'zwnj': 8204
-}
diff --git a/module/lib/jinja2/_markupsafe/_native.py b/module/lib/jinja2/_markupsafe/_native.py
deleted file mode 100644
index 7b95828ec..000000000
--- a/module/lib/jinja2/_markupsafe/_native.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- markupsafe._native
- ~~~~~~~~~~~~~~~~~~
-
- Native Python implementation the C module is not compiled.
-
- :copyright: (c) 2010 by Armin Ronacher.
- :license: BSD, see LICENSE for more details.
-"""
-from jinja2._markupsafe import Markup
-
-
-def escape(s):
- """Convert the characters &, <, >, ' and " in string s to HTML-safe
- sequences. Use this if you need to display text that might contain
- such characters in HTML. Marks return value as markup string.
- """
- if hasattr(s, '__html__'):
- return s.__html__()
- return Markup(unicode(s)
- .replace('&', '&amp;')
- .replace('>', '&gt;')
- .replace('<', '&lt;')
- .replace("'", '&#39;')
- .replace('"', '&#34;')
- )
-
-
-def escape_silent(s):
- """Like :func:`escape` but converts `None` into an empty
- markup string.
- """
- if s is None:
- return Markup()
- return escape(s)
-
-
-def soft_unicode(s):
- """Make a string unicode if it isn't already. That way a markup
- string is not converted back to unicode.
- """
- if not isinstance(s, unicode):
- s = unicode(s)
- return s
diff --git a/module/lib/jinja2/_markupsafe/tests.py b/module/lib/jinja2/_markupsafe/tests.py
deleted file mode 100644
index c1ce3943a..000000000
--- a/module/lib/jinja2/_markupsafe/tests.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import gc
-import unittest
-from jinja2._markupsafe import Markup, escape, escape_silent
-
-
-class MarkupTestCase(unittest.TestCase):
-
- def test_markup_operations(self):
- # adding two strings should escape the unsafe one
- unsafe = '<script type="application/x-some-script">alert("foo");</script>'
- safe = Markup('<em>username</em>')
- assert unsafe + safe == unicode(escape(unsafe)) + unicode(safe)
-
- # string interpolations are safe to use too
- assert Markup('<em>%s</em>') % '<bad user>' == \
- '<em>&lt;bad user&gt;</em>'
- assert Markup('<em>%(username)s</em>') % {
- 'username': '<bad user>'
- } == '<em>&lt;bad user&gt;</em>'
-
- # an escaped object is markup too
- assert type(Markup('foo') + 'bar') is Markup
-
- # and it implements __html__ by returning itself
- x = Markup("foo")
- assert x.__html__() is x
-
- # it also knows how to treat __html__ objects
- class Foo(object):
- def __html__(self):
- return '<em>awesome</em>'
- def __unicode__(self):
- return 'awesome'
- assert Markup(Foo()) == '<em>awesome</em>'
- assert Markup('<strong>%s</strong>') % Foo() == \
- '<strong><em>awesome</em></strong>'
-
- # escaping and unescaping
- assert escape('"<>&\'') == '&#34;&lt;&gt;&amp;&#39;'
- assert Markup("<em>Foo &amp; Bar</em>").striptags() == "Foo & Bar"
- assert Markup("&lt;test&gt;").unescape() == "<test>"
-
- def test_all_set(self):
- import jinja2._markupsafe as markup
- for item in markup.__all__:
- getattr(markup, item)
-
- def test_escape_silent(self):
- assert escape_silent(None) == Markup()
- assert escape(None) == Markup(None)
- assert escape_silent('<foo>') == Markup(u'&lt;foo&gt;')
-
-
-class MarkupLeakTestCase(unittest.TestCase):
-
- def test_markup_leaks(self):
- counts = set()
- for count in xrange(20):
- for item in xrange(1000):
- escape("foo")
- escape("<foo>")
- escape(u"foo")
- escape(u"<foo>")
- counts.add(len(gc.get_objects()))
- assert len(counts) == 1, 'ouch, c extension seems to leak objects'
-
-
-def suite():
- suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(MarkupTestCase))
-
- # this test only tests the c extension
- if not hasattr(escape, 'func_code'):
- suite.addTest(unittest.makeSuite(MarkupLeakTestCase))
-
- return suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')
diff --git a/module/lib/jinja2/_stringdefs.py b/module/lib/jinja2/_stringdefs.py
deleted file mode 100644
index 1161b7f4a..000000000
--- a/module/lib/jinja2/_stringdefs.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2._stringdefs
- ~~~~~~~~~~~~~~~~~~
-
- Strings of all Unicode characters of a certain category.
- Used for matching in Unicode-aware languages. Run to regenerate.
-
- Inspired by chartypes_create.py from the MoinMoin project, original
- implementation from Pygments.
-
- :copyright: Copyright 2006-2009 by the Jinja team, see AUTHORS.
- :license: BSD, see LICENSE for details.
-"""
-
-Cc = u'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f'
-
-Cf = u'\xad\u0600\u0601\u0602\u0603\u06dd\u070f\u17b4\u17b5\u200b\u200c\u200d\u200e\u200f\u202a\u202b\u202c\u202d\u202e\u2060\u2061\u2062\u2063\u206a\u206b\u206c\u206d\u206e\u206f\ufeff\ufff9\ufffa\ufffb'
-
-Cn = u'\u0242\u0243\u0244\u0245\u0246\u0247\u0248\u0249\u024a\u024b\u024c\u024d\u024e\u024f\u0370\u0371\u0372\u0373\u0376\u0377\u0378\u0379\u037b\u037c\u037d\u037f\u0380\u0381\u0382\u0383\u038b\u038d\u03a2\u03cf\u0487\u04cf\u04fa\u04fb\u04fc\u04fd\u04fe\u04ff\u0510\u0511\u0512\u0513\u0514\u0515\u0516\u0517\u0518\u0519\u051a\u051b\u051c\u051d\u051e\u051f\u0520\u0521\u0522\u0523\u0524\u0525\u0526\u0527\u0528\u0529\u052a\u052b\u052c\u052d\u052e\u052f\u0530\u0557\u0558\u0560\u0588\u058b\u058c\u058d\u058e\u058f\u0590\u05ba\u05c8\u05c9\u05ca\u05cb\u05cc\u05cd\u05ce\u05cf\u05eb\u05ec\u05ed\u05ee\u05ef\u05f5\u05f6\u05f7\u05f8\u05f9\u05fa\u05fb\u05fc\u05fd\u05fe\u05ff\u0604\u0605\u0606\u0607\u0608\u0609\u060a\u0616\u0617\u0618\u0619\u061a\u061c\u061d\u0620\u063b\u063c\u063d\u063e\u063f\u065f\u070e\u074b\u074c\u076e\u076f\u0770\u0771\u0772\u0773\u0774\u0775\u0776\u0777\u0778\u0779\u077a\u077b\u077c\u077d\u077e\u077f\u07b2\u07b3\u07b4\u07b5\u07b6\u07b7\u07b8\u07b9\u07ba\u07bb\u07bc\u07bd\u07be\u07bf\u07c0\u07c1\u07c2\u07c3\u07c4\u07c5\u07c6\u07c7\u07c8\u07c9\u07ca\u07cb\u07cc\u07cd\u07ce\u07cf\u07d0\u07d1\u07d2\u07d3\u07d4\u07d5\u07d6\u07d7\u07d8\u07d9\u07da\u07db\u07dc\u07dd\u07de\u07df\u07e0\u07e1\u07e2\u07e3\u07e4\u07e5\u07e6\u07e7\u07e8\u07e9\u07ea\u07eb\u07ec\u07ed\u07ee\u07ef\u07f0\u07f1\u07f2\u07f3\u07f4\u07f5\u07f6\u07f7\u07f8\u07f9\u07fa\u07fb\u07fc\u07fd\u07fe\u07ff\u0800\u0801\u0802\u0803\u0804\u0805\u0806\u0807\u0808\u0809\u080a\u080b\u080c\u080d\u080e\u080f\u0810\u0811\u0812\u0813\u0814\u0815\u0816\u0817\u0818\u0819\u081a\u081b\u081c\u081d\u081e\u081f\u0820\u0821\u0822\u0823\u0824\u0825\u0826\u0827\u0828\u0829\u082a\u082b\u082c\u082d\u082e\u082f\u0830\u0831\u0832\u0833\u0834\u0835\u0836\u0837\u0838\u0839\u083a\u083b\u083c\u083d\u083e\u083f\u0840\u0841\u0842\u0843\u0844\u0845\u0846\u0847\u0848\u0849\u084a\u084b\u084c\u084d\u084e\u084f\u0850\u0851\u0852\u0853\u0854\u0855\u0856\u0857\u0858\u0859\u085a\u085b\u085c\u085d\u085e\u085f\u0860\u0861\u0862\u0863\u0864\u0865\u0866\u0867\u0868\u0869\u086a\u086b\u086c\u086d\u086e\u086f\u0870\u0871\u0872\u0873\u0874\u0875\u0876\u0877\u0878\u0879\u087a\u087b\u087c\u087d\u087e\u087f\u0880\u0881\u0882\u0883\u0884\u0885\u0886\u0887\u0888\u0889\u088a\u088b\u088c\u088d\u088e\u088f\u0890\u0891\u0892\u0893\u0894\u0895\u0896\u0897\u0898\u0899\u089a\u089b\u089c\u089d\u089e\u089f\u08a0\u08a1\u08a2\u08a3\u08a4\u08a5\u08a6\u08a7\u08a8\u08a9\u08aa\u08ab\u08ac\u08ad\u08ae\u08af\u08b0\u08b1\u08b2\u08b3\u08b4\u08b5\u08b6\u08b7\u08b8\u08b9\u08ba\u08bb\u08bc\u08bd\u08be\u08bf\u08c0\u08c1\u08c2\u08c3\u08c4\u08c5\u08c6\u08c7\u08c8\u08c9\u08ca\u08cb\u08cc\u08cd\u08ce\u08cf\u08d0\u08d1\u08d2\u08d3\u08d4\u08d5\u08d6\u08d7\u08d8\u08d9\u08da\u08db\u08dc\u08dd\u08de\u08df\u08e0\u08e1\u08e2\u08e3\u08e4\u08e5\u08e6\u08e7\u08e8\u08e9\u08ea\u08eb\u08ec\u08ed\u08ee\u08ef\u08f0\u08f1\u08f2\u08f3\u08f4\u08f5\u08f6\u08f7\u08f8\u08f9\u08fa\u08fb\u08fc\u08fd\u08fe\u08ff\u0900\u093a\u093b\u094e\u094f\u0955\u0956\u0957\u0971\u0972\u0973\u0974\u0975\u0976\u0977\u0978\u0979\u097a\u097b\u097c\u097e\u097f\u0980\u0984\u098d\u098e\u0991\u0992\u09a9\u09b1\u09b3\u09b4\u09b5\u09ba\u09bb\u09c5\u09c6\u09c9\u09ca\u09cf\u09d0\u09d1\u09d2\u09d3\u09d4\u09d5\u09d6\u09d8\u09d9\u09da\u09db\u09de\u09e4\u09e5\u09fb\u09fc\u09fd\u09fe\u09ff\u0a00\u0a04\u0a0b\u0a0c\u0a0d\u0a0e\u0a11\u0a12\u0a29\u0a31\u0a34\u0a37\u0a3a\u0a3b\u0a3d\u0a43\u0a44\u0a45\u0a46\u0a49\u0a4a\u0a4e\u0a4f\u0a50\u0a51\u0a52\u0a53\u0a54\u0a55\u0a56\u0a57\u0a58\u0a5d\u0a5f\u0a60\u0a61\u0a62\u0a63\u0a64\u0a65\u0a75\u0a76\u0a77\u0a78\u0a79\u0a7a\u0a7b\u0a7c\u0a7d\u0a7e\u0a7f\u0a80\u0a84\u0a8e\u0a92\u0aa9\u0ab1\u0ab4\u0aba\u0abb\u0ac6\u0aca\u0ace\u0acf\u0ad1\u0ad2\u0ad3\u0ad4\u0ad5\u0ad6\u0ad7\u0ad8\u0ad9\u0ada\u0adb\u0adc\u0add\u0ade\u0adf\u0ae4\u0ae5\u0af0\u0af2\u0af3\u0af4\u0af5\u0af6\u0af7\u0af8\u0af9\u0afa\u0afb\u0afc\u0afd\u0afe\u0aff\u0b00\u0b04\u0b0d\u0b0e\u0b11\u0b12\u0b29\u0b31\u0b34\u0b3a\u0b3b\u0b44\u0b45\u0b46\u0b49\u0b4a\u0b4e\u0b4f\u0b50\u0b51\u0b52\u0b53\u0b54\u0b55\u0b58\u0b59\u0b5a\u0b5b\u0b5e\u0b62\u0b63\u0b64\u0b65\u0b72\u0b73\u0b74\u0b75\u0b76\u0b77\u0b78\u0b79\u0b7a\u0b7b\u0b7c\u0b7d\u0b7e\u0b7f\u0b80\u0b81\u0b84\u0b8b\u0b8c\u0b8d\u0b91\u0b96\u0b97\u0b98\u0b9b\u0b9d\u0ba0\u0ba1\u0ba2\u0ba5\u0ba6\u0ba7\u0bab\u0bac\u0bad\u0bba\u0bbb\u0bbc\u0bbd\u0bc3\u0bc4\u0bc5\u0bc9\u0bce\u0bcf\u0bd0\u0bd1\u0bd2\u0bd3\u0bd4\u0bd5\u0bd6\u0bd8\u0bd9\u0bda\u0bdb\u0bdc\u0bdd\u0bde\u0bdf\u0be0\u0be1\u0be2\u0be3\u0be4\u0be5\u0bfb\u0bfc\u0bfd\u0bfe\u0bff\u0c00\u0c04\u0c0d\u0c11\u0c29\u0c34\u0c3a\u0c3b\u0c3c\u0c3d\u0c45\u0c49\u0c4e\u0c4f\u0c50\u0c51\u0c52\u0c53\u0c54\u0c57\u0c58\u0c59\u0c5a\u0c5b\u0c5c\u0c5d\u0c5e\u0c5f\u0c62\u0c63\u0c64\u0c65\u0c70\u0c71\u0c72\u0c73\u0c74\u0c75\u0c76\u0c77\u0c78\u0c79\u0c7a\u0c7b\u0c7c\u0c7d\u0c7e\u0c7f\u0c80\u0c81\u0c84\u0c8d\u0c91\u0ca9\u0cb4\u0cba\u0cbb\u0cc5\u0cc9\u0cce\u0ccf\u0cd0\u0cd1\u0cd2\u0cd3\u0cd4\u0cd7\u0cd8\u0cd9\u0cda\u0cdb\u0cdc\u0cdd\u0cdf\u0ce2\u0ce3\u0ce4\u0ce5\u0cf0\u0cf1\u0cf2\u0cf3\u0cf4\u0cf5\u0cf6\u0cf7\u0cf8\u0cf9\u0cfa\u0cfb\u0cfc\u0cfd\u0cfe\u0cff\u0d00\u0d01\u0d04\u0d0d\u0d11\u0d29\u0d3a\u0d3b\u0d3c\u0d3d\u0d44\u0d45\u0d49\u0d4e\u0d4f\u0d50\u0d51\u0d52\u0d53\u0d54\u0d55\u0d56\u0d58\u0d59\u0d5a\u0d5b\u0d5c\u0d5d\u0d5e\u0d5f\u0d62\u0d63\u0d64\u0d65\u0d70\u0d71\u0d72\u0d73\u0d74\u0d75\u0d76\u0d77\u0d78\u0d79\u0d7a\u0d7b\u0d7c\u0d7d\u0d7e\u0d7f\u0d80\u0d81\u0d84\u0d97\u0d98\u0d99\u0db2\u0dbc\u0dbe\u0dbf\u0dc7\u0dc8\u0dc9\u0dcb\u0dcc\u0dcd\u0dce\u0dd5\u0dd7\u0de0\u0de1\u0de2\u0de3\u0de4\u0de5\u0de6\u0de7\u0de8\u0de9\u0dea\u0deb\u0dec\u0ded\u0dee\u0def\u0df0\u0df1\u0df5\u0df6\u0df7\u0df8\u0df9\u0dfa\u0dfb\u0dfc\u0dfd\u0dfe\u0dff\u0e00\u0e3b\u0e3c\u0e3d\u0e3e\u0e5c\u0e5d\u0e5e\u0e5f\u0e60\u0e61\u0e62\u0e63\u0e64\u0e65\u0e66\u0e67\u0e68\u0e69\u0e6a\u0e6b\u0e6c\u0e6d\u0e6e\u0e6f\u0e70\u0e71\u0e72\u0e73\u0e74\u0e75\u0e76\u0e77\u0e78\u0e79\u0e7a\u0e7b\u0e7c\u0e7d\u0e7e\u0e7f\u0e80\u0e83\u0e85\u0e86\u0e89\u0e8b\u0e8c\u0e8e\u0e8f\u0e90\u0e91\u0e92\u0e93\u0e98\u0ea0\u0ea4\u0ea6\u0ea8\u0ea9\u0eac\u0eba\u0ebe\u0ebf\u0ec5\u0ec7\u0ece\u0ecf\u0eda\u0edb\u0ede\u0edf\u0ee0\u0ee1\u0ee2\u0ee3\u0ee4\u0ee5\u0ee6\u0ee7\u0ee8\u0ee9\u0eea\u0eeb\u0eec\u0eed\u0eee\u0eef\u0ef0\u0ef1\u0ef2\u0ef3\u0ef4\u0ef5\u0ef6\u0ef7\u0ef8\u0ef9\u0efa\u0efb\u0efc\u0efd\u0efe\u0eff\u0f48\u0f6b\u0f6c\u0f6d\u0f6e\u0f6f\u0f70\u0f8c\u0f8d\u0f8e\u0f8f\u0f98\u0fbd\u0fcd\u0fce\u0fd2\u0fd3\u0fd4\u0fd5\u0fd6\u0fd7\u0fd8\u0fd9\u0fda\u0fdb\u0fdc\u0fdd\u0fde\u0fdf\u0fe0\u0fe1\u0fe2\u0fe3\u0fe4\u0fe5\u0fe6\u0fe7\u0fe8\u0fe9\u0fea\u0feb\u0fec\u0fed\u0fee\u0fef\u0ff0\u0ff1\u0ff2\u0ff3\u0ff4\u0ff5\u0ff6\u0ff7\u0ff8\u0ff9\u0ffa\u0ffb\u0ffc\u0ffd\u0ffe\u0fff\u1022\u1028\u102b\u1033\u1034\u1035\u103a\u103b\u103c\u103d\u103e\u103f\u105a\u105b\u105c\u105d\u105e\u105f\u1060\u1061\u1062\u1063\u1064\u1065\u1066\u1067\u1068\u1069\u106a\u106b\u106c\u106d\u106e\u106f\u1070\u1071\u1072\u1073\u1074\u1075\u1076\u1077\u1078\u1079\u107a\u107b\u107c\u107d\u107e\u107f\u1080\u1081\u1082\u1083\u1084\u1085\u1086\u1087\u1088\u1089\u108a\u108b\u108c\u108d\u108e\u108f\u1090\u1091\u1092\u1093\u1094\u1095\u1096\u1097\u1098\u1099\u109a\u109b\u109c\u109d\u109e\u109f\u10c6\u10c7\u10c8\u10c9\u10ca\u10cb\u10cc\u10cd\u10ce\u10cf\u10fd\u10fe\u10ff\u115a\u115b\u115c\u115d\u115e\u11a3\u11a4\u11a5\u11a6\u11a7\u11fa\u11fb\u11fc\u11fd\u11fe\u11ff\u1249\u124e\u124f\u1257\u1259\u125e\u125f\u1289\u128e\u128f\u12b1\u12b6\u12b7\u12bf\u12c1\u12c6\u12c7\u12d7\u1311\u1316\u1317\u135b\u135c\u135d\u135e\u137d\u137e\u137f\u139a\u139b\u139c\u139d\u139e\u139f\u13f5\u13f6\u13f7\u13f8\u13f9\u13fa\u13fb\u13fc\u13fd\u13fe\u13ff\u1400\u1677\u1678\u1679\u167a\u167b\u167c\u167d\u167e\u167f\u169d\u169e\u169f\u16f1\u16f2\u16f3\u16f4\u16f5\u16f6\u16f7\u16f8\u16f9\u16fa\u16fb\u16fc\u16fd\u16fe\u16ff\u170d\u1715\u1716\u1717\u1718\u1719\u171a\u171b\u171c\u171d\u171e\u171f\u1737\u1738\u1739\u173a\u173b\u173c\u173d\u173e\u173f\u1754\u1755\u1756\u1757\u1758\u1759\u175a\u175b\u175c\u175d\u175e\u175f\u176d\u1771\u1774\u1775\u1776\u1777\u1778\u1779\u177a\u177b\u177c\u177d\u177e\u177f\u17de\u17df\u17ea\u17eb\u17ec\u17ed\u17ee\u17ef\u17fa\u17fb\u17fc\u17fd\u17fe\u17ff\u180f\u181a\u181b\u181c\u181d\u181e\u181f\u1878\u1879\u187a\u187b\u187c\u187d\u187e\u187f\u18aa\u18ab\u18ac\u18ad\u18ae\u18af\u18b0\u18b1\u18b2\u18b3\u18b4\u18b5\u18b6\u18b7\u18b8\u18b9\u18ba\u18bb\u18bc\u18bd\u18be\u18bf\u18c0\u18c1\u18c2\u18c3\u18c4\u18c5\u18c6\u18c7\u18c8\u18c9\u18ca\u18cb\u18cc\u18cd\u18ce\u18cf\u18d0\u18d1\u18d2\u18d3\u18d4\u18d5\u18d6\u18d7\u18d8\u18d9\u18da\u18db\u18dc\u18dd\u18de\u18df\u18e0\u18e1\u18e2\u18e3\u18e4\u18e5\u18e6\u18e7\u18e8\u18e9\u18ea\u18eb\u18ec\u18ed\u18ee\u18ef\u18f0\u18f1\u18f2\u18f3\u18f4\u18f5\u18f6\u18f7\u18f8\u18f9\u18fa\u18fb\u18fc\u18fd\u18fe\u18ff\u191d\u191e\u191f\u192c\u192d\u192e\u192f\u193c\u193d\u193e\u193f\u1941\u1942\u1943\u196e\u196f\u1975\u1976\u1977\u1978\u1979\u197a\u197b\u197c\u197d\u197e\u197f\u19aa\u19ab\u19ac\u19ad\u19ae\u19af\u19ca\u19cb\u19cc\u19cd\u19ce\u19cf\u19da\u19db\u19dc\u19dd\u1a1c\u1a1d\u1a20\u1a21\u1a22\u1a23\u1a24\u1a25\u1a26\u1a27\u1a28\u1a29\u1a2a\u1a2b\u1a2c\u1a2d\u1a2e\u1a2f\u1a30\u1a31\u1a32\u1a33\u1a34\u1a35\u1a36\u1a37\u1a38\u1a39\u1a3a\u1a3b\u1a3c\u1a3d\u1a3e\u1a3f\u1a40\u1a41\u1a42\u1a43\u1a44\u1a45\u1a46\u1a47\u1a48\u1a49\u1a4a\u1a4b\u1a4c\u1a4d\u1a4e\u1a4f\u1a50\u1a51\u1a52\u1a53\u1a54\u1a55\u1a56\u1a57\u1a58\u1a59\u1a5a\u1a5b\u1a5c\u1a5d\u1a5e\u1a5f\u1a60\u1a61\u1a62\u1a63\u1a64\u1a65\u1a66\u1a67\u1a68\u1a69\u1a6a\u1a6b\u1a6c\u1a6d\u1a6e\u1a6f\u1a70\u1a71\u1a72\u1a73\u1a74\u1a75\u1a76\u1a77\u1a78\u1a79\u1a7a\u1a7b\u1a7c\u1a7d\u1a7e\u1a7f\u1a80\u1a81\u1a82\u1a83\u1a84\u1a85\u1a86\u1a87\u1a88\u1a89\u1a8a\u1a8b\u1a8c\u1a8d\u1a8e\u1a8f\u1a90\u1a91\u1a92\u1a93\u1a94\u1a95\u1a96\u1a97\u1a98\u1a99\u1a9a\u1a9b\u1a9c\u1a9d\u1a9e\u1a9f\u1aa0\u1aa1\u1aa2\u1aa3\u1aa4\u1aa5\u1aa6\u1aa7\u1aa8\u1aa9\u1aaa\u1aab\u1aac\u1aad\u1aae\u1aaf\u1ab0\u1ab1\u1ab2\u1ab3\u1ab4\u1ab5\u1ab6\u1ab7\u1ab8\u1ab9\u1aba\u1abb\u1abc\u1abd\u1abe\u1abf\u1ac0\u1ac1\u1ac2\u1ac3\u1ac4\u1ac5\u1ac6\u1ac7\u1ac8\u1ac9\u1aca\u1acb\u1acc\u1acd\u1ace\u1acf\u1ad0\u1ad1\u1ad2\u1ad3\u1ad4\u1ad5\u1ad6\u1ad7\u1ad8\u1ad9\u1ada\u1adb\u1adc\u1add\u1ade\u1adf\u1ae0\u1ae1\u1ae2\u1ae3\u1ae4\u1ae5\u1ae6\u1ae7\u1ae8\u1ae9\u1aea\u1aeb\u1aec\u1aed\u1aee\u1aef\u1af0\u1af1\u1af2\u1af3\u1af4\u1af5\u1af6\u1af7\u1af8\u1af9\u1afa\u1afb\u1afc\u1afd\u1afe\u1aff\u1b00\u1b01\u1b02\u1b03\u1b04\u1b05\u1b06\u1b07\u1b08\u1b09\u1b0a\u1b0b\u1b0c\u1b0d\u1b0e\u1b0f\u1b10\u1b11\u1b12\u1b13\u1b14\u1b15\u1b16\u1b17\u1b18\u1b19\u1b1a\u1b1b\u1b1c\u1b1d\u1b1e\u1b1f\u1b20\u1b21\u1b22\u1b23\u1b24\u1b25\u1b26\u1b27\u1b28\u1b29\u1b2a\u1b2b\u1b2c\u1b2d\u1b2e\u1b2f\u1b30\u1b31\u1b32\u1b33\u1b34\u1b35\u1b36\u1b37\u1b38\u1b39\u1b3a\u1b3b\u1b3c\u1b3d\u1b3e\u1b3f\u1b40\u1b41\u1b42\u1b43\u1b44\u1b45\u1b46\u1b47\u1b48\u1b49\u1b4a\u1b4b\u1b4c\u1b4d\u1b4e\u1b4f\u1b50\u1b51\u1b52\u1b53\u1b54\u1b55\u1b56\u1b57\u1b58\u1b59\u1b5a\u1b5b\u1b5c\u1b5d\u1b5e\u1b5f\u1b60\u1b61\u1b62\u1b63\u1b64\u1b65\u1b66\u1b67\u1b68\u1b69\u1b6a\u1b6b\u1b6c\u1b6d\u1b6e\u1b6f\u1b70\u1b71\u1b72\u1b73\u1b74\u1b75\u1b76\u1b77\u1b78\u1b79\u1b7a\u1b7b\u1b7c\u1b7d\u1b7e\u1b7f\u1b80\u1b81\u1b82\u1b83\u1b84\u1b85\u1b86\u1b87\u1b88\u1b89\u1b8a\u1b8b\u1b8c\u1b8d\u1b8e\u1b8f\u1b90\u1b91\u1b92\u1b93\u1b94\u1b95\u1b96\u1b97\u1b98\u1b99\u1b9a\u1b9b\u1b9c\u1b9d\u1b9e\u1b9f\u1ba0\u1ba1\u1ba2\u1ba3\u1ba4\u1ba5\u1ba6\u1ba7\u1ba8\u1ba9\u1baa\u1bab\u1bac\u1bad\u1bae\u1baf\u1bb0\u1bb1\u1bb2\u1bb3\u1bb4\u1bb5\u1bb6\u1bb7\u1bb8\u1bb9\u1bba\u1bbb\u1bbc\u1bbd\u1bbe\u1bbf\u1bc0\u1bc1\u1bc2\u1bc3\u1bc4\u1bc5\u1bc6\u1bc7\u1bc8\u1bc9\u1bca\u1bcb\u1bcc\u1bcd\u1bce\u1bcf\u1bd0\u1bd1\u1bd2\u1bd3\u1bd4\u1bd5\u1bd6\u1bd7\u1bd8\u1bd9\u1bda\u1bdb\u1bdc\u1bdd\u1bde\u1bdf\u1be0\u1be1\u1be2\u1be3\u1be4\u1be5\u1be6\u1be7\u1be8\u1be9\u1bea\u1beb\u1bec\u1bed\u1bee\u1bef\u1bf0\u1bf1\u1bf2\u1bf3\u1bf4\u1bf5\u1bf6\u1bf7\u1bf8\u1bf9\u1bfa\u1bfb\u1bfc\u1bfd\u1bfe\u1bff\u1c00\u1c01\u1c02\u1c03\u1c04\u1c05\u1c06\u1c07\u1c08\u1c09\u1c0a\u1c0b\u1c0c\u1c0d\u1c0e\u1c0f\u1c10\u1c11\u1c12\u1c13\u1c14\u1c15\u1c16\u1c17\u1c18\u1c19\u1c1a\u1c1b\u1c1c\u1c1d\u1c1e\u1c1f\u1c20\u1c21\u1c22\u1c23\u1c24\u1c25\u1c26\u1c27\u1c28\u1c29\u1c2a\u1c2b\u1c2c\u1c2d\u1c2e\u1c2f\u1c30\u1c31\u1c32\u1c33\u1c34\u1c35\u1c36\u1c37\u1c38\u1c39\u1c3a\u1c3b\u1c3c\u1c3d\u1c3e\u1c3f\u1c40\u1c41\u1c42\u1c43\u1c44\u1c45\u1c46\u1c47\u1c48\u1c49\u1c4a\u1c4b\u1c4c\u1c4d\u1c4e\u1c4f\u1c50\u1c51\u1c52\u1c53\u1c54\u1c55\u1c56\u1c57\u1c58\u1c59\u1c5a\u1c5b\u1c5c\u1c5d\u1c5e\u1c5f\u1c60\u1c61\u1c62\u1c63\u1c64\u1c65\u1c66\u1c67\u1c68\u1c69\u1c6a\u1c6b\u1c6c\u1c6d\u1c6e\u1c6f\u1c70\u1c71\u1c72\u1c73\u1c74\u1c75\u1c76\u1c77\u1c78\u1c79\u1c7a\u1c7b\u1c7c\u1c7d\u1c7e\u1c7f\u1c80\u1c81\u1c82\u1c83\u1c84\u1c85\u1c86\u1c87\u1c88\u1c89\u1c8a\u1c8b\u1c8c\u1c8d\u1c8e\u1c8f\u1c90\u1c91\u1c92\u1c93\u1c94\u1c95\u1c96\u1c97\u1c98\u1c99\u1c9a\u1c9b\u1c9c\u1c9d\u1c9e\u1c9f\u1ca0\u1ca1\u1ca2\u1ca3\u1ca4\u1ca5\u1ca6\u1ca7\u1ca8\u1ca9\u1caa\u1cab\u1cac\u1cad\u1cae\u1caf\u1cb0\u1cb1\u1cb2\u1cb3\u1cb4\u1cb5\u1cb6\u1cb7\u1cb8\u1cb9\u1cba\u1cbb\u1cbc\u1cbd\u1cbe\u1cbf\u1cc0\u1cc1\u1cc2\u1cc3\u1cc4\u1cc5\u1cc6\u1cc7\u1cc8\u1cc9\u1cca\u1ccb\u1ccc\u1ccd\u1cce\u1ccf\u1cd0\u1cd1\u1cd2\u1cd3\u1cd4\u1cd5\u1cd6\u1cd7\u1cd8\u1cd9\u1cda\u1cdb\u1cdc\u1cdd\u1cde\u1cdf\u1ce0\u1ce1\u1ce2\u1ce3\u1ce4\u1ce5\u1ce6\u1ce7\u1ce8\u1ce9\u1cea\u1ceb\u1cec\u1ced\u1cee\u1cef\u1cf0\u1cf1\u1cf2\u1cf3\u1cf4\u1cf5\u1cf6\u1cf7\u1cf8\u1cf9\u1cfa\u1cfb\u1cfc\u1cfd\u1cfe\u1cff\u1dc4\u1dc5\u1dc6\u1dc7\u1dc8\u1dc9\u1dca\u1dcb\u1dcc\u1dcd\u1dce\u1dcf\u1dd0\u1dd1\u1dd2\u1dd3\u1dd4\u1dd5\u1dd6\u1dd7\u1dd8\u1dd9\u1dda\u1ddb\u1ddc\u1ddd\u1dde\u1ddf\u1de0\u1de1\u1de2\u1de3\u1de4\u1de5\u1de6\u1de7\u1de8\u1de9\u1dea\u1deb\u1dec\u1ded\u1dee\u1def\u1df0\u1df1\u1df2\u1df3\u1df4\u1df5\u1df6\u1df7\u1df8\u1df9\u1dfa\u1dfb\u1dfc\u1dfd\u1dfe\u1dff\u1e9c\u1e9d\u1e9e\u1e9f\u1efa\u1efb\u1efc\u1efd\u1efe\u1eff\u1f16\u1f17\u1f1e\u1f1f\u1f46\u1f47\u1f4e\u1f4f\u1f58\u1f5a\u1f5c\u1f5e\u1f7e\u1f7f\u1fb5\u1fc5\u1fd4\u1fd5\u1fdc\u1ff0\u1ff1\u1ff5\u1fff\u2064\u2065\u2066\u2067\u2068\u2069\u2072\u2073\u208f\u2095\u2096\u2097\u2098\u2099\u209a\u209b\u209c\u209d\u209e\u209f\u20b6\u20b7\u20b8\u20b9\u20ba\u20bb\u20bc\u20bd\u20be\u20bf\u20c0\u20c1\u20c2\u20c3\u20c4\u20c5\u20c6\u20c7\u20c8\u20c9\u20ca\u20cb\u20cc\u20cd\u20ce\u20cf\u20ec\u20ed\u20ee\u20ef\u20f0\u20f1\u20f2\u20f3\u20f4\u20f5\u20f6\u20f7\u20f8\u20f9\u20fa\u20fb\u20fc\u20fd\u20fe\u20ff\u214d\u214e\u214f\u2150\u2151\u2152\u2184\u2185\u2186\u2187\u2188\u2189\u218a\u218b\u218c\u218d\u218e\u218f\u23dc\u23dd\u23de\u23df\u23e0\u23e1\u23e2\u23e3\u23e4\u23e5\u23e6\u23e7\u23e8\u23e9\u23ea\u23eb\u23ec\u23ed\u23ee\u23ef\u23f0\u23f1\u23f2\u23f3\u23f4\u23f5\u23f6\u23f7\u23f8\u23f9\u23fa\u23fb\u23fc\u23fd\u23fe\u23ff\u2427\u2428\u2429\u242a\u242b\u242c\u242d\u242e\u242f\u2430\u2431\u2432\u2433\u2434\u2435\u2436\u2437\u2438\u2439\u243a\u243b\u243c\u243d\u243e\u243f\u244b\u244c\u244d\u244e\u244f\u2450\u2451\u2452\u2453\u2454\u2455\u2456\u2457\u2458\u2459\u245a\u245b\u245c\u245d\u245e\u245f\u269d\u269e\u269f\u26b2\u26b3\u26b4\u26b5\u26b6\u26b7\u26b8\u26b9\u26ba\u26bb\u26bc\u26bd\u26be\u26bf\u26c0\u26c1\u26c2\u26c3\u26c4\u26c5\u26c6\u26c7\u26c8\u26c9\u26ca\u26cb\u26cc\u26cd\u26ce\u26cf\u26d0\u26d1\u26d2\u26d3\u26d4\u26d5\u26d6\u26d7\u26d8\u26d9\u26da\u26db\u26dc\u26dd\u26de\u26df\u26e0\u26e1\u26e2\u26e3\u26e4\u26e5\u26e6\u26e7\u26e8\u26e9\u26ea\u26eb\u26ec\u26ed\u26ee\u26ef\u26f0\u26f1\u26f2\u26f3\u26f4\u26f5\u26f6\u26f7\u26f8\u26f9\u26fa\u26fb\u26fc\u26fd\u26fe\u26ff\u2700\u2705\u270a\u270b\u2728\u274c\u274e\u2753\u2754\u2755\u2757\u275f\u2760\u2795\u2796\u2797\u27b0\u27bf\u27c7\u27c8\u27c9\u27ca\u27cb\u27cc\u27cd\u27ce\u27cf\u27ec\u27ed\u27ee\u27ef\u2b14\u2b15\u2b16\u2b17\u2b18\u2b19\u2b1a\u2b1b\u2b1c\u2b1d\u2b1e\u2b1f\u2b20\u2b21\u2b22\u2b23\u2b24\u2b25\u2b26\u2b27\u2b28\u2b29\u2b2a\u2b2b\u2b2c\u2b2d\u2b2e\u2b2f\u2b30\u2b31\u2b32\u2b33\u2b34\u2b35\u2b36\u2b37\u2b38\u2b39\u2b3a\u2b3b\u2b3c\u2b3d\u2b3e\u2b3f\u2b40\u2b41\u2b42\u2b43\u2b44\u2b45\u2b46\u2b47\u2b48\u2b49\u2b4a\u2b4b\u2b4c\u2b4d\u2b4e\u2b4f\u2b50\u2b51\u2b52\u2b53\u2b54\u2b55\u2b56\u2b57\u2b58\u2b59\u2b5a\u2b5b\u2b5c\u2b5d\u2b5e\u2b5f\u2b60\u2b61\u2b62\u2b63\u2b64\u2b65\u2b66\u2b67\u2b68\u2b69\u2b6a\u2b6b\u2b6c\u2b6d\u2b6e\u2b6f\u2b70\u2b71\u2b72\u2b73\u2b74\u2b75\u2b76\u2b77\u2b78\u2b79\u2b7a\u2b7b\u2b7c\u2b7d\u2b7e\u2b7f\u2b80\u2b81\u2b82\u2b83\u2b84\u2b85\u2b86\u2b87\u2b88\u2b89\u2b8a\u2b8b\u2b8c\u2b8d\u2b8e\u2b8f\u2b90\u2b91\u2b92\u2b93\u2b94\u2b95\u2b96\u2b97\u2b98\u2b99\u2b9a\u2b9b\u2b9c\u2b9d\u2b9e\u2b9f\u2ba0\u2ba1\u2ba2\u2ba3\u2ba4\u2ba5\u2ba6\u2ba7\u2ba8\u2ba9\u2baa\u2bab\u2bac\u2bad\u2bae\u2baf\u2bb0\u2bb1\u2bb2\u2bb3\u2bb4\u2bb5\u2bb6\u2bb7\u2bb8\u2bb9\u2bba\u2bbb\u2bbc\u2bbd\u2bbe\u2bbf\u2bc0\u2bc1\u2bc2\u2bc3\u2bc4\u2bc5\u2bc6\u2bc7\u2bc8\u2bc9\u2bca\u2bcb\u2bcc\u2bcd\u2bce\u2bcf\u2bd0\u2bd1\u2bd2\u2bd3\u2bd4\u2bd5\u2bd6\u2bd7\u2bd8\u2bd9\u2bda\u2bdb\u2bdc\u2bdd\u2bde\u2bdf\u2be0\u2be1\u2be2\u2be3\u2be4\u2be5\u2be6\u2be7\u2be8\u2be9\u2bea\u2beb\u2bec\u2bed\u2bee\u2bef\u2bf0\u2bf1\u2bf2\u2bf3\u2bf4\u2bf5\u2bf6\u2bf7\u2bf8\u2bf9\u2bfa\u2bfb\u2bfc\u2bfd\u2bfe\u2bff\u2c2f\u2c5f\u2c60\u2c61\u2c62\u2c63\u2c64\u2c65\u2c66\u2c67\u2c68\u2c69\u2c6a\u2c6b\u2c6c\u2c6d\u2c6e\u2c6f\u2c70\u2c71\u2c72\u2c73\u2c74\u2c75\u2c76\u2c77\u2c78\u2c79\u2c7a\u2c7b\u2c7c\u2c7d\u2c7e\u2c7f\u2ceb\u2cec\u2ced\u2cee\u2cef\u2cf0\u2cf1\u2cf2\u2cf3\u2cf4\u2cf5\u2cf6\u2cf7\u2cf8\u2d26\u2d27\u2d28\u2d29\u2d2a\u2d2b\u2d2c\u2d2d\u2d2e\u2d2f\u2d66\u2d67\u2d68\u2d69\u2d6a\u2d6b\u2d6c\u2d6d\u2d6e\u2d70\u2d71\u2d72\u2d73\u2d74\u2d75\u2d76\u2d77\u2d78\u2d79\u2d7a\u2d7b\u2d7c\u2d7d\u2d7e\u2d7f\u2d97\u2d98\u2d99\u2d9a\u2d9b\u2d9c\u2d9d\u2d9e\u2d9f\u2da7\u2daf\u2db7\u2dbf\u2dc7\u2dcf\u2dd7\u2ddf\u2de0\u2de1\u2de2\u2de3\u2de4\u2de5\u2de6\u2de7\u2de8\u2de9\u2dea\u2deb\u2dec\u2ded\u2dee\u2def\u2df0\u2df1\u2df2\u2df3\u2df4\u2df5\u2df6\u2df7\u2df8\u2df9\u2dfa\u2dfb\u2dfc\u2dfd\u2dfe\u2dff\u2e18\u2e19\u2e1a\u2e1b\u2e1e\u2e1f\u2e20\u2e21\u2e22\u2e23\u2e24\u2e25\u2e26\u2e27\u2e28\u2e29\u2e2a\u2e2b\u2e2c\u2e2d\u2e2e\u2e2f\u2e30\u2e31\u2e32\u2e33\u2e34\u2e35\u2e36\u2e37\u2e38\u2e39\u2e3a\u2e3b\u2e3c\u2e3d\u2e3e\u2e3f\u2e40\u2e41\u2e42\u2e43\u2e44\u2e45\u2e46\u2e47\u2e48\u2e49\u2e4a\u2e4b\u2e4c\u2e4d\u2e4e\u2e4f\u2e50\u2e51\u2e52\u2e53\u2e54\u2e55\u2e56\u2e57\u2e58\u2e59\u2e5a\u2e5b\u2e5c\u2e5d\u2e5e\u2e5f\u2e60\u2e61\u2e62\u2e63\u2e64\u2e65\u2e66\u2e67\u2e68\u2e69\u2e6a\u2e6b\u2e6c\u2e6d\u2e6e\u2e6f\u2e70\u2e71\u2e72\u2e73\u2e74\u2e75\u2e76\u2e77\u2e78\u2e79\u2e7a\u2e7b\u2e7c\u2e7d\u2e7e\u2e7f\u2e9a\u2ef4\u2ef5\u2ef6\u2ef7\u2ef8\u2ef9\u2efa\u2efb\u2efc\u2efd\u2efe\u2eff\u2fd6\u2fd7\u2fd8\u2fd9\u2fda\u2fdb\u2fdc\u2fdd\u2fde\u2fdf\u2fe0\u2fe1\u2fe2\u2fe3\u2fe4\u2fe5\u2fe6\u2fe7\u2fe8\u2fe9\u2fea\u2feb\u2fec\u2fed\u2fee\u2fef\u2ffc\u2ffd\u2ffe\u2fff\u3040\u3097\u3098\u3100\u3101\u3102\u3103\u3104\u312d\u312e\u312f\u3130\u318f\u31b8\u31b9\u31ba\u31bb\u31bc\u31bd\u31be\u31bf\u31d0\u31d1\u31d2\u31d3\u31d4\u31d5\u31d6\u31d7\u31d8\u31d9\u31da\u31db\u31dc\u31dd\u31de\u31df\u31e0\u31e1\u31e2\u31e3\u31e4\u31e5\u31e6\u31e7\u31e8\u31e9\u31ea\u31eb\u31ec\u31ed\u31ee\u31ef\u321f\u3244\u3245\u3246\u3247\u3248\u3249\u324a\u324b\u324c\u324d\u324e\u324f\u32ff\u4db6\u4db7\u4db8\u4db9\u4dba\u4dbb\u4dbc\u4dbd\u4dbe\u4dbf\u9fbc\u9fbd\u9fbe\u9fbf\u9fc0\u9fc1\u9fc2\u9fc3\u9fc4\u9fc5\u9fc6\u9fc7\u9fc8\u9fc9\u9fca\u9fcb\u9fcc\u9fcd\u9fce\u9fcf\u9fd0\u9fd1\u9fd2\u9fd3\u9fd4\u9fd5\u9fd6\u9fd7\u9fd8\u9fd9\u9fda\u9fdb\u9fdc\u9fdd\u9fde\u9fdf\u9fe0\u9fe1\u9fe2\u9fe3\u9fe4\u9fe5\u9fe6\u9fe7\u9fe8\u9fe9\u9fea\u9feb\u9fec\u9fed\u9fee\u9fef\u9ff0\u9ff1\u9ff2\u9ff3\u9ff4\u9ff5\u9ff6\u9ff7\u9ff8\u9ff9\u9ffa\u9ffb\u9ffc\u9ffd\u9ffe\u9fff\ua48d\ua48e\ua48f\ua4c7\ua4c8\ua4c9\ua4ca\ua4cb\ua4cc\ua4cd\ua4ce\ua4cf\ua4d0\ua4d1\ua4d2\ua4d3\ua4d4\ua4d5\ua4d6\ua4d7\ua4d8\ua4d9\ua4da\ua4db\ua4dc\ua4dd\ua4de\ua4df\ua4e0\ua4e1\ua4e2\ua4e3\ua4e4\ua4e5\ua4e6\ua4e7\ua4e8\ua4e9\ua4ea\ua4eb\ua4ec\ua4ed\ua4ee\ua4ef\ua4f0\ua4f1\ua4f2\ua4f3\ua4f4\ua4f5\ua4f6\ua4f7\ua4f8\ua4f9\ua4fa\ua4fb\ua4fc\ua4fd\ua4fe\ua4ff\ua500\ua501\ua502\ua503\ua504\ua505\ua506\ua507\ua508\ua509\ua50a\ua50b\ua50c\ua50d\ua50e\ua50f\ua510\ua511\ua512\ua513\ua514\ua515\ua516\ua517\ua518\ua519\ua51a\ua51b\ua51c\ua51d\ua51e\ua51f\ua520\ua521\ua522\ua523\ua524\ua525\ua526\ua527\ua528\ua529\ua52a\ua52b\ua52c\ua52d\ua52e\ua52f\ua530\ua531\ua532\ua533\ua534\ua535\ua536\ua537\ua538\ua539\ua53a\ua53b\ua53c\ua53d\ua53e\ua53f\ua540\ua541\ua542\ua543\ua544\ua545\ua546\ua547\ua548\ua549\ua54a\ua54b\ua54c\ua54d\ua54e\ua54f\ua550\ua551\ua552\ua553\ua554\ua555\ua556\ua557\ua558\ua559\ua55a\ua55b\ua55c\ua55d\ua55e\ua55f\ua560\ua561\ua562\ua563\ua564\ua565\ua566\ua567\ua568\ua569\ua56a\ua56b\ua56c\ua56d\ua56e\ua56f\ua570\ua571\ua572\ua573\ua574\ua575\ua576\ua577\ua578\ua579\ua57a\ua57b\ua57c\ua57d\ua57e\ua57f\ua580\ua581\ua582\ua583\ua584\ua585\ua586\ua587\ua588\ua589\ua58a\ua58b\ua58c\ua58d\ua58e\ua58f\ua590\ua591\ua592\ua593\ua594\ua595\ua596\ua597\ua598\ua599\ua59a\ua59b\ua59c\ua59d\ua59e\ua59f\ua5a0\ua5a1\ua5a2\ua5a3\ua5a4\ua5a5\ua5a6\ua5a7\ua5a8\ua5a9\ua5aa\ua5ab\ua5ac\ua5ad\ua5ae\ua5af\ua5b0\ua5b1\ua5b2\ua5b3\ua5b4\ua5b5\ua5b6\ua5b7\ua5b8\ua5b9\ua5ba\ua5bb\ua5bc\ua5bd\ua5be\ua5bf\ua5c0\ua5c1\ua5c2\ua5c3\ua5c4\ua5c5\ua5c6\ua5c7\ua5c8\ua5c9\ua5ca\ua5cb\ua5cc\ua5cd\ua5ce\ua5cf\ua5d0\ua5d1\ua5d2\ua5d3\ua5d4\ua5d5\ua5d6\ua5d7\ua5d8\ua5d9\ua5da\ua5db\ua5dc\ua5dd\ua5de\ua5df\ua5e0\ua5e1\ua5e2\ua5e3\ua5e4\ua5e5\ua5e6\ua5e7\ua5e8\ua5e9\ua5ea\ua5eb\ua5ec\ua5ed\ua5ee\ua5ef\ua5f0\ua5f1\ua5f2\ua5f3\ua5f4\ua5f5\ua5f6\ua5f7\ua5f8\ua5f9\ua5fa\ua5fb\ua5fc\ua5fd\ua5fe\ua5ff\ua600\ua601\ua602\ua603\ua604\ua605\ua606\ua607\ua608\ua609\ua60a\ua60b\ua60c\ua60d\ua60e\ua60f\ua610\ua611\ua612\ua613\ua614\ua615\ua616\ua617\ua618\ua619\ua61a\ua61b\ua61c\ua61d\ua61e\ua61f\ua620\ua621\ua622\ua623\ua624\ua625\ua626\ua627\ua628\ua629\ua62a\ua62b\ua62c\ua62d\ua62e\ua62f\ua630\ua631\ua632\ua633\ua634\ua635\ua636\ua637\ua638\ua639\ua63a\ua63b\ua63c\ua63d\ua63e\ua63f\ua640\ua641\ua642\ua643\ua644\ua645\ua646\ua647\ua648\ua649\ua64a\ua64b\ua64c\ua64d\ua64e\ua64f\ua650\ua651\ua652\ua653\ua654\ua655\ua656\ua657\ua658\ua659\ua65a\ua65b\ua65c\ua65d\ua65e\ua65f\ua660\ua661\ua662\ua663\ua664\ua665\ua666\ua667\ua668\ua669\ua66a\ua66b\ua66c\ua66d\ua66e\ua66f\ua670\ua671\ua672\ua673\ua674\ua675\ua676\ua677\ua678\ua679\ua67a\ua67b\ua67c\ua67d\ua67e\ua67f\ua680\ua681\ua682\ua683\ua684\ua685\ua686\ua687\ua688\ua689\ua68a\ua68b\ua68c\ua68d\ua68e\ua68f\ua690\ua691\ua692\ua693\ua694\ua695\ua696\ua697\ua698\ua699\ua69a\ua69b\ua69c\ua69d\ua69e\ua69f\ua6a0\ua6a1\ua6a2\ua6a3\ua6a4\ua6a5\ua6a6\ua6a7\ua6a8\ua6a9\ua6aa\ua6ab\ua6ac\ua6ad\ua6ae\ua6af\ua6b0\ua6b1\ua6b2\ua6b3\ua6b4\ua6b5\ua6b6\ua6b7\ua6b8\ua6b9\ua6ba\ua6bb\ua6bc\ua6bd\ua6be\ua6bf\ua6c0\ua6c1\ua6c2\ua6c3\ua6c4\ua6c5\ua6c6\ua6c7\ua6c8\ua6c9\ua6ca\ua6cb\ua6cc\ua6cd\ua6ce\ua6cf\ua6d0\ua6d1\ua6d2\ua6d3\ua6d4\ua6d5\ua6d6\ua6d7\ua6d8\ua6d9\ua6da\ua6db\ua6dc\ua6dd\ua6de\ua6df\ua6e0\ua6e1\ua6e2\ua6e3\ua6e4\ua6e5\ua6e6\ua6e7\ua6e8\ua6e9\ua6ea\ua6eb\ua6ec\ua6ed\ua6ee\ua6ef\ua6f0\ua6f1\ua6f2\ua6f3\ua6f4\ua6f5\ua6f6\ua6f7\ua6f8\ua6f9\ua6fa\ua6fb\ua6fc\ua6fd\ua6fe\ua6ff\ua717\ua718\ua719\ua71a\ua71b\ua71c\ua71d\ua71e\ua71f\ua720\ua721\ua722\ua723\ua724\ua725\ua726\ua727\ua728\ua729\ua72a\ua72b\ua72c\ua72d\ua72e\ua72f\ua730\ua731\ua732\ua733\ua734\ua735\ua736\ua737\ua738\ua739\ua73a\ua73b\ua73c\ua73d\ua73e\ua73f\ua740\ua741\ua742\ua743\ua744\ua745\ua746\ua747\ua748\ua749\ua74a\ua74b\ua74c\ua74d\ua74e\ua74f\ua750\ua751\ua752\ua753\ua754\ua755\ua756\ua757\ua758\ua759\ua75a\ua75b\ua75c\ua75d\ua75e\ua75f\ua760\ua761\ua762\ua763\ua764\ua765\ua766\ua767\ua768\ua769\ua76a\ua76b\ua76c\ua76d\ua76e\ua76f\ua770\ua771\ua772\ua773\ua774\ua775\ua776\ua777\ua778\ua779\ua77a\ua77b\ua77c\ua77d\ua77e\ua77f\ua780\ua781\ua782\ua783\ua784\ua785\ua786\ua787\ua788\ua789\ua78a\ua78b\ua78c\ua78d\ua78e\ua78f\ua790\ua791\ua792\ua793\ua794\ua795\ua796\ua797\ua798\ua799\ua79a\ua79b\ua79c\ua79d\ua79e\ua79f\ua7a0\ua7a1\ua7a2\ua7a3\ua7a4\ua7a5\ua7a6\ua7a7\ua7a8\ua7a9\ua7aa\ua7ab\ua7ac\ua7ad\ua7ae\ua7af\ua7b0\ua7b1\ua7b2\ua7b3\ua7b4\ua7b5\ua7b6\ua7b7\ua7b8\ua7b9\ua7ba\ua7bb\ua7bc\ua7bd\ua7be\ua7bf\ua7c0\ua7c1\ua7c2\ua7c3\ua7c4\ua7c5\ua7c6\ua7c7\ua7c8\ua7c9\ua7ca\ua7cb\ua7cc\ua7cd\ua7ce\ua7cf\ua7d0\ua7d1\ua7d2\ua7d3\ua7d4\ua7d5\ua7d6\ua7d7\ua7d8\ua7d9\ua7da\ua7db\ua7dc\ua7dd\ua7de\ua7df\ua7e0\ua7e1\ua7e2\ua7e3\ua7e4\ua7e5\ua7e6\ua7e7\ua7e8\ua7e9\ua7ea\ua7eb\ua7ec\ua7ed\ua7ee\ua7ef\ua7f0\ua7f1\ua7f2\ua7f3\ua7f4\ua7f5\ua7f6\ua7f7\ua7f8\ua7f9\ua7fa\ua7fb\ua7fc\ua7fd\ua7fe\ua7ff\ua82c\ua82d\ua82e\ua82f\ua830\ua831\ua832\ua833\ua834\ua835\ua836\ua837\ua838\ua839\ua83a\ua83b\ua83c\ua83d\ua83e\ua83f\ua840\ua841\ua842\ua843\ua844\ua845\ua846\ua847\ua848\ua849\ua84a\ua84b\ua84c\ua84d\ua84e\ua84f\ua850\ua851\ua852\ua853\ua854\ua855\ua856\ua857\ua858\ua859\ua85a\ua85b\ua85c\ua85d\ua85e\ua85f\ua860\ua861\ua862\ua863\ua864\ua865\ua866\ua867\ua868\ua869\ua86a\ua86b\ua86c\ua86d\ua86e\ua86f\ua870\ua871\ua872\ua873\ua874\ua875\ua876\ua877\ua878\ua879\ua87a\ua87b\ua87c\ua87d\ua87e\ua87f\ua880\ua881\ua882\ua883\ua884\ua885\ua886\ua887\ua888\ua889\ua88a\ua88b\ua88c\ua88d\ua88e\ua88f\ua890\ua891\ua892\ua893\ua894\ua895\ua896\ua897\ua898\ua899\ua89a\ua89b\ua89c\ua89d\ua89e\ua89f\ua8a0\ua8a1\ua8a2\ua8a3\ua8a4\ua8a5\ua8a6\ua8a7\ua8a8\ua8a9\ua8aa\ua8ab\ua8ac\ua8ad\ua8ae\ua8af\ua8b0\ua8b1\ua8b2\ua8b3\ua8b4\ua8b5\ua8b6\ua8b7\ua8b8\ua8b9\ua8ba\ua8bb\ua8bc\ua8bd\ua8be\ua8bf\ua8c0\ua8c1\ua8c2\ua8c3\ua8c4\ua8c5\ua8c6\ua8c7\ua8c8\ua8c9\ua8ca\ua8cb\ua8cc\ua8cd\ua8ce\ua8cf\ua8d0\ua8d1\ua8d2\ua8d3\ua8d4\ua8d5\ua8d6\ua8d7\ua8d8\ua8d9\ua8da\ua8db\ua8dc\ua8dd\ua8de\ua8df\ua8e0\ua8e1\ua8e2\ua8e3\ua8e4\ua8e5\ua8e6\ua8e7\ua8e8\ua8e9\ua8ea\ua8eb\ua8ec\ua8ed\ua8ee\ua8ef\ua8f0\ua8f1\ua8f2\ua8f3\ua8f4\ua8f5\ua8f6\ua8f7\ua8f8\ua8f9\ua8fa\ua8fb\ua8fc\ua8fd\ua8fe\ua8ff\ua900\ua901\ua902\ua903\ua904\ua905\ua906\ua907\ua908\ua909\ua90a\ua90b\ua90c\ua90d\ua90e\ua90f\ua910\ua911\ua912\ua913\ua914\ua915\ua916\ua917\ua918\ua919\ua91a\ua91b\ua91c\ua91d\ua91e\ua91f\ua920\ua921\ua922\ua923\ua924\ua925\ua926\ua927\ua928\ua929\ua92a\ua92b\ua92c\ua92d\ua92e\ua92f\ua930\ua931\ua932\ua933\ua934\ua935\ua936\ua937\ua938\ua939\ua93a\ua93b\ua93c\ua93d\ua93e\ua93f\ua940\ua941\ua942\ua943\ua944\ua945\ua946\ua947\ua948\ua949\ua94a\ua94b\ua94c\ua94d\ua94e\ua94f\ua950\ua951\ua952\ua953\ua954\ua955\ua956\ua957\ua958\ua959\ua95a\ua95b\ua95c\ua95d\ua95e\ua95f\ua960\ua961\ua962\ua963\ua964\ua965\ua966\ua967\ua968\ua969\ua96a\ua96b\ua96c\ua96d\ua96e\ua96f\ua970\ua971\ua972\ua973\ua974\ua975\ua976\ua977\ua978\ua979\ua97a\ua97b\ua97c\ua97d\ua97e\ua97f\ua980\ua981\ua982\ua983\ua984\ua985\ua986\ua987\ua988\ua989\ua98a\ua98b\ua98c\ua98d\ua98e\ua98f\ua990\ua991\ua992\ua993\ua994\ua995\ua996\ua997\ua998\ua999\ua99a\ua99b\ua99c\ua99d\ua99e\ua99f\ua9a0\ua9a1\ua9a2\ua9a3\ua9a4\ua9a5\ua9a6\ua9a7\ua9a8\ua9a9\ua9aa\ua9ab\ua9ac\ua9ad\ua9ae\ua9af\ua9b0\ua9b1\ua9b2\ua9b3\ua9b4\ua9b5\ua9b6\ua9b7\ua9b8\ua9b9\ua9ba\ua9bb\ua9bc\ua9bd\ua9be\ua9bf\ua9c0\ua9c1\ua9c2\ua9c3\ua9c4\ua9c5\ua9c6\ua9c7\ua9c8\ua9c9\ua9ca\ua9cb\ua9cc\ua9cd\ua9ce\ua9cf\ua9d0\ua9d1\ua9d2\ua9d3\ua9d4\ua9d5\ua9d6\ua9d7\ua9d8\ua9d9\ua9da\ua9db\ua9dc\ua9dd\ua9de\ua9df\ua9e0\ua9e1\ua9e2\ua9e3\ua9e4\ua9e5\ua9e6\ua9e7\ua9e8\ua9e9\ua9ea\ua9eb\ua9ec\ua9ed\ua9ee\ua9ef\ua9f0\ua9f1\ua9f2\ua9f3\ua9f4\ua9f5\ua9f6\ua9f7\ua9f8\ua9f9\ua9fa\ua9fb\ua9fc\ua9fd\ua9fe\ua9ff\uaa00\uaa01\uaa02\uaa03\uaa04\uaa05\uaa06\uaa07\uaa08\uaa09\uaa0a\uaa0b\uaa0c\uaa0d\uaa0e\uaa0f\uaa10\uaa11\uaa12\uaa13\uaa14\uaa15\uaa16\uaa17\uaa18\uaa19\uaa1a\uaa1b\uaa1c\uaa1d\uaa1e\uaa1f\uaa20\uaa21\uaa22\uaa23\uaa24\uaa25\uaa26\uaa27\uaa28\uaa29\uaa2a\uaa2b\uaa2c\uaa2d\uaa2e\uaa2f\uaa30\uaa31\uaa32\uaa33\uaa34\uaa35\uaa36\uaa37\uaa38\uaa39\uaa3a\uaa3b\uaa3c\uaa3d\uaa3e\uaa3f\uaa40\uaa41\uaa42\uaa43\uaa44\uaa45\uaa46\uaa47\uaa48\uaa49\uaa4a\uaa4b\uaa4c\uaa4d\uaa4e\uaa4f\uaa50\uaa51\uaa52\uaa53\uaa54\uaa55\uaa56\uaa57\uaa58\uaa59\uaa5a\uaa5b\uaa5c\uaa5d\uaa5e\uaa5f\uaa60\uaa61\uaa62\uaa63\uaa64\uaa65\uaa66\uaa67\uaa68\uaa69\uaa6a\uaa6b\uaa6c\uaa6d\uaa6e\uaa6f\uaa70\uaa71\uaa72\uaa73\uaa74\uaa75\uaa76\uaa77\uaa78\uaa79\uaa7a\uaa7b\uaa7c\uaa7d\uaa7e\uaa7f\uaa80\uaa81\uaa82\uaa83\uaa84\uaa85\uaa86\uaa87\uaa88\uaa89\uaa8a\uaa8b\uaa8c\uaa8d\uaa8e\uaa8f\uaa90\uaa91\uaa92\uaa93\uaa94\uaa95\uaa96\uaa97\uaa98\uaa99\uaa9a\uaa9b\uaa9c\uaa9d\uaa9e\uaa9f\uaaa0\uaaa1\uaaa2\uaaa3\uaaa4\uaaa5\uaaa6\uaaa7\uaaa8\uaaa9\uaaaa\uaaab\uaaac\uaaad\uaaae\uaaaf\uaab0\uaab1\uaab2\uaab3\uaab4\uaab5\uaab6\uaab7\uaab8\uaab9\uaaba\uaabb\uaabc\uaabd\uaabe\uaabf\uaac0\uaac1\uaac2\uaac3\uaac4\uaac5\uaac6\uaac7\uaac8\uaac9\uaaca\uaacb\uaacc\uaacd\uaace\uaacf\uaad0\uaad1\uaad2\uaad3\uaad4\uaad5\uaad6\uaad7\uaad8\uaad9\uaada\uaadb\uaadc\uaadd\uaade\uaadf\uaae0\uaae1\uaae2\uaae3\uaae4\uaae5\uaae6\uaae7\uaae8\uaae9\uaaea\uaaeb\uaaec\uaaed\uaaee\uaaef\uaaf0\uaaf1\uaaf2\uaaf3\uaaf4\uaaf5\uaaf6\uaaf7\uaaf8\uaaf9\uaafa\uaafb\uaafc\uaafd\uaafe\uaaff\uab00\uab01\uab02\uab03\uab04\uab05\uab06\uab07\uab08\uab09\uab0a\uab0b\uab0c\uab0d\uab0e\uab0f\uab10\uab11\uab12\uab13\uab14\uab15\uab16\uab17\uab18\uab19\uab1a\uab1b\uab1c\uab1d\uab1e\uab1f\uab20\uab21\uab22\uab23\uab24\uab25\uab26\uab27\uab28\uab29\uab2a\uab2b\uab2c\uab2d\uab2e\uab2f\uab30\uab31\uab32\uab33\uab34\uab35\uab36\uab37\uab38\uab39\uab3a\uab3b\uab3c\uab3d\uab3e\uab3f\uab40\uab41\uab42\uab43\uab44\uab45\uab46\uab47\uab48\uab49\uab4a\uab4b\uab4c\uab4d\uab4e\uab4f\uab50\uab51\uab52\uab53\uab54\uab55\uab56\uab57\uab58\uab59\uab5a\uab5b\uab5c\uab5d\uab5e\uab5f\uab60\uab61\uab62\uab63\uab64\uab65\uab66\uab67\uab68\uab69\uab6a\uab6b\uab6c\uab6d\uab6e\uab6f\uab70\uab71\uab72\uab73\uab74\uab75\uab76\uab77\uab78\uab79\uab7a\uab7b\uab7c\uab7d\uab7e\uab7f\uab80\uab81\uab82\uab83\uab84\uab85\uab86\uab87\uab88\uab89\uab8a\uab8b\uab8c\uab8d\uab8e\uab8f\uab90\uab91\uab92\uab93\uab94\uab95\uab96\uab97\uab98\uab99\uab9a\uab9b\uab9c\uab9d\uab9e\uab9f\uaba0\uaba1\uaba2\uaba3\uaba4\uaba5\uaba6\uaba7\uaba8\uaba9\uabaa\uabab\uabac\uabad\uabae\uabaf\uabb0\uabb1\uabb2\uabb3\uabb4\uabb5\uabb6\uabb7\uabb8\uabb9\uabba\uabbb\uabbc\uabbd\uabbe\uabbf\uabc0\uabc1\uabc2\uabc3\uabc4\uabc5\uabc6\uabc7\uabc8\uabc9\uabca\uabcb\uabcc\uabcd\uabce\uabcf\uabd0\uabd1\uabd2\uabd3\uabd4\uabd5\uabd6\uabd7\uabd8\uabd9\uabda\uabdb\uabdc\uabdd\uabde\uabdf\uabe0\uabe1\uabe2\uabe3\uabe4\uabe5\uabe6\uabe7\uabe8\uabe9\uabea\uabeb\uabec\uabed\uabee\uabef\uabf0\uabf1\uabf2\uabf3\uabf4\uabf5\uabf6\uabf7\uabf8\uabf9\uabfa\uabfb\uabfc\uabfd\uabfe\uabff\ud7a4\ud7a5\ud7a6\ud7a7\ud7a8\ud7a9\ud7aa\ud7ab\ud7ac\ud7ad\ud7ae\ud7af\ud7b0\ud7b1\ud7b2\ud7b3\ud7b4\ud7b5\ud7b6\ud7b7\ud7b8\ud7b9\ud7ba\ud7bb\ud7bc\ud7bd\ud7be\ud7bf\ud7c0\ud7c1\ud7c2\ud7c3\ud7c4\ud7c5\ud7c6\ud7c7\ud7c8\ud7c9\ud7ca\ud7cb\ud7cc\ud7cd\ud7ce\ud7cf\ud7d0\ud7d1\ud7d2\ud7d3\ud7d4\ud7d5\ud7d6\ud7d7\ud7d8\ud7d9\ud7da\ud7db\ud7dc\ud7dd\ud7de\ud7df\ud7e0\ud7e1\ud7e2\ud7e3\ud7e4\ud7e5\ud7e6\ud7e7\ud7e8\ud7e9\ud7ea\ud7eb\ud7ec\ud7ed\ud7ee\ud7ef\ud7f0\ud7f1\ud7f2\ud7f3\ud7f4\ud7f5\ud7f6\ud7f7\ud7f8\ud7f9\ud7fa\ud7fb\ud7fc\ud7fd\ud7fe\ud7ff\ufa2e\ufa2f\ufa6b\ufa6c\ufa6d\ufa6e\ufa6f\ufada\ufadb\ufadc\ufadd\ufade\ufadf\ufae0\ufae1\ufae2\ufae3\ufae4\ufae5\ufae6\ufae7\ufae8\ufae9\ufaea\ufaeb\ufaec\ufaed\ufaee\ufaef\ufaf0\ufaf1\ufaf2\ufaf3\ufaf4\ufaf5\ufaf6\ufaf7\ufaf8\ufaf9\ufafa\ufafb\ufafc\ufafd\ufafe\ufaff\ufb07\ufb08\ufb09\ufb0a\ufb0b\ufb0c\ufb0d\ufb0e\ufb0f\ufb10\ufb11\ufb12\ufb18\ufb19\ufb1a\ufb1b\ufb1c\ufb37\ufb3d\ufb3f\ufb42\ufb45\ufbb2\ufbb3\ufbb4\ufbb5\ufbb6\ufbb7\ufbb8\ufbb9\ufbba\ufbbb\ufbbc\ufbbd\ufbbe\ufbbf\ufbc0\ufbc1\ufbc2\ufbc3\ufbc4\ufbc5\ufbc6\ufbc7\ufbc8\ufbc9\ufbca\ufbcb\ufbcc\ufbcd\ufbce\ufbcf\ufbd0\ufbd1\ufbd2\ufd40\ufd41\ufd42\ufd43\ufd44\ufd45\ufd46\ufd47\ufd48\ufd49\ufd4a\ufd4b\ufd4c\ufd4d\ufd4e\ufd4f\ufd90\ufd91\ufdc8\ufdc9\ufdca\ufdcb\ufdcc\ufdcd\ufdce\ufdcf\ufdd0\ufdd1\ufdd2\ufdd3\ufdd4\ufdd5\ufdd6\ufdd7\ufdd8\ufdd9\ufdda\ufddb\ufddc\ufddd\ufdde\ufddf\ufde0\ufde1\ufde2\ufde3\ufde4\ufde5\ufde6\ufde7\ufde8\ufde9\ufdea\ufdeb\ufdec\ufded\ufdee\ufdef\ufdfe\ufdff\ufe1a\ufe1b\ufe1c\ufe1d\ufe1e\ufe1f\ufe24\ufe25\ufe26\ufe27\ufe28\ufe29\ufe2a\ufe2b\ufe2c\ufe2d\ufe2e\ufe2f\ufe53\ufe67\ufe6c\ufe6d\ufe6e\ufe6f\ufe75\ufefd\ufefe\uff00\uffbf\uffc0\uffc1\uffc8\uffc9\uffd0\uffd1\uffd8\uffd9\uffdd\uffde\uffdf\uffe7\uffef\ufff0\ufff1\ufff2\ufff3\ufff4\ufff5\ufff6\ufff7\ufff8\ufffe'
-
-Co = u'\ue000\ue001\ue002\ue003\ue004\ue005\ue006\ue007\ue008\ue009\ue00a\ue00b\ue00c\ue00d\ue00e\ue00f\ue010\ue011\ue012\ue013\ue014\ue015\ue016\ue017\ue018\ue019\ue01a\ue01b\ue01c\ue01d\ue01e\ue01f\ue020\ue021\ue022\ue023\ue024\ue025\ue026\ue027\ue028\ue029\ue02a\ue02b\ue02c\ue02d\ue02e\ue02f\ue030\ue031\ue032\ue033\ue034\ue035\ue036\ue037\ue038\ue039\ue03a\ue03b\ue03c\ue03d\ue03e\ue03f\ue040\ue041\ue042\ue043\ue044\ue045\ue046\ue047\ue048\ue049\ue04a\ue04b\ue04c\ue04d\ue04e\ue04f\ue050\ue051\ue052\ue053\ue054\ue055\ue056\ue057\ue058\ue059\ue05a\ue05b\ue05c\ue05d\ue05e\ue05f\ue060\ue061\ue062\ue063\ue064\ue065\ue066\ue067\ue068\ue069\ue06a\ue06b\ue06c\ue06d\ue06e\ue06f\ue070\ue071\ue072\ue073\ue074\ue075\ue076\ue077\ue078\ue079\ue07a\ue07b\ue07c\ue07d\ue07e\ue07f\ue080\ue081\ue082\ue083\ue084\ue085\ue086\ue087\ue088\ue089\ue08a\ue08b\ue08c\ue08d\ue08e\ue08f\ue090\ue091\ue092\ue093\ue094\ue095\ue096\ue097\ue098\ue099\ue09a\ue09b\ue09c\ue09d\ue09e\ue09f\ue0a0\ue0a1\ue0a2\ue0a3\ue0a4\ue0a5\ue0a6\ue0a7\ue0a8\ue0a9\ue0aa\ue0ab\ue0ac\ue0ad\ue0ae\ue0af\ue0b0\ue0b1\ue0b2\ue0b3\ue0b4\ue0b5\ue0b6\ue0b7\ue0b8\ue0b9\ue0ba\ue0bb\ue0bc\ue0bd\ue0be\ue0bf\ue0c0\ue0c1\ue0c2\ue0c3\ue0c4\ue0c5\ue0c6\ue0c7\ue0c8\ue0c9\ue0ca\ue0cb\ue0cc\ue0cd\ue0ce\ue0cf\ue0d0\ue0d1\ue0d2\ue0d3\ue0d4\ue0d5\ue0d6\ue0d7\ue0d8\ue0d9\ue0da\ue0db\ue0dc\ue0dd\ue0de\ue0df\ue0e0\ue0e1\ue0e2\ue0e3\ue0e4\ue0e5\ue0e6\ue0e7\ue0e8\ue0e9\ue0ea\ue0eb\ue0ec\ue0ed\ue0ee\ue0ef\ue0f0\ue0f1\ue0f2\ue0f3\ue0f4\ue0f5\ue0f6\ue0f7\ue0f8\ue0f9\ue0fa\ue0fb\ue0fc\ue0fd\ue0fe\ue0ff\ue100\ue101\ue102\ue103\ue104\ue105\ue106\ue107\ue108\ue109\ue10a\ue10b\ue10c\ue10d\ue10e\ue10f\ue110\ue111\ue112\ue113\ue114\ue115\ue116\ue117\ue118\ue119\ue11a\ue11b\ue11c\ue11d\ue11e\ue11f\ue120\ue121\ue122\ue123\ue124\ue125\ue126\ue127\ue128\ue129\ue12a\ue12b\ue12c\ue12d\ue12e\ue12f\ue130\ue131\ue132\ue133\ue134\ue135\ue136\ue137\ue138\ue139\ue13a\ue13b\ue13c\ue13d\ue13e\ue13f\ue140\ue141\ue142\ue143\ue144\ue145\ue146\ue147\ue148\ue149\ue14a\ue14b\ue14c\ue14d\ue14e\ue14f\ue150\ue151\ue152\ue153\ue154\ue155\ue156\ue157\ue158\ue159\ue15a\ue15b\ue15c\ue15d\ue15e\ue15f\ue160\ue161\ue162\ue163\ue164\ue165\ue166\ue167\ue168\ue169\ue16a\ue16b\ue16c\ue16d\ue16e\ue16f\ue170\ue171\ue172\ue173\ue174\ue175\ue176\ue177\ue178\ue179\ue17a\ue17b\ue17c\ue17d\ue17e\ue17f\ue180\ue181\ue182\ue183\ue184\ue185\ue186\ue187\ue188\ue189\ue18a\ue18b\ue18c\ue18d\ue18e\ue18f\ue190\ue191\ue192\ue193\ue194\ue195\ue196\ue197\ue198\ue199\ue19a\ue19b\ue19c\ue19d\ue19e\ue19f\ue1a0\ue1a1\ue1a2\ue1a3\ue1a4\ue1a5\ue1a6\ue1a7\ue1a8\ue1a9\ue1aa\ue1ab\ue1ac\ue1ad\ue1ae\ue1af\ue1b0\ue1b1\ue1b2\ue1b3\ue1b4\ue1b5\ue1b6\ue1b7\ue1b8\ue1b9\ue1ba\ue1bb\ue1bc\ue1bd\ue1be\ue1bf\ue1c0\ue1c1\ue1c2\ue1c3\ue1c4\ue1c5\ue1c6\ue1c7\ue1c8\ue1c9\ue1ca\ue1cb\ue1cc\ue1cd\ue1ce\ue1cf\ue1d0\ue1d1\ue1d2\ue1d3\ue1d4\ue1d5\ue1d6\ue1d7\ue1d8\ue1d9\ue1da\ue1db\ue1dc\ue1dd\ue1de\ue1df\ue1e0\ue1e1\ue1e2\ue1e3\ue1e4\ue1e5\ue1e6\ue1e7\ue1e8\ue1e9\ue1ea\ue1eb\ue1ec\ue1ed\ue1ee\ue1ef\ue1f0\ue1f1\ue1f2\ue1f3\ue1f4\ue1f5\ue1f6\ue1f7\ue1f8\ue1f9\ue1fa\ue1fb\ue1fc\ue1fd\ue1fe\ue1ff\ue200\ue201\ue202\ue203\ue204\ue205\ue206\ue207\ue208\ue209\ue20a\ue20b\ue20c\ue20d\ue20e\ue20f\ue210\ue211\ue212\ue213\ue214\ue215\ue216\ue217\ue218\ue219\ue21a\ue21b\ue21c\ue21d\ue21e\ue21f\ue220\ue221\ue222\ue223\ue224\ue225\ue226\ue227\ue228\ue229\ue22a\ue22b\ue22c\ue22d\ue22e\ue22f\ue230\ue231\ue232\ue233\ue234\ue235\ue236\ue237\ue238\ue239\ue23a\ue23b\ue23c\ue23d\ue23e\ue23f\ue240\ue241\ue242\ue243\ue244\ue245\ue246\ue247\ue248\ue249\ue24a\ue24b\ue24c\ue24d\ue24e\ue24f\ue250\ue251\ue252\ue253\ue254\ue255\ue256\ue257\ue258\ue259\ue25a\ue25b\ue25c\ue25d\ue25e\ue25f\ue260\ue261\ue262\ue263\ue264\ue265\ue266\ue267\ue268\ue269\ue26a\ue26b\ue26c\ue26d\ue26e\ue26f\ue270\ue271\ue272\ue273\ue274\ue275\ue276\ue277\ue278\ue279\ue27a\ue27b\ue27c\ue27d\ue27e\ue27f\ue280\ue281\ue282\ue283\ue284\ue285\ue286\ue287\ue288\ue289\ue28a\ue28b\ue28c\ue28d\ue28e\ue28f\ue290\ue291\ue292\ue293\ue294\ue295\ue296\ue297\ue298\ue299\ue29a\ue29b\ue29c\ue29d\ue29e\ue29f\ue2a0\ue2a1\ue2a2\ue2a3\ue2a4\ue2a5\ue2a6\ue2a7\ue2a8\ue2a9\ue2aa\ue2ab\ue2ac\ue2ad\ue2ae\ue2af\ue2b0\ue2b1\ue2b2\ue2b3\ue2b4\ue2b5\ue2b6\ue2b7\ue2b8\ue2b9\ue2ba\ue2bb\ue2bc\ue2bd\ue2be\ue2bf\ue2c0\ue2c1\ue2c2\ue2c3\ue2c4\ue2c5\ue2c6\ue2c7\ue2c8\ue2c9\ue2ca\ue2cb\ue2cc\ue2cd\ue2ce\ue2cf\ue2d0\ue2d1\ue2d2\ue2d3\ue2d4\ue2d5\ue2d6\ue2d7\ue2d8\ue2d9\ue2da\ue2db\ue2dc\ue2dd\ue2de\ue2df\ue2e0\ue2e1\ue2e2\ue2e3\ue2e4\ue2e5\ue2e6\ue2e7\ue2e8\ue2e9\ue2ea\ue2eb\ue2ec\ue2ed\ue2ee\ue2ef\ue2f0\ue2f1\ue2f2\ue2f3\ue2f4\ue2f5\ue2f6\ue2f7\ue2f8\ue2f9\ue2fa\ue2fb\ue2fc\ue2fd\ue2fe\ue2ff\ue300\ue301\ue302\ue303\ue304\ue305\ue306\ue307\ue308\ue309\ue30a\ue30b\ue30c\ue30d\ue30e\ue30f\ue310\ue311\ue312\ue313\ue314\ue315\ue316\ue317\ue318\ue319\ue31a\ue31b\ue31c\ue31d\ue31e\ue31f\ue320\ue321\ue322\ue323\ue324\ue325\ue326\ue327\ue328\ue329\ue32a\ue32b\ue32c\ue32d\ue32e\ue32f\ue330\ue331\ue332\ue333\ue334\ue335\ue336\ue337\ue338\ue339\ue33a\ue33b\ue33c\ue33d\ue33e\ue33f\ue340\ue341\ue342\ue343\ue344\ue345\ue346\ue347\ue348\ue349\ue34a\ue34b\ue34c\ue34d\ue34e\ue34f\ue350\ue351\ue352\ue353\ue354\ue355\ue356\ue357\ue358\ue359\ue35a\ue35b\ue35c\ue35d\ue35e\ue35f\ue360\ue361\ue362\ue363\ue364\ue365\ue366\ue367\ue368\ue369\ue36a\ue36b\ue36c\ue36d\ue36e\ue36f\ue370\ue371\ue372\ue373\ue374\ue375\ue376\ue377\ue378\ue379\ue37a\ue37b\ue37c\ue37d\ue37e\ue37f\ue380\ue381\ue382\ue383\ue384\ue385\ue386\ue387\ue388\ue389\ue38a\ue38b\ue38c\ue38d\ue38e\ue38f\ue390\ue391\ue392\ue393\ue394\ue395\ue396\ue397\ue398\ue399\ue39a\ue39b\ue39c\ue39d\ue39e\ue39f\ue3a0\ue3a1\ue3a2\ue3a3\ue3a4\ue3a5\ue3a6\ue3a7\ue3a8\ue3a9\ue3aa\ue3ab\ue3ac\ue3ad\ue3ae\ue3af\ue3b0\ue3b1\ue3b2\ue3b3\ue3b4\ue3b5\ue3b6\ue3b7\ue3b8\ue3b9\ue3ba\ue3bb\ue3bc\ue3bd\ue3be\ue3bf\ue3c0\ue3c1\ue3c2\ue3c3\ue3c4\ue3c5\ue3c6\ue3c7\ue3c8\ue3c9\ue3ca\ue3cb\ue3cc\ue3cd\ue3ce\ue3cf\ue3d0\ue3d1\ue3d2\ue3d3\ue3d4\ue3d5\ue3d6\ue3d7\ue3d8\ue3d9\ue3da\ue3db\ue3dc\ue3dd\ue3de\ue3df\ue3e0\ue3e1\ue3e2\ue3e3\ue3e4\ue3e5\ue3e6\ue3e7\ue3e8\ue3e9\ue3ea\ue3eb\ue3ec\ue3ed\ue3ee\ue3ef\ue3f0\ue3f1\ue3f2\ue3f3\ue3f4\ue3f5\ue3f6\ue3f7\ue3f8\ue3f9\ue3fa\ue3fb\ue3fc\ue3fd\ue3fe\ue3ff\ue400\ue401\ue402\ue403\ue404\ue405\ue406\ue407\ue408\ue409\ue40a\ue40b\ue40c\ue40d\ue40e\ue40f\ue410\ue411\ue412\ue413\ue414\ue415\ue416\ue417\ue418\ue419\ue41a\ue41b\ue41c\ue41d\ue41e\ue41f\ue420\ue421\ue422\ue423\ue424\ue425\ue426\ue427\ue428\ue429\ue42a\ue42b\ue42c\ue42d\ue42e\ue42f\ue430\ue431\ue432\ue433\ue434\ue435\ue436\ue437\ue438\ue439\ue43a\ue43b\ue43c\ue43d\ue43e\ue43f\ue440\ue441\ue442\ue443\ue444\ue445\ue446\ue447\ue448\ue449\ue44a\ue44b\ue44c\ue44d\ue44e\ue44f\ue450\ue451\ue452\ue453\ue454\ue455\ue456\ue457\ue458\ue459\ue45a\ue45b\ue45c\ue45d\ue45e\ue45f\ue460\ue461\ue462\ue463\ue464\ue465\ue466\ue467\ue468\ue469\ue46a\ue46b\ue46c\ue46d\ue46e\ue46f\ue470\ue471\ue472\ue473\ue474\ue475\ue476\ue477\ue478\ue479\ue47a\ue47b\ue47c\ue47d\ue47e\ue47f\ue480\ue481\ue482\ue483\ue484\ue485\ue486\ue487\ue488\ue489\ue48a\ue48b\ue48c\ue48d\ue48e\ue48f\ue490\ue491\ue492\ue493\ue494\ue495\ue496\ue497\ue498\ue499\ue49a\ue49b\ue49c\ue49d\ue49e\ue49f\ue4a0\ue4a1\ue4a2\ue4a3\ue4a4\ue4a5\ue4a6\ue4a7\ue4a8\ue4a9\ue4aa\ue4ab\ue4ac\ue4ad\ue4ae\ue4af\ue4b0\ue4b1\ue4b2\ue4b3\ue4b4\ue4b5\ue4b6\ue4b7\ue4b8\ue4b9\ue4ba\ue4bb\ue4bc\ue4bd\ue4be\ue4bf\ue4c0\ue4c1\ue4c2\ue4c3\ue4c4\ue4c5\ue4c6\ue4c7\ue4c8\ue4c9\ue4ca\ue4cb\ue4cc\ue4cd\ue4ce\ue4cf\ue4d0\ue4d1\ue4d2\ue4d3\ue4d4\ue4d5\ue4d6\ue4d7\ue4d8\ue4d9\ue4da\ue4db\ue4dc\ue4dd\ue4de\ue4df\ue4e0\ue4e1\ue4e2\ue4e3\ue4e4\ue4e5\ue4e6\ue4e7\ue4e8\ue4e9\ue4ea\ue4eb\ue4ec\ue4ed\ue4ee\ue4ef\ue4f0\ue4f1\ue4f2\ue4f3\ue4f4\ue4f5\ue4f6\ue4f7\ue4f8\ue4f9\ue4fa\ue4fb\ue4fc\ue4fd\ue4fe\ue4ff\ue500\ue501\ue502\ue503\ue504\ue505\ue506\ue507\ue508\ue509\ue50a\ue50b\ue50c\ue50d\ue50e\ue50f\ue510\ue511\ue512\ue513\ue514\ue515\ue516\ue517\ue518\ue519\ue51a\ue51b\ue51c\ue51d\ue51e\ue51f\ue520\ue521\ue522\ue523\ue524\ue525\ue526\ue527\ue528\ue529\ue52a\ue52b\ue52c\ue52d\ue52e\ue52f\ue530\ue531\ue532\ue533\ue534\ue535\ue536\ue537\ue538\ue539\ue53a\ue53b\ue53c\ue53d\ue53e\ue53f\ue540\ue541\ue542\ue543\ue544\ue545\ue546\ue547\ue548\ue549\ue54a\ue54b\ue54c\ue54d\ue54e\ue54f\ue550\ue551\ue552\ue553\ue554\ue555\ue556\ue557\ue558\ue559\ue55a\ue55b\ue55c\ue55d\ue55e\ue55f\ue560\ue561\ue562\ue563\ue564\ue565\ue566\ue567\ue568\ue569\ue56a\ue56b\ue56c\ue56d\ue56e\ue56f\ue570\ue571\ue572\ue573\ue574\ue575\ue576\ue577\ue578\ue579\ue57a\ue57b\ue57c\ue57d\ue57e\ue57f\ue580\ue581\ue582\ue583\ue584\ue585\ue586\ue587\ue588\ue589\ue58a\ue58b\ue58c\ue58d\ue58e\ue58f\ue590\ue591\ue592\ue593\ue594\ue595\ue596\ue597\ue598\ue599\ue59a\ue59b\ue59c\ue59d\ue59e\ue59f\ue5a0\ue5a1\ue5a2\ue5a3\ue5a4\ue5a5\ue5a6\ue5a7\ue5a8\ue5a9\ue5aa\ue5ab\ue5ac\ue5ad\ue5ae\ue5af\ue5b0\ue5b1\ue5b2\ue5b3\ue5b4\ue5b5\ue5b6\ue5b7\ue5b8\ue5b9\ue5ba\ue5bb\ue5bc\ue5bd\ue5be\ue5bf\ue5c0\ue5c1\ue5c2\ue5c3\ue5c4\ue5c5\ue5c6\ue5c7\ue5c8\ue5c9\ue5ca\ue5cb\ue5cc\ue5cd\ue5ce\ue5cf\ue5d0\ue5d1\ue5d2\ue5d3\ue5d4\ue5d5\ue5d6\ue5d7\ue5d8\ue5d9\ue5da\ue5db\ue5dc\ue5dd\ue5de\ue5df\ue5e0\ue5e1\ue5e2\ue5e3\ue5e4\ue5e5\ue5e6\ue5e7\ue5e8\ue5e9\ue5ea\ue5eb\ue5ec\ue5ed\ue5ee\ue5ef\ue5f0\ue5f1\ue5f2\ue5f3\ue5f4\ue5f5\ue5f6\ue5f7\ue5f8\ue5f9\ue5fa\ue5fb\ue5fc\ue5fd\ue5fe\ue5ff\ue600\ue601\ue602\ue603\ue604\ue605\ue606\ue607\ue608\ue609\ue60a\ue60b\ue60c\ue60d\ue60e\ue60f\ue610\ue611\ue612\ue613\ue614\ue615\ue616\ue617\ue618\ue619\ue61a\ue61b\ue61c\ue61d\ue61e\ue61f\ue620\ue621\ue622\ue623\ue624\ue625\ue626\ue627\ue628\ue629\ue62a\ue62b\ue62c\ue62d\ue62e\ue62f\ue630\ue631\ue632\ue633\ue634\ue635\ue636\ue637\ue638\ue639\ue63a\ue63b\ue63c\ue63d\ue63e\ue63f\ue640\ue641\ue642\ue643\ue644\ue645\ue646\ue647\ue648\ue649\ue64a\ue64b\ue64c\ue64d\ue64e\ue64f\ue650\ue651\ue652\ue653\ue654\ue655\ue656\ue657\ue658\ue659\ue65a\ue65b\ue65c\ue65d\ue65e\ue65f\ue660\ue661\ue662\ue663\ue664\ue665\ue666\ue667\ue668\ue669\ue66a\ue66b\ue66c\ue66d\ue66e\ue66f\ue670\ue671\ue672\ue673\ue674\ue675\ue676\ue677\ue678\ue679\ue67a\ue67b\ue67c\ue67d\ue67e\ue67f\ue680\ue681\ue682\ue683\ue684\ue685\ue686\ue687\ue688\ue689\ue68a\ue68b\ue68c\ue68d\ue68e\ue68f\ue690\ue691\ue692\ue693\ue694\ue695\ue696\ue697\ue698\ue699\ue69a\ue69b\ue69c\ue69d\ue69e\ue69f\ue6a0\ue6a1\ue6a2\ue6a3\ue6a4\ue6a5\ue6a6\ue6a7\ue6a8\ue6a9\ue6aa\ue6ab\ue6ac\ue6ad\ue6ae\ue6af\ue6b0\ue6b1\ue6b2\ue6b3\ue6b4\ue6b5\ue6b6\ue6b7\ue6b8\ue6b9\ue6ba\ue6bb\ue6bc\ue6bd\ue6be\ue6bf\ue6c0\ue6c1\ue6c2\ue6c3\ue6c4\ue6c5\ue6c6\ue6c7\ue6c8\ue6c9\ue6ca\ue6cb\ue6cc\ue6cd\ue6ce\ue6cf\ue6d0\ue6d1\ue6d2\ue6d3\ue6d4\ue6d5\ue6d6\ue6d7\ue6d8\ue6d9\ue6da\ue6db\ue6dc\ue6dd\ue6de\ue6df\ue6e0\ue6e1\ue6e2\ue6e3\ue6e4\ue6e5\ue6e6\ue6e7\ue6e8\ue6e9\ue6ea\ue6eb\ue6ec\ue6ed\ue6ee\ue6ef\ue6f0\ue6f1\ue6f2\ue6f3\ue6f4\ue6f5\ue6f6\ue6f7\ue6f8\ue6f9\ue6fa\ue6fb\ue6fc\ue6fd\ue6fe\ue6ff\ue700\ue701\ue702\ue703\ue704\ue705\ue706\ue707\ue708\ue709\ue70a\ue70b\ue70c\ue70d\ue70e\ue70f\ue710\ue711\ue712\ue713\ue714\ue715\ue716\ue717\ue718\ue719\ue71a\ue71b\ue71c\ue71d\ue71e\ue71f\ue720\ue721\ue722\ue723\ue724\ue725\ue726\ue727\ue728\ue729\ue72a\ue72b\ue72c\ue72d\ue72e\ue72f\ue730\ue731\ue732\ue733\ue734\ue735\ue736\ue737\ue738\ue739\ue73a\ue73b\ue73c\ue73d\ue73e\ue73f\ue740\ue741\ue742\ue743\ue744\ue745\ue746\ue747\ue748\ue749\ue74a\ue74b\ue74c\ue74d\ue74e\ue74f\ue750\ue751\ue752\ue753\ue754\ue755\ue756\ue757\ue758\ue759\ue75a\ue75b\ue75c\ue75d\ue75e\ue75f\ue760\ue761\ue762\ue763\ue764\ue765\ue766\ue767\ue768\ue769\ue76a\ue76b\ue76c\ue76d\ue76e\ue76f\ue770\ue771\ue772\ue773\ue774\ue775\ue776\ue777\ue778\ue779\ue77a\ue77b\ue77c\ue77d\ue77e\ue77f\ue780\ue781\ue782\ue783\ue784\ue785\ue786\ue787\ue788\ue789\ue78a\ue78b\ue78c\ue78d\ue78e\ue78f\ue790\ue791\ue792\ue793\ue794\ue795\ue796\ue797\ue798\ue799\ue79a\ue79b\ue79c\ue79d\ue79e\ue79f\ue7a0\ue7a1\ue7a2\ue7a3\ue7a4\ue7a5\ue7a6\ue7a7\ue7a8\ue7a9\ue7aa\ue7ab\ue7ac\ue7ad\ue7ae\ue7af\ue7b0\ue7b1\ue7b2\ue7b3\ue7b4\ue7b5\ue7b6\ue7b7\ue7b8\ue7b9\ue7ba\ue7bb\ue7bc\ue7bd\ue7be\ue7bf\ue7c0\ue7c1\ue7c2\ue7c3\ue7c4\ue7c5\ue7c6\ue7c7\ue7c8\ue7c9\ue7ca\ue7cb\ue7cc\ue7cd\ue7ce\ue7cf\ue7d0\ue7d1\ue7d2\ue7d3\ue7d4\ue7d5\ue7d6\ue7d7\ue7d8\ue7d9\ue7da\ue7db\ue7dc\ue7dd\ue7de\ue7df\ue7e0\ue7e1\ue7e2\ue7e3\ue7e4\ue7e5\ue7e6\ue7e7\ue7e8\ue7e9\ue7ea\ue7eb\ue7ec\ue7ed\ue7ee\ue7ef\ue7f0\ue7f1\ue7f2\ue7f3\ue7f4\ue7f5\ue7f6\ue7f7\ue7f8\ue7f9\ue7fa\ue7fb\ue7fc\ue7fd\ue7fe\ue7ff\ue800\ue801\ue802\ue803\ue804\ue805\ue806\ue807\ue808\ue809\ue80a\ue80b\ue80c\ue80d\ue80e\ue80f\ue810\ue811\ue812\ue813\ue814\ue815\ue816\ue817\ue818\ue819\ue81a\ue81b\ue81c\ue81d\ue81e\ue81f\ue820\ue821\ue822\ue823\ue824\ue825\ue826\ue827\ue828\ue829\ue82a\ue82b\ue82c\ue82d\ue82e\ue82f\ue830\ue831\ue832\ue833\ue834\ue835\ue836\ue837\ue838\ue839\ue83a\ue83b\ue83c\ue83d\ue83e\ue83f\ue840\ue841\ue842\ue843\ue844\ue845\ue846\ue847\ue848\ue849\ue84a\ue84b\ue84c\ue84d\ue84e\ue84f\ue850\ue851\ue852\ue853\ue854\ue855\ue856\ue857\ue858\ue859\ue85a\ue85b\ue85c\ue85d\ue85e\ue85f\ue860\ue861\ue862\ue863\ue864\ue865\ue866\ue867\ue868\ue869\ue86a\ue86b\ue86c\ue86d\ue86e\ue86f\ue870\ue871\ue872\ue873\ue874\ue875\ue876\ue877\ue878\ue879\ue87a\ue87b\ue87c\ue87d\ue87e\ue87f\ue880\ue881\ue882\ue883\ue884\ue885\ue886\ue887\ue888\ue889\ue88a\ue88b\ue88c\ue88d\ue88e\ue88f\ue890\ue891\ue892\ue893\ue894\ue895\ue896\ue897\ue898\ue899\ue89a\ue89b\ue89c\ue89d\ue89e\ue89f\ue8a0\ue8a1\ue8a2\ue8a3\ue8a4\ue8a5\ue8a6\ue8a7\ue8a8\ue8a9\ue8aa\ue8ab\ue8ac\ue8ad\ue8ae\ue8af\ue8b0\ue8b1\ue8b2\ue8b3\ue8b4\ue8b5\ue8b6\ue8b7\ue8b8\ue8b9\ue8ba\ue8bb\ue8bc\ue8bd\ue8be\ue8bf\ue8c0\ue8c1\ue8c2\ue8c3\ue8c4\ue8c5\ue8c6\ue8c7\ue8c8\ue8c9\ue8ca\ue8cb\ue8cc\ue8cd\ue8ce\ue8cf\ue8d0\ue8d1\ue8d2\ue8d3\ue8d4\ue8d5\ue8d6\ue8d7\ue8d8\ue8d9\ue8da\ue8db\ue8dc\ue8dd\ue8de\ue8df\ue8e0\ue8e1\ue8e2\ue8e3\ue8e4\ue8e5\ue8e6\ue8e7\ue8e8\ue8e9\ue8ea\ue8eb\ue8ec\ue8ed\ue8ee\ue8ef\ue8f0\ue8f1\ue8f2\ue8f3\ue8f4\ue8f5\ue8f6\ue8f7\ue8f8\ue8f9\ue8fa\ue8fb\ue8fc\ue8fd\ue8fe\ue8ff\ue900\ue901\ue902\ue903\ue904\ue905\ue906\ue907\ue908\ue909\ue90a\ue90b\ue90c\ue90d\ue90e\ue90f\ue910\ue911\ue912\ue913\ue914\ue915\ue916\ue917\ue918\ue919\ue91a\ue91b\ue91c\ue91d\ue91e\ue91f\ue920\ue921\ue922\ue923\ue924\ue925\ue926\ue927\ue928\ue929\ue92a\ue92b\ue92c\ue92d\ue92e\ue92f\ue930\ue931\ue932\ue933\ue934\ue935\ue936\ue937\ue938\ue939\ue93a\ue93b\ue93c\ue93d\ue93e\ue93f\ue940\ue941\ue942\ue943\ue944\ue945\ue946\ue947\ue948\ue949\ue94a\ue94b\ue94c\ue94d\ue94e\ue94f\ue950\ue951\ue952\ue953\ue954\ue955\ue956\ue957\ue958\ue959\ue95a\ue95b\ue95c\ue95d\ue95e\ue95f\ue960\ue961\ue962\ue963\ue964\ue965\ue966\ue967\ue968\ue969\ue96a\ue96b\ue96c\ue96d\ue96e\ue96f\ue970\ue971\ue972\ue973\ue974\ue975\ue976\ue977\ue978\ue979\ue97a\ue97b\ue97c\ue97d\ue97e\ue97f\ue980\ue981\ue982\ue983\ue984\ue985\ue986\ue987\ue988\ue989\ue98a\ue98b\ue98c\ue98d\ue98e\ue98f\ue990\ue991\ue992\ue993\ue994\ue995\ue996\ue997\ue998\ue999\ue99a\ue99b\ue99c\ue99d\ue99e\ue99f\ue9a0\ue9a1\ue9a2\ue9a3\ue9a4\ue9a5\ue9a6\ue9a7\ue9a8\ue9a9\ue9aa\ue9ab\ue9ac\ue9ad\ue9ae\ue9af\ue9b0\ue9b1\ue9b2\ue9b3\ue9b4\ue9b5\ue9b6\ue9b7\ue9b8\ue9b9\ue9ba\ue9bb\ue9bc\ue9bd\ue9be\ue9bf\ue9c0\ue9c1\ue9c2\ue9c3\ue9c4\ue9c5\ue9c6\ue9c7\ue9c8\ue9c9\ue9ca\ue9cb\ue9cc\ue9cd\ue9ce\ue9cf\ue9d0\ue9d1\ue9d2\ue9d3\ue9d4\ue9d5\ue9d6\ue9d7\ue9d8\ue9d9\ue9da\ue9db\ue9dc\ue9dd\ue9de\ue9df\ue9e0\ue9e1\ue9e2\ue9e3\ue9e4\ue9e5\ue9e6\ue9e7\ue9e8\ue9e9\ue9ea\ue9eb\ue9ec\ue9ed\ue9ee\ue9ef\ue9f0\ue9f1\ue9f2\ue9f3\ue9f4\ue9f5\ue9f6\ue9f7\ue9f8\ue9f9\ue9fa\ue9fb\ue9fc\ue9fd\ue9fe\ue9ff\uea00\uea01\uea02\uea03\uea04\uea05\uea06\uea07\uea08\uea09\uea0a\uea0b\uea0c\uea0d\uea0e\uea0f\uea10\uea11\uea12\uea13\uea14\uea15\uea16\uea17\uea18\uea19\uea1a\uea1b\uea1c\uea1d\uea1e\uea1f\uea20\uea21\uea22\uea23\uea24\uea25\uea26\uea27\uea28\uea29\uea2a\uea2b\uea2c\uea2d\uea2e\uea2f\uea30\uea31\uea32\uea33\uea34\uea35\uea36\uea37\uea38\uea39\uea3a\uea3b\uea3c\uea3d\uea3e\uea3f\uea40\uea41\uea42\uea43\uea44\uea45\uea46\uea47\uea48\uea49\uea4a\uea4b\uea4c\uea4d\uea4e\uea4f\uea50\uea51\uea52\uea53\uea54\uea55\uea56\uea57\uea58\uea59\uea5a\uea5b\uea5c\uea5d\uea5e\uea5f\uea60\uea61\uea62\uea63\uea64\uea65\uea66\uea67\uea68\uea69\uea6a\uea6b\uea6c\uea6d\uea6e\uea6f\uea70\uea71\uea72\uea73\uea74\uea75\uea76\uea77\uea78\uea79\uea7a\uea7b\uea7c\uea7d\uea7e\uea7f\uea80\uea81\uea82\uea83\uea84\uea85\uea86\uea87\uea88\uea89\uea8a\uea8b\uea8c\uea8d\uea8e\uea8f\uea90\uea91\uea92\uea93\uea94\uea95\uea96\uea97\uea98\uea99\uea9a\uea9b\uea9c\uea9d\uea9e\uea9f\ueaa0\ueaa1\ueaa2\ueaa3\ueaa4\ueaa5\ueaa6\ueaa7\ueaa8\ueaa9\ueaaa\ueaab\ueaac\ueaad\ueaae\ueaaf\ueab0\ueab1\ueab2\ueab3\ueab4\ueab5\ueab6\ueab7\ueab8\ueab9\ueaba\ueabb\ueabc\ueabd\ueabe\ueabf\ueac0\ueac1\ueac2\ueac3\ueac4\ueac5\ueac6\ueac7\ueac8\ueac9\ueaca\ueacb\ueacc\ueacd\ueace\ueacf\uead0\uead1\uead2\uead3\uead4\uead5\uead6\uead7\uead8\uead9\ueada\ueadb\ueadc\ueadd\ueade\ueadf\ueae0\ueae1\ueae2\ueae3\ueae4\ueae5\ueae6\ueae7\ueae8\ueae9\ueaea\ueaeb\ueaec\ueaed\ueaee\ueaef\ueaf0\ueaf1\ueaf2\ueaf3\ueaf4\ueaf5\ueaf6\ueaf7\ueaf8\ueaf9\ueafa\ueafb\ueafc\ueafd\ueafe\ueaff\ueb00\ueb01\ueb02\ueb03\ueb04\ueb05\ueb06\ueb07\ueb08\ueb09\ueb0a\ueb0b\ueb0c\ueb0d\ueb0e\ueb0f\ueb10\ueb11\ueb12\ueb13\ueb14\ueb15\ueb16\ueb17\ueb18\ueb19\ueb1a\ueb1b\ueb1c\ueb1d\ueb1e\ueb1f\ueb20\ueb21\ueb22\ueb23\ueb24\ueb25\ueb26\ueb27\ueb28\ueb29\ueb2a\ueb2b\ueb2c\ueb2d\ueb2e\ueb2f\ueb30\ueb31\ueb32\ueb33\ueb34\ueb35\ueb36\ueb37\ueb38\ueb39\ueb3a\ueb3b\ueb3c\ueb3d\ueb3e\ueb3f\ueb40\ueb41\ueb42\ueb43\ueb44\ueb45\ueb46\ueb47\ueb48\ueb49\ueb4a\ueb4b\ueb4c\ueb4d\ueb4e\ueb4f\ueb50\ueb51\ueb52\ueb53\ueb54\ueb55\ueb56\ueb57\ueb58\ueb59\ueb5a\ueb5b\ueb5c\ueb5d\ueb5e\ueb5f\ueb60\ueb61\ueb62\ueb63\ueb64\ueb65\ueb66\ueb67\ueb68\ueb69\ueb6a\ueb6b\ueb6c\ueb6d\ueb6e\ueb6f\ueb70\ueb71\ueb72\ueb73\ueb74\ueb75\ueb76\ueb77\ueb78\ueb79\ueb7a\ueb7b\ueb7c\ueb7d\ueb7e\ueb7f\ueb80\ueb81\ueb82\ueb83\ueb84\ueb85\ueb86\ueb87\ueb88\ueb89\ueb8a\ueb8b\ueb8c\ueb8d\ueb8e\ueb8f\ueb90\ueb91\ueb92\ueb93\ueb94\ueb95\ueb96\ueb97\ueb98\ueb99\ueb9a\ueb9b\ueb9c\ueb9d\ueb9e\ueb9f\ueba0\ueba1\ueba2\ueba3\ueba4\ueba5\ueba6\ueba7\ueba8\ueba9\uebaa\uebab\uebac\uebad\uebae\uebaf\uebb0\uebb1\uebb2\uebb3\uebb4\uebb5\uebb6\uebb7\uebb8\uebb9\uebba\uebbb\uebbc\uebbd\uebbe\uebbf\uebc0\uebc1\uebc2\uebc3\uebc4\uebc5\uebc6\uebc7\uebc8\uebc9\uebca\uebcb\uebcc\uebcd\uebce\uebcf\uebd0\uebd1\uebd2\uebd3\uebd4\uebd5\uebd6\uebd7\uebd8\uebd9\uebda\uebdb\uebdc\uebdd\uebde\uebdf\uebe0\uebe1\uebe2\uebe3\uebe4\uebe5\uebe6\uebe7\uebe8\uebe9\uebea\uebeb\uebec\uebed\uebee\uebef\uebf0\uebf1\uebf2\uebf3\uebf4\uebf5\uebf6\uebf7\uebf8\uebf9\uebfa\uebfb\uebfc\uebfd\uebfe\uebff\uec00\uec01\uec02\uec03\uec04\uec05\uec06\uec07\uec08\uec09\uec0a\uec0b\uec0c\uec0d\uec0e\uec0f\uec10\uec11\uec12\uec13\uec14\uec15\uec16\uec17\uec18\uec19\uec1a\uec1b\uec1c\uec1d\uec1e\uec1f\uec20\uec21\uec22\uec23\uec24\uec25\uec26\uec27\uec28\uec29\uec2a\uec2b\uec2c\uec2d\uec2e\uec2f\uec30\uec31\uec32\uec33\uec34\uec35\uec36\uec37\uec38\uec39\uec3a\uec3b\uec3c\uec3d\uec3e\uec3f\uec40\uec41\uec42\uec43\uec44\uec45\uec46\uec47\uec48\uec49\uec4a\uec4b\uec4c\uec4d\uec4e\uec4f\uec50\uec51\uec52\uec53\uec54\uec55\uec56\uec57\uec58\uec59\uec5a\uec5b\uec5c\uec5d\uec5e\uec5f\uec60\uec61\uec62\uec63\uec64\uec65\uec66\uec67\uec68\uec69\uec6a\uec6b\uec6c\uec6d\uec6e\uec6f\uec70\uec71\uec72\uec73\uec74\uec75\uec76\uec77\uec78\uec79\uec7a\uec7b\uec7c\uec7d\uec7e\uec7f\uec80\uec81\uec82\uec83\uec84\uec85\uec86\uec87\uec88\uec89\uec8a\uec8b\uec8c\uec8d\uec8e\uec8f\uec90\uec91\uec92\uec93\uec94\uec95\uec96\uec97\uec98\uec99\uec9a\uec9b\uec9c\uec9d\uec9e\uec9f\ueca0\ueca1\ueca2\ueca3\ueca4\ueca5\ueca6\ueca7\ueca8\ueca9\uecaa\uecab\uecac\uecad\uecae\uecaf\uecb0\uecb1\uecb2\uecb3\uecb4\uecb5\uecb6\uecb7\uecb8\uecb9\uecba\uecbb\uecbc\uecbd\uecbe\uecbf\uecc0\uecc1\uecc2\uecc3\uecc4\uecc5\uecc6\uecc7\uecc8\uecc9\uecca\ueccb\ueccc\ueccd\uecce\ueccf\uecd0\uecd1\uecd2\uecd3\uecd4\uecd5\uecd6\uecd7\uecd8\uecd9\uecda\uecdb\uecdc\uecdd\uecde\uecdf\uece0\uece1\uece2\uece3\uece4\uece5\uece6\uece7\uece8\uece9\uecea\ueceb\uecec\ueced\uecee\uecef\uecf0\uecf1\uecf2\uecf3\uecf4\uecf5\uecf6\uecf7\uecf8\uecf9\uecfa\uecfb\uecfc\uecfd\uecfe\uecff\ued00\ued01\ued02\ued03\ued04\ued05\ued06\ued07\ued08\ued09\ued0a\ued0b\ued0c\ued0d\ued0e\ued0f\ued10\ued11\ued12\ued13\ued14\ued15\ued16\ued17\ued18\ued19\ued1a\ued1b\ued1c\ued1d\ued1e\ued1f\ued20\ued21\ued22\ued23\ued24\ued25\ued26\ued27\ued28\ued29\ued2a\ued2b\ued2c\ued2d\ued2e\ued2f\ued30\ued31\ued32\ued33\ued34\ued35\ued36\ued37\ued38\ued39\ued3a\ued3b\ued3c\ued3d\ued3e\ued3f\ued40\ued41\ued42\ued43\ued44\ued45\ued46\ued47\ued48\ued49\ued4a\ued4b\ued4c\ued4d\ued4e\ued4f\ued50\ued51\ued52\ued53\ued54\ued55\ued56\ued57\ued58\ued59\ued5a\ued5b\ued5c\ued5d\ued5e\ued5f\ued60\ued61\ued62\ued63\ued64\ued65\ued66\ued67\ued68\ued69\ued6a\ued6b\ued6c\ued6d\ued6e\ued6f\ued70\ued71\ued72\ued73\ued74\ued75\ued76\ued77\ued78\ued79\ued7a\ued7b\ued7c\ued7d\ued7e\ued7f\ued80\ued81\ued82\ued83\ued84\ued85\ued86\ued87\ued88\ued89\ued8a\ued8b\ued8c\ued8d\ued8e\ued8f\ued90\ued91\ued92\ued93\ued94\ued95\ued96\ued97\ued98\ued99\ued9a\ued9b\ued9c\ued9d\ued9e\ued9f\ueda0\ueda1\ueda2\ueda3\ueda4\ueda5\ueda6\ueda7\ueda8\ueda9\uedaa\uedab\uedac\uedad\uedae\uedaf\uedb0\uedb1\uedb2\uedb3\uedb4\uedb5\uedb6\uedb7\uedb8\uedb9\uedba\uedbb\uedbc\uedbd\uedbe\uedbf\uedc0\uedc1\uedc2\uedc3\uedc4\uedc5\uedc6\uedc7\uedc8\uedc9\uedca\uedcb\uedcc\uedcd\uedce\uedcf\uedd0\uedd1\uedd2\uedd3\uedd4\uedd5\uedd6\uedd7\uedd8\uedd9\uedda\ueddb\ueddc\ueddd\uedde\ueddf\uede0\uede1\uede2\uede3\uede4\uede5\uede6\uede7\uede8\uede9\uedea\uedeb\uedec\ueded\uedee\uedef\uedf0\uedf1\uedf2\uedf3\uedf4\uedf5\uedf6\uedf7\uedf8\uedf9\uedfa\uedfb\uedfc\uedfd\uedfe\uedff\uee00\uee01\uee02\uee03\uee04\uee05\uee06\uee07\uee08\uee09\uee0a\uee0b\uee0c\uee0d\uee0e\uee0f\uee10\uee11\uee12\uee13\uee14\uee15\uee16\uee17\uee18\uee19\uee1a\uee1b\uee1c\uee1d\uee1e\uee1f\uee20\uee21\uee22\uee23\uee24\uee25\uee26\uee27\uee28\uee29\uee2a\uee2b\uee2c\uee2d\uee2e\uee2f\uee30\uee31\uee32\uee33\uee34\uee35\uee36\uee37\uee38\uee39\uee3a\uee3b\uee3c\uee3d\uee3e\uee3f\uee40\uee41\uee42\uee43\uee44\uee45\uee46\uee47\uee48\uee49\uee4a\uee4b\uee4c\uee4d\uee4e\uee4f\uee50\uee51\uee52\uee53\uee54\uee55\uee56\uee57\uee58\uee59\uee5a\uee5b\uee5c\uee5d\uee5e\uee5f\uee60\uee61\uee62\uee63\uee64\uee65\uee66\uee67\uee68\uee69\uee6a\uee6b\uee6c\uee6d\uee6e\uee6f\uee70\uee71\uee72\uee73\uee74\uee75\uee76\uee77\uee78\uee79\uee7a\uee7b\uee7c\uee7d\uee7e\uee7f\uee80\uee81\uee82\uee83\uee84\uee85\uee86\uee87\uee88\uee89\uee8a\uee8b\uee8c\uee8d\uee8e\uee8f\uee90\uee91\uee92\uee93\uee94\uee95\uee96\uee97\uee98\uee99\uee9a\uee9b\uee9c\uee9d\uee9e\uee9f\ueea0\ueea1\ueea2\ueea3\ueea4\ueea5\ueea6\ueea7\ueea8\ueea9\ueeaa\ueeab\ueeac\ueead\ueeae\ueeaf\ueeb0\ueeb1\ueeb2\ueeb3\ueeb4\ueeb5\ueeb6\ueeb7\ueeb8\ueeb9\ueeba\ueebb\ueebc\ueebd\ueebe\ueebf\ueec0\ueec1\ueec2\ueec3\ueec4\ueec5\ueec6\ueec7\ueec8\ueec9\ueeca\ueecb\ueecc\ueecd\ueece\ueecf\ueed0\ueed1\ueed2\ueed3\ueed4\ueed5\ueed6\ueed7\ueed8\ueed9\ueeda\ueedb\ueedc\ueedd\ueede\ueedf\ueee0\ueee1\ueee2\ueee3\ueee4\ueee5\ueee6\ueee7\ueee8\ueee9\ueeea\ueeeb\ueeec\ueeed\ueeee\ueeef\ueef0\ueef1\ueef2\ueef3\ueef4\ueef5\ueef6\ueef7\ueef8\ueef9\ueefa\ueefb\ueefc\ueefd\ueefe\ueeff\uef00\uef01\uef02\uef03\uef04\uef05\uef06\uef07\uef08\uef09\uef0a\uef0b\uef0c\uef0d\uef0e\uef0f\uef10\uef11\uef12\uef13\uef14\uef15\uef16\uef17\uef18\uef19\uef1a\uef1b\uef1c\uef1d\uef1e\uef1f\uef20\uef21\uef22\uef23\uef24\uef25\uef26\uef27\uef28\uef29\uef2a\uef2b\uef2c\uef2d\uef2e\uef2f\uef30\uef31\uef32\uef33\uef34\uef35\uef36\uef37\uef38\uef39\uef3a\uef3b\uef3c\uef3d\uef3e\uef3f\uef40\uef41\uef42\uef43\uef44\uef45\uef46\uef47\uef48\uef49\uef4a\uef4b\uef4c\uef4d\uef4e\uef4f\uef50\uef51\uef52\uef53\uef54\uef55\uef56\uef57\uef58\uef59\uef5a\uef5b\uef5c\uef5d\uef5e\uef5f\uef60\uef61\uef62\uef63\uef64\uef65\uef66\uef67\uef68\uef69\uef6a\uef6b\uef6c\uef6d\uef6e\uef6f\uef70\uef71\uef72\uef73\uef74\uef75\uef76\uef77\uef78\uef79\uef7a\uef7b\uef7c\uef7d\uef7e\uef7f\uef80\uef81\uef82\uef83\uef84\uef85\uef86\uef87\uef88\uef89\uef8a\uef8b\uef8c\uef8d\uef8e\uef8f\uef90\uef91\uef92\uef93\uef94\uef95\uef96\uef97\uef98\uef99\uef9a\uef9b\uef9c\uef9d\uef9e\uef9f\uefa0\uefa1\uefa2\uefa3\uefa4\uefa5\uefa6\uefa7\uefa8\uefa9\uefaa\uefab\uefac\uefad\uefae\uefaf\uefb0\uefb1\uefb2\uefb3\uefb4\uefb5\uefb6\uefb7\uefb8\uefb9\uefba\uefbb\uefbc\uefbd\uefbe\uefbf\uefc0\uefc1\uefc2\uefc3\uefc4\uefc5\uefc6\uefc7\uefc8\uefc9\uefca\uefcb\uefcc\uefcd\uefce\uefcf\uefd0\uefd1\uefd2\uefd3\uefd4\uefd5\uefd6\uefd7\uefd8\uefd9\uefda\uefdb\uefdc\uefdd\uefde\uefdf\uefe0\uefe1\uefe2\uefe3\uefe4\uefe5\uefe6\uefe7\uefe8\uefe9\uefea\uefeb\uefec\uefed\uefee\uefef\ueff0\ueff1\ueff2\ueff3\ueff4\ueff5\ueff6\ueff7\ueff8\ueff9\ueffa\ueffb\ueffc\ueffd\ueffe\uefff\uf000\uf001\uf002\uf003\uf004\uf005\uf006\uf007\uf008\uf009\uf00a\uf00b\uf00c\uf00d\uf00e\uf00f\uf010\uf011\uf012\uf013\uf014\uf015\uf016\uf017\uf018\uf019\uf01a\uf01b\uf01c\uf01d\uf01e\uf01f\uf020\uf021\uf022\uf023\uf024\uf025\uf026\uf027\uf028\uf029\uf02a\uf02b\uf02c\uf02d\uf02e\uf02f\uf030\uf031\uf032\uf033\uf034\uf035\uf036\uf037\uf038\uf039\uf03a\uf03b\uf03c\uf03d\uf03e\uf03f\uf040\uf041\uf042\uf043\uf044\uf045\uf046\uf047\uf048\uf049\uf04a\uf04b\uf04c\uf04d\uf04e\uf04f\uf050\uf051\uf052\uf053\uf054\uf055\uf056\uf057\uf058\uf059\uf05a\uf05b\uf05c\uf05d\uf05e\uf05f\uf060\uf061\uf062\uf063\uf064\uf065\uf066\uf067\uf068\uf069\uf06a\uf06b\uf06c\uf06d\uf06e\uf06f\uf070\uf071\uf072\uf073\uf074\uf075\uf076\uf077\uf078\uf079\uf07a\uf07b\uf07c\uf07d\uf07e\uf07f\uf080\uf081\uf082\uf083\uf084\uf085\uf086\uf087\uf088\uf089\uf08a\uf08b\uf08c\uf08d\uf08e\uf08f\uf090\uf091\uf092\uf093\uf094\uf095\uf096\uf097\uf098\uf099\uf09a\uf09b\uf09c\uf09d\uf09e\uf09f\uf0a0\uf0a1\uf0a2\uf0a3\uf0a4\uf0a5\uf0a6\uf0a7\uf0a8\uf0a9\uf0aa\uf0ab\uf0ac\uf0ad\uf0ae\uf0af\uf0b0\uf0b1\uf0b2\uf0b3\uf0b4\uf0b5\uf0b6\uf0b7\uf0b8\uf0b9\uf0ba\uf0bb\uf0bc\uf0bd\uf0be\uf0bf\uf0c0\uf0c1\uf0c2\uf0c3\uf0c4\uf0c5\uf0c6\uf0c7\uf0c8\uf0c9\uf0ca\uf0cb\uf0cc\uf0cd\uf0ce\uf0cf\uf0d0\uf0d1\uf0d2\uf0d3\uf0d4\uf0d5\uf0d6\uf0d7\uf0d8\uf0d9\uf0da\uf0db\uf0dc\uf0dd\uf0de\uf0df\uf0e0\uf0e1\uf0e2\uf0e3\uf0e4\uf0e5\uf0e6\uf0e7\uf0e8\uf0e9\uf0ea\uf0eb\uf0ec\uf0ed\uf0ee\uf0ef\uf0f0\uf0f1\uf0f2\uf0f3\uf0f4\uf0f5\uf0f6\uf0f7\uf0f8\uf0f9\uf0fa\uf0fb\uf0fc\uf0fd\uf0fe\uf0ff\uf100\uf101\uf102\uf103\uf104\uf105\uf106\uf107\uf108\uf109\uf10a\uf10b\uf10c\uf10d\uf10e\uf10f\uf110\uf111\uf112\uf113\uf114\uf115\uf116\uf117\uf118\uf119\uf11a\uf11b\uf11c\uf11d\uf11e\uf11f\uf120\uf121\uf122\uf123\uf124\uf125\uf126\uf127\uf128\uf129\uf12a\uf12b\uf12c\uf12d\uf12e\uf12f\uf130\uf131\uf132\uf133\uf134\uf135\uf136\uf137\uf138\uf139\uf13a\uf13b\uf13c\uf13d\uf13e\uf13f\uf140\uf141\uf142\uf143\uf144\uf145\uf146\uf147\uf148\uf149\uf14a\uf14b\uf14c\uf14d\uf14e\uf14f\uf150\uf151\uf152\uf153\uf154\uf155\uf156\uf157\uf158\uf159\uf15a\uf15b\uf15c\uf15d\uf15e\uf15f\uf160\uf161\uf162\uf163\uf164\uf165\uf166\uf167\uf168\uf169\uf16a\uf16b\uf16c\uf16d\uf16e\uf16f\uf170\uf171\uf172\uf173\uf174\uf175\uf176\uf177\uf178\uf179\uf17a\uf17b\uf17c\uf17d\uf17e\uf17f\uf180\uf181\uf182\uf183\uf184\uf185\uf186\uf187\uf188\uf189\uf18a\uf18b\uf18c\uf18d\uf18e\uf18f\uf190\uf191\uf192\uf193\uf194\uf195\uf196\uf197\uf198\uf199\uf19a\uf19b\uf19c\uf19d\uf19e\uf19f\uf1a0\uf1a1\uf1a2\uf1a3\uf1a4\uf1a5\uf1a6\uf1a7\uf1a8\uf1a9\uf1aa\uf1ab\uf1ac\uf1ad\uf1ae\uf1af\uf1b0\uf1b1\uf1b2\uf1b3\uf1b4\uf1b5\uf1b6\uf1b7\uf1b8\uf1b9\uf1ba\uf1bb\uf1bc\uf1bd\uf1be\uf1bf\uf1c0\uf1c1\uf1c2\uf1c3\uf1c4\uf1c5\uf1c6\uf1c7\uf1c8\uf1c9\uf1ca\uf1cb\uf1cc\uf1cd\uf1ce\uf1cf\uf1d0\uf1d1\uf1d2\uf1d3\uf1d4\uf1d5\uf1d6\uf1d7\uf1d8\uf1d9\uf1da\uf1db\uf1dc\uf1dd\uf1de\uf1df\uf1e0\uf1e1\uf1e2\uf1e3\uf1e4\uf1e5\uf1e6\uf1e7\uf1e8\uf1e9\uf1ea\uf1eb\uf1ec\uf1ed\uf1ee\uf1ef\uf1f0\uf1f1\uf1f2\uf1f3\uf1f4\uf1f5\uf1f6\uf1f7\uf1f8\uf1f9\uf1fa\uf1fb\uf1fc\uf1fd\uf1fe\uf1ff\uf200\uf201\uf202\uf203\uf204\uf205\uf206\uf207\uf208\uf209\uf20a\uf20b\uf20c\uf20d\uf20e\uf20f\uf210\uf211\uf212\uf213\uf214\uf215\uf216\uf217\uf218\uf219\uf21a\uf21b\uf21c\uf21d\uf21e\uf21f\uf220\uf221\uf222\uf223\uf224\uf225\uf226\uf227\uf228\uf229\uf22a\uf22b\uf22c\uf22d\uf22e\uf22f\uf230\uf231\uf232\uf233\uf234\uf235\uf236\uf237\uf238\uf239\uf23a\uf23b\uf23c\uf23d\uf23e\uf23f\uf240\uf241\uf242\uf243\uf244\uf245\uf246\uf247\uf248\uf249\uf24a\uf24b\uf24c\uf24d\uf24e\uf24f\uf250\uf251\uf252\uf253\uf254\uf255\uf256\uf257\uf258\uf259\uf25a\uf25b\uf25c\uf25d\uf25e\uf25f\uf260\uf261\uf262\uf263\uf264\uf265\uf266\uf267\uf268\uf269\uf26a\uf26b\uf26c\uf26d\uf26e\uf26f\uf270\uf271\uf272\uf273\uf274\uf275\uf276\uf277\uf278\uf279\uf27a\uf27b\uf27c\uf27d\uf27e\uf27f\uf280\uf281\uf282\uf283\uf284\uf285\uf286\uf287\uf288\uf289\uf28a\uf28b\uf28c\uf28d\uf28e\uf28f\uf290\uf291\uf292\uf293\uf294\uf295\uf296\uf297\uf298\uf299\uf29a\uf29b\uf29c\uf29d\uf29e\uf29f\uf2a0\uf2a1\uf2a2\uf2a3\uf2a4\uf2a5\uf2a6\uf2a7\uf2a8\uf2a9\uf2aa\uf2ab\uf2ac\uf2ad\uf2ae\uf2af\uf2b0\uf2b1\uf2b2\uf2b3\uf2b4\uf2b5\uf2b6\uf2b7\uf2b8\uf2b9\uf2ba\uf2bb\uf2bc\uf2bd\uf2be\uf2bf\uf2c0\uf2c1\uf2c2\uf2c3\uf2c4\uf2c5\uf2c6\uf2c7\uf2c8\uf2c9\uf2ca\uf2cb\uf2cc\uf2cd\uf2ce\uf2cf\uf2d0\uf2d1\uf2d2\uf2d3\uf2d4\uf2d5\uf2d6\uf2d7\uf2d8\uf2d9\uf2da\uf2db\uf2dc\uf2dd\uf2de\uf2df\uf2e0\uf2e1\uf2e2\uf2e3\uf2e4\uf2e5\uf2e6\uf2e7\uf2e8\uf2e9\uf2ea\uf2eb\uf2ec\uf2ed\uf2ee\uf2ef\uf2f0\uf2f1\uf2f2\uf2f3\uf2f4\uf2f5\uf2f6\uf2f7\uf2f8\uf2f9\uf2fa\uf2fb\uf2fc\uf2fd\uf2fe\uf2ff\uf300\uf301\uf302\uf303\uf304\uf305\uf306\uf307\uf308\uf309\uf30a\uf30b\uf30c\uf30d\uf30e\uf30f\uf310\uf311\uf312\uf313\uf314\uf315\uf316\uf317\uf318\uf319\uf31a\uf31b\uf31c\uf31d\uf31e\uf31f\uf320\uf321\uf322\uf323\uf324\uf325\uf326\uf327\uf328\uf329\uf32a\uf32b\uf32c\uf32d\uf32e\uf32f\uf330\uf331\uf332\uf333\uf334\uf335\uf336\uf337\uf338\uf339\uf33a\uf33b\uf33c\uf33d\uf33e\uf33f\uf340\uf341\uf342\uf343\uf344\uf345\uf346\uf347\uf348\uf349\uf34a\uf34b\uf34c\uf34d\uf34e\uf34f\uf350\uf351\uf352\uf353\uf354\uf355\uf356\uf357\uf358\uf359\uf35a\uf35b\uf35c\uf35d\uf35e\uf35f\uf360\uf361\uf362\uf363\uf364\uf365\uf366\uf367\uf368\uf369\uf36a\uf36b\uf36c\uf36d\uf36e\uf36f\uf370\uf371\uf372\uf373\uf374\uf375\uf376\uf377\uf378\uf379\uf37a\uf37b\uf37c\uf37d\uf37e\uf37f\uf380\uf381\uf382\uf383\uf384\uf385\uf386\uf387\uf388\uf389\uf38a\uf38b\uf38c\uf38d\uf38e\uf38f\uf390\uf391\uf392\uf393\uf394\uf395\uf396\uf397\uf398\uf399\uf39a\uf39b\uf39c\uf39d\uf39e\uf39f\uf3a0\uf3a1\uf3a2\uf3a3\uf3a4\uf3a5\uf3a6\uf3a7\uf3a8\uf3a9\uf3aa\uf3ab\uf3ac\uf3ad\uf3ae\uf3af\uf3b0\uf3b1\uf3b2\uf3b3\uf3b4\uf3b5\uf3b6\uf3b7\uf3b8\uf3b9\uf3ba\uf3bb\uf3bc\uf3bd\uf3be\uf3bf\uf3c0\uf3c1\uf3c2\uf3c3\uf3c4\uf3c5\uf3c6\uf3c7\uf3c8\uf3c9\uf3ca\uf3cb\uf3cc\uf3cd\uf3ce\uf3cf\uf3d0\uf3d1\uf3d2\uf3d3\uf3d4\uf3d5\uf3d6\uf3d7\uf3d8\uf3d9\uf3da\uf3db\uf3dc\uf3dd\uf3de\uf3df\uf3e0\uf3e1\uf3e2\uf3e3\uf3e4\uf3e5\uf3e6\uf3e7\uf3e8\uf3e9\uf3ea\uf3eb\uf3ec\uf3ed\uf3ee\uf3ef\uf3f0\uf3f1\uf3f2\uf3f3\uf3f4\uf3f5\uf3f6\uf3f7\uf3f8\uf3f9\uf3fa\uf3fb\uf3fc\uf3fd\uf3fe\uf3ff\uf400\uf401\uf402\uf403\uf404\uf405\uf406\uf407\uf408\uf409\uf40a\uf40b\uf40c\uf40d\uf40e\uf40f\uf410\uf411\uf412\uf413\uf414\uf415\uf416\uf417\uf418\uf419\uf41a\uf41b\uf41c\uf41d\uf41e\uf41f\uf420\uf421\uf422\uf423\uf424\uf425\uf426\uf427\uf428\uf429\uf42a\uf42b\uf42c\uf42d\uf42e\uf42f\uf430\uf431\uf432\uf433\uf434\uf435\uf436\uf437\uf438\uf439\uf43a\uf43b\uf43c\uf43d\uf43e\uf43f\uf440\uf441\uf442\uf443\uf444\uf445\uf446\uf447\uf448\uf449\uf44a\uf44b\uf44c\uf44d\uf44e\uf44f\uf450\uf451\uf452\uf453\uf454\uf455\uf456\uf457\uf458\uf459\uf45a\uf45b\uf45c\uf45d\uf45e\uf45f\uf460\uf461\uf462\uf463\uf464\uf465\uf466\uf467\uf468\uf469\uf46a\uf46b\uf46c\uf46d\uf46e\uf46f\uf470\uf471\uf472\uf473\uf474\uf475\uf476\uf477\uf478\uf479\uf47a\uf47b\uf47c\uf47d\uf47e\uf47f\uf480\uf481\uf482\uf483\uf484\uf485\uf486\uf487\uf488\uf489\uf48a\uf48b\uf48c\uf48d\uf48e\uf48f\uf490\uf491\uf492\uf493\uf494\uf495\uf496\uf497\uf498\uf499\uf49a\uf49b\uf49c\uf49d\uf49e\uf49f\uf4a0\uf4a1\uf4a2\uf4a3\uf4a4\uf4a5\uf4a6\uf4a7\uf4a8\uf4a9\uf4aa\uf4ab\uf4ac\uf4ad\uf4ae\uf4af\uf4b0\uf4b1\uf4b2\uf4b3\uf4b4\uf4b5\uf4b6\uf4b7\uf4b8\uf4b9\uf4ba\uf4bb\uf4bc\uf4bd\uf4be\uf4bf\uf4c0\uf4c1\uf4c2\uf4c3\uf4c4\uf4c5\uf4c6\uf4c7\uf4c8\uf4c9\uf4ca\uf4cb\uf4cc\uf4cd\uf4ce\uf4cf\uf4d0\uf4d1\uf4d2\uf4d3\uf4d4\uf4d5\uf4d6\uf4d7\uf4d8\uf4d9\uf4da\uf4db\uf4dc\uf4dd\uf4de\uf4df\uf4e0\uf4e1\uf4e2\uf4e3\uf4e4\uf4e5\uf4e6\uf4e7\uf4e8\uf4e9\uf4ea\uf4eb\uf4ec\uf4ed\uf4ee\uf4ef\uf4f0\uf4f1\uf4f2\uf4f3\uf4f4\uf4f5\uf4f6\uf4f7\uf4f8\uf4f9\uf4fa\uf4fb\uf4fc\uf4fd\uf4fe\uf4ff\uf500\uf501\uf502\uf503\uf504\uf505\uf506\uf507\uf508\uf509\uf50a\uf50b\uf50c\uf50d\uf50e\uf50f\uf510\uf511\uf512\uf513\uf514\uf515\uf516\uf517\uf518\uf519\uf51a\uf51b\uf51c\uf51d\uf51e\uf51f\uf520\uf521\uf522\uf523\uf524\uf525\uf526\uf527\uf528\uf529\uf52a\uf52b\uf52c\uf52d\uf52e\uf52f\uf530\uf531\uf532\uf533\uf534\uf535\uf536\uf537\uf538\uf539\uf53a\uf53b\uf53c\uf53d\uf53e\uf53f\uf540\uf541\uf542\uf543\uf544\uf545\uf546\uf547\uf548\uf549\uf54a\uf54b\uf54c\uf54d\uf54e\uf54f\uf550\uf551\uf552\uf553\uf554\uf555\uf556\uf557\uf558\uf559\uf55a\uf55b\uf55c\uf55d\uf55e\uf55f\uf560\uf561\uf562\uf563\uf564\uf565\uf566\uf567\uf568\uf569\uf56a\uf56b\uf56c\uf56d\uf56e\uf56f\uf570\uf571\uf572\uf573\uf574\uf575\uf576\uf577\uf578\uf579\uf57a\uf57b\uf57c\uf57d\uf57e\uf57f\uf580\uf581\uf582\uf583\uf584\uf585\uf586\uf587\uf588\uf589\uf58a\uf58b\uf58c\uf58d\uf58e\uf58f\uf590\uf591\uf592\uf593\uf594\uf595\uf596\uf597\uf598\uf599\uf59a\uf59b\uf59c\uf59d\uf59e\uf59f\uf5a0\uf5a1\uf5a2\uf5a3\uf5a4\uf5a5\uf5a6\uf5a7\uf5a8\uf5a9\uf5aa\uf5ab\uf5ac\uf5ad\uf5ae\uf5af\uf5b0\uf5b1\uf5b2\uf5b3\uf5b4\uf5b5\uf5b6\uf5b7\uf5b8\uf5b9\uf5ba\uf5bb\uf5bc\uf5bd\uf5be\uf5bf\uf5c0\uf5c1\uf5c2\uf5c3\uf5c4\uf5c5\uf5c6\uf5c7\uf5c8\uf5c9\uf5ca\uf5cb\uf5cc\uf5cd\uf5ce\uf5cf\uf5d0\uf5d1\uf5d2\uf5d3\uf5d4\uf5d5\uf5d6\uf5d7\uf5d8\uf5d9\uf5da\uf5db\uf5dc\uf5dd\uf5de\uf5df\uf5e0\uf5e1\uf5e2\uf5e3\uf5e4\uf5e5\uf5e6\uf5e7\uf5e8\uf5e9\uf5ea\uf5eb\uf5ec\uf5ed\uf5ee\uf5ef\uf5f0\uf5f1\uf5f2\uf5f3\uf5f4\uf5f5\uf5f6\uf5f7\uf5f8\uf5f9\uf5fa\uf5fb\uf5fc\uf5fd\uf5fe\uf5ff\uf600\uf601\uf602\uf603\uf604\uf605\uf606\uf607\uf608\uf609\uf60a\uf60b\uf60c\uf60d\uf60e\uf60f\uf610\uf611\uf612\uf613\uf614\uf615\uf616\uf617\uf618\uf619\uf61a\uf61b\uf61c\uf61d\uf61e\uf61f\uf620\uf621\uf622\uf623\uf624\uf625\uf626\uf627\uf628\uf629\uf62a\uf62b\uf62c\uf62d\uf62e\uf62f\uf630\uf631\uf632\uf633\uf634\uf635\uf636\uf637\uf638\uf639\uf63a\uf63b\uf63c\uf63d\uf63e\uf63f\uf640\uf641\uf642\uf643\uf644\uf645\uf646\uf647\uf648\uf649\uf64a\uf64b\uf64c\uf64d\uf64e\uf64f\uf650\uf651\uf652\uf653\uf654\uf655\uf656\uf657\uf658\uf659\uf65a\uf65b\uf65c\uf65d\uf65e\uf65f\uf660\uf661\uf662\uf663\uf664\uf665\uf666\uf667\uf668\uf669\uf66a\uf66b\uf66c\uf66d\uf66e\uf66f\uf670\uf671\uf672\uf673\uf674\uf675\uf676\uf677\uf678\uf679\uf67a\uf67b\uf67c\uf67d\uf67e\uf67f\uf680\uf681\uf682\uf683\uf684\uf685\uf686\uf687\uf688\uf689\uf68a\uf68b\uf68c\uf68d\uf68e\uf68f\uf690\uf691\uf692\uf693\uf694\uf695\uf696\uf697\uf698\uf699\uf69a\uf69b\uf69c\uf69d\uf69e\uf69f\uf6a0\uf6a1\uf6a2\uf6a3\uf6a4\uf6a5\uf6a6\uf6a7\uf6a8\uf6a9\uf6aa\uf6ab\uf6ac\uf6ad\uf6ae\uf6af\uf6b0\uf6b1\uf6b2\uf6b3\uf6b4\uf6b5\uf6b6\uf6b7\uf6b8\uf6b9\uf6ba\uf6bb\uf6bc\uf6bd\uf6be\uf6bf\uf6c0\uf6c1\uf6c2\uf6c3\uf6c4\uf6c5\uf6c6\uf6c7\uf6c8\uf6c9\uf6ca\uf6cb\uf6cc\uf6cd\uf6ce\uf6cf\uf6d0\uf6d1\uf6d2\uf6d3\uf6d4\uf6d5\uf6d6\uf6d7\uf6d8\uf6d9\uf6da\uf6db\uf6dc\uf6dd\uf6de\uf6df\uf6e0\uf6e1\uf6e2\uf6e3\uf6e4\uf6e5\uf6e6\uf6e7\uf6e8\uf6e9\uf6ea\uf6eb\uf6ec\uf6ed\uf6ee\uf6ef\uf6f0\uf6f1\uf6f2\uf6f3\uf6f4\uf6f5\uf6f6\uf6f7\uf6f8\uf6f9\uf6fa\uf6fb\uf6fc\uf6fd\uf6fe\uf6ff\uf700\uf701\uf702\uf703\uf704\uf705\uf706\uf707\uf708\uf709\uf70a\uf70b\uf70c\uf70d\uf70e\uf70f\uf710\uf711\uf712\uf713\uf714\uf715\uf716\uf717\uf718\uf719\uf71a\uf71b\uf71c\uf71d\uf71e\uf71f\uf720\uf721\uf722\uf723\uf724\uf725\uf726\uf727\uf728\uf729\uf72a\uf72b\uf72c\uf72d\uf72e\uf72f\uf730\uf731\uf732\uf733\uf734\uf735\uf736\uf737\uf738\uf739\uf73a\uf73b\uf73c\uf73d\uf73e\uf73f\uf740\uf741\uf742\uf743\uf744\uf745\uf746\uf747\uf748\uf749\uf74a\uf74b\uf74c\uf74d\uf74e\uf74f\uf750\uf751\uf752\uf753\uf754\uf755\uf756\uf757\uf758\uf759\uf75a\uf75b\uf75c\uf75d\uf75e\uf75f\uf760\uf761\uf762\uf763\uf764\uf765\uf766\uf767\uf768\uf769\uf76a\uf76b\uf76c\uf76d\uf76e\uf76f\uf770\uf771\uf772\uf773\uf774\uf775\uf776\uf777\uf778\uf779\uf77a\uf77b\uf77c\uf77d\uf77e\uf77f\uf780\uf781\uf782\uf783\uf784\uf785\uf786\uf787\uf788\uf789\uf78a\uf78b\uf78c\uf78d\uf78e\uf78f\uf790\uf791\uf792\uf793\uf794\uf795\uf796\uf797\uf798\uf799\uf79a\uf79b\uf79c\uf79d\uf79e\uf79f\uf7a0\uf7a1\uf7a2\uf7a3\uf7a4\uf7a5\uf7a6\uf7a7\uf7a8\uf7a9\uf7aa\uf7ab\uf7ac\uf7ad\uf7ae\uf7af\uf7b0\uf7b1\uf7b2\uf7b3\uf7b4\uf7b5\uf7b6\uf7b7\uf7b8\uf7b9\uf7ba\uf7bb\uf7bc\uf7bd\uf7be\uf7bf\uf7c0\uf7c1\uf7c2\uf7c3\uf7c4\uf7c5\uf7c6\uf7c7\uf7c8\uf7c9\uf7ca\uf7cb\uf7cc\uf7cd\uf7ce\uf7cf\uf7d0\uf7d1\uf7d2\uf7d3\uf7d4\uf7d5\uf7d6\uf7d7\uf7d8\uf7d9\uf7da\uf7db\uf7dc\uf7dd\uf7de\uf7df\uf7e0\uf7e1\uf7e2\uf7e3\uf7e4\uf7e5\uf7e6\uf7e7\uf7e8\uf7e9\uf7ea\uf7eb\uf7ec\uf7ed\uf7ee\uf7ef\uf7f0\uf7f1\uf7f2\uf7f3\uf7f4\uf7f5\uf7f6\uf7f7\uf7f8\uf7f9\uf7fa\uf7fb\uf7fc\uf7fd\uf7fe\uf7ff\uf800\uf801\uf802\uf803\uf804\uf805\uf806\uf807\uf808\uf809\uf80a\uf80b\uf80c\uf80d\uf80e\uf80f\uf810\uf811\uf812\uf813\uf814\uf815\uf816\uf817\uf818\uf819\uf81a\uf81b\uf81c\uf81d\uf81e\uf81f\uf820\uf821\uf822\uf823\uf824\uf825\uf826\uf827\uf828\uf829\uf82a\uf82b\uf82c\uf82d\uf82e\uf82f\uf830\uf831\uf832\uf833\uf834\uf835\uf836\uf837\uf838\uf839\uf83a\uf83b\uf83c\uf83d\uf83e\uf83f\uf840\uf841\uf842\uf843\uf844\uf845\uf846\uf847\uf848\uf849\uf84a\uf84b\uf84c\uf84d\uf84e\uf84f\uf850\uf851\uf852\uf853\uf854\uf855\uf856\uf857\uf858\uf859\uf85a\uf85b\uf85c\uf85d\uf85e\uf85f\uf860\uf861\uf862\uf863\uf864\uf865\uf866\uf867\uf868\uf869\uf86a\uf86b\uf86c\uf86d\uf86e\uf86f\uf870\uf871\uf872\uf873\uf874\uf875\uf876\uf877\uf878\uf879\uf87a\uf87b\uf87c\uf87d\uf87e\uf87f\uf880\uf881\uf882\uf883\uf884\uf885\uf886\uf887\uf888\uf889\uf88a\uf88b\uf88c\uf88d\uf88e\uf88f\uf890\uf891\uf892\uf893\uf894\uf895\uf896\uf897\uf898\uf899\uf89a\uf89b\uf89c\uf89d\uf89e\uf89f\uf8a0\uf8a1\uf8a2\uf8a3\uf8a4\uf8a5\uf8a6\uf8a7\uf8a8\uf8a9\uf8aa\uf8ab\uf8ac\uf8ad\uf8ae\uf8af\uf8b0\uf8b1\uf8b2\uf8b3\uf8b4\uf8b5\uf8b6\uf8b7\uf8b8\uf8b9\uf8ba\uf8bb\uf8bc\uf8bd\uf8be\uf8bf\uf8c0\uf8c1\uf8c2\uf8c3\uf8c4\uf8c5\uf8c6\uf8c7\uf8c8\uf8c9\uf8ca\uf8cb\uf8cc\uf8cd\uf8ce\uf8cf\uf8d0\uf8d1\uf8d2\uf8d3\uf8d4\uf8d5\uf8d6\uf8d7\uf8d8\uf8d9\uf8da\uf8db\uf8dc\uf8dd\uf8de\uf8df\uf8e0\uf8e1\uf8e2\uf8e3\uf8e4\uf8e5\uf8e6\uf8e7\uf8e8\uf8e9\uf8ea\uf8eb\uf8ec\uf8ed\uf8ee\uf8ef\uf8f0\uf8f1\uf8f2\uf8f3\uf8f4\uf8f5\uf8f6\uf8f7\uf8f8\uf8f9\uf8fa\uf8fb\uf8fc\uf8fd\uf8fe\uf8ff'
-
-try:
- Cs = eval(r"'\ud800\ud801\ud802\ud803\ud804\ud805\ud806\ud807\ud808\ud809\ud80a\ud80b\ud80c\ud80d\ud80e\ud80f\ud810\ud811\ud812\ud813\ud814\ud815\ud816\ud817\ud818\ud819\ud81a\ud81b\ud81c\ud81d\ud81e\ud81f\ud820\ud821\ud822\ud823\ud824\ud825\ud826\ud827\ud828\ud829\ud82a\ud82b\ud82c\ud82d\ud82e\ud82f\ud830\ud831\ud832\ud833\ud834\ud835\ud836\ud837\ud838\ud839\ud83a\ud83b\ud83c\ud83d\ud83e\ud83f\ud840\ud841\ud842\ud843\ud844\ud845\ud846\ud847\ud848\ud849\ud84a\ud84b\ud84c\ud84d\ud84e\ud84f\ud850\ud851\ud852\ud853\ud854\ud855\ud856\ud857\ud858\ud859\ud85a\ud85b\ud85c\ud85d\ud85e\ud85f\ud860\ud861\ud862\ud863\ud864\ud865\ud866\ud867\ud868\ud869\ud86a\ud86b\ud86c\ud86d\ud86e\ud86f\ud870\ud871\ud872\ud873\ud874\ud875\ud876\ud877\ud878\ud879\ud87a\ud87b\ud87c\ud87d\ud87e\ud87f\ud880\ud881\ud882\ud883\ud884\ud885\ud886\ud887\ud888\ud889\ud88a\ud88b\ud88c\ud88d\ud88e\ud88f\ud890\ud891\ud892\ud893\ud894\ud895\ud896\ud897\ud898\ud899\ud89a\ud89b\ud89c\ud89d\ud89e\ud89f\ud8a0\ud8a1\ud8a2\ud8a3\ud8a4\ud8a5\ud8a6\ud8a7\ud8a8\ud8a9\ud8aa\ud8ab\ud8ac\ud8ad\ud8ae\ud8af\ud8b0\ud8b1\ud8b2\ud8b3\ud8b4\ud8b5\ud8b6\ud8b7\ud8b8\ud8b9\ud8ba\ud8bb\ud8bc\ud8bd\ud8be\ud8bf\ud8c0\ud8c1\ud8c2\ud8c3\ud8c4\ud8c5\ud8c6\ud8c7\ud8c8\ud8c9\ud8ca\ud8cb\ud8cc\ud8cd\ud8ce\ud8cf\ud8d0\ud8d1\ud8d2\ud8d3\ud8d4\ud8d5\ud8d6\ud8d7\ud8d8\ud8d9\ud8da\ud8db\ud8dc\ud8dd\ud8de\ud8df\ud8e0\ud8e1\ud8e2\ud8e3\ud8e4\ud8e5\ud8e6\ud8e7\ud8e8\ud8e9\ud8ea\ud8eb\ud8ec\ud8ed\ud8ee\ud8ef\ud8f0\ud8f1\ud8f2\ud8f3\ud8f4\ud8f5\ud8f6\ud8f7\ud8f8\ud8f9\ud8fa\ud8fb\ud8fc\ud8fd\ud8fe\ud8ff\ud900\ud901\ud902\ud903\ud904\ud905\ud906\ud907\ud908\ud909\ud90a\ud90b\ud90c\ud90d\ud90e\ud90f\ud910\ud911\ud912\ud913\ud914\ud915\ud916\ud917\ud918\ud919\ud91a\ud91b\ud91c\ud91d\ud91e\ud91f\ud920\ud921\ud922\ud923\ud924\ud925\ud926\ud927\ud928\ud929\ud92a\ud92b\ud92c\ud92d\ud92e\ud92f\ud930\ud931\ud932\ud933\ud934\ud935\ud936\ud937\ud938\ud939\ud93a\ud93b\ud93c\ud93d\ud93e\ud93f\ud940\ud941\ud942\ud943\ud944\ud945\ud946\ud947\ud948\ud949\ud94a\ud94b\ud94c\ud94d\ud94e\ud94f\ud950\ud951\ud952\ud953\ud954\ud955\ud956\ud957\ud958\ud959\ud95a\ud95b\ud95c\ud95d\ud95e\ud95f\ud960\ud961\ud962\ud963\ud964\ud965\ud966\ud967\ud968\ud969\ud96a\ud96b\ud96c\ud96d\ud96e\ud96f\ud970\ud971\ud972\ud973\ud974\ud975\ud976\ud977\ud978\ud979\ud97a\ud97b\ud97c\ud97d\ud97e\ud97f\ud980\ud981\ud982\ud983\ud984\ud985\ud986\ud987\ud988\ud989\ud98a\ud98b\ud98c\ud98d\ud98e\ud98f\ud990\ud991\ud992\ud993\ud994\ud995\ud996\ud997\ud998\ud999\ud99a\ud99b\ud99c\ud99d\ud99e\ud99f\ud9a0\ud9a1\ud9a2\ud9a3\ud9a4\ud9a5\ud9a6\ud9a7\ud9a8\ud9a9\ud9aa\ud9ab\ud9ac\ud9ad\ud9ae\ud9af\ud9b0\ud9b1\ud9b2\ud9b3\ud9b4\ud9b5\ud9b6\ud9b7\ud9b8\ud9b9\ud9ba\ud9bb\ud9bc\ud9bd\ud9be\ud9bf\ud9c0\ud9c1\ud9c2\ud9c3\ud9c4\ud9c5\ud9c6\ud9c7\ud9c8\ud9c9\ud9ca\ud9cb\ud9cc\ud9cd\ud9ce\ud9cf\ud9d0\ud9d1\ud9d2\ud9d3\ud9d4\ud9d5\ud9d6\ud9d7\ud9d8\ud9d9\ud9da\ud9db\ud9dc\ud9dd\ud9de\ud9df\ud9e0\ud9e1\ud9e2\ud9e3\ud9e4\ud9e5\ud9e6\ud9e7\ud9e8\ud9e9\ud9ea\ud9eb\ud9ec\ud9ed\ud9ee\ud9ef\ud9f0\ud9f1\ud9f2\ud9f3\ud9f4\ud9f5\ud9f6\ud9f7\ud9f8\ud9f9\ud9fa\ud9fb\ud9fc\ud9fd\ud9fe\ud9ff\uda00\uda01\uda02\uda03\uda04\uda05\uda06\uda07\uda08\uda09\uda0a\uda0b\uda0c\uda0d\uda0e\uda0f\uda10\uda11\uda12\uda13\uda14\uda15\uda16\uda17\uda18\uda19\uda1a\uda1b\uda1c\uda1d\uda1e\uda1f\uda20\uda21\uda22\uda23\uda24\uda25\uda26\uda27\uda28\uda29\uda2a\uda2b\uda2c\uda2d\uda2e\uda2f\uda30\uda31\uda32\uda33\uda34\uda35\uda36\uda37\uda38\uda39\uda3a\uda3b\uda3c\uda3d\uda3e\uda3f\uda40\uda41\uda42\uda43\uda44\uda45\uda46\uda47\uda48\uda49\uda4a\uda4b\uda4c\uda4d\uda4e\uda4f\uda50\uda51\uda52\uda53\uda54\uda55\uda56\uda57\uda58\uda59\uda5a\uda5b\uda5c\uda5d\uda5e\uda5f\uda60\uda61\uda62\uda63\uda64\uda65\uda66\uda67\uda68\uda69\uda6a\uda6b\uda6c\uda6d\uda6e\uda6f\uda70\uda71\uda72\uda73\uda74\uda75\uda76\uda77\uda78\uda79\uda7a\uda7b\uda7c\uda7d\uda7e\uda7f\uda80\uda81\uda82\uda83\uda84\uda85\uda86\uda87\uda88\uda89\uda8a\uda8b\uda8c\uda8d\uda8e\uda8f\uda90\uda91\uda92\uda93\uda94\uda95\uda96\uda97\uda98\uda99\uda9a\uda9b\uda9c\uda9d\uda9e\uda9f\udaa0\udaa1\udaa2\udaa3\udaa4\udaa5\udaa6\udaa7\udaa8\udaa9\udaaa\udaab\udaac\udaad\udaae\udaaf\udab0\udab1\udab2\udab3\udab4\udab5\udab6\udab7\udab8\udab9\udaba\udabb\udabc\udabd\udabe\udabf\udac0\udac1\udac2\udac3\udac4\udac5\udac6\udac7\udac8\udac9\udaca\udacb\udacc\udacd\udace\udacf\udad0\udad1\udad2\udad3\udad4\udad5\udad6\udad7\udad8\udad9\udada\udadb\udadc\udadd\udade\udadf\udae0\udae1\udae2\udae3\udae4\udae5\udae6\udae7\udae8\udae9\udaea\udaeb\udaec\udaed\udaee\udaef\udaf0\udaf1\udaf2\udaf3\udaf4\udaf5\udaf6\udaf7\udaf8\udaf9\udafa\udafb\udafc\udafd\udafe\udaff\udb00\udb01\udb02\udb03\udb04\udb05\udb06\udb07\udb08\udb09\udb0a\udb0b\udb0c\udb0d\udb0e\udb0f\udb10\udb11\udb12\udb13\udb14\udb15\udb16\udb17\udb18\udb19\udb1a\udb1b\udb1c\udb1d\udb1e\udb1f\udb20\udb21\udb22\udb23\udb24\udb25\udb26\udb27\udb28\udb29\udb2a\udb2b\udb2c\udb2d\udb2e\udb2f\udb30\udb31\udb32\udb33\udb34\udb35\udb36\udb37\udb38\udb39\udb3a\udb3b\udb3c\udb3d\udb3e\udb3f\udb40\udb41\udb42\udb43\udb44\udb45\udb46\udb47\udb48\udb49\udb4a\udb4b\udb4c\udb4d\udb4e\udb4f\udb50\udb51\udb52\udb53\udb54\udb55\udb56\udb57\udb58\udb59\udb5a\udb5b\udb5c\udb5d\udb5e\udb5f\udb60\udb61\udb62\udb63\udb64\udb65\udb66\udb67\udb68\udb69\udb6a\udb6b\udb6c\udb6d\udb6e\udb6f\udb70\udb71\udb72\udb73\udb74\udb75\udb76\udb77\udb78\udb79\udb7a\udb7b\udb7c\udb7d\udb7e\udb7f\udb80\udb81\udb82\udb83\udb84\udb85\udb86\udb87\udb88\udb89\udb8a\udb8b\udb8c\udb8d\udb8e\udb8f\udb90\udb91\udb92\udb93\udb94\udb95\udb96\udb97\udb98\udb99\udb9a\udb9b\udb9c\udb9d\udb9e\udb9f\udba0\udba1\udba2\udba3\udba4\udba5\udba6\udba7\udba8\udba9\udbaa\udbab\udbac\udbad\udbae\udbaf\udbb0\udbb1\udbb2\udbb3\udbb4\udbb5\udbb6\udbb7\udbb8\udbb9\udbba\udbbb\udbbc\udbbd\udbbe\udbbf\udbc0\udbc1\udbc2\udbc3\udbc4\udbc5\udbc6\udbc7\udbc8\udbc9\udbca\udbcb\udbcc\udbcd\udbce\udbcf\udbd0\udbd1\udbd2\udbd3\udbd4\udbd5\udbd6\udbd7\udbd8\udbd9\udbda\udbdb\udbdc\udbdd\udbde\udbdf\udbe0\udbe1\udbe2\udbe3\udbe4\udbe5\udbe6\udbe7\udbe8\udbe9\udbea\udbeb\udbec\udbed\udbee\udbef\udbf0\udbf1\udbf2\udbf3\udbf4\udbf5\udbf6\udbf7\udbf8\udbf9\udbfa\udbfb\udbfc\udbfd\udbfe\U0010fc00\udc01\udc02\udc03\udc04\udc05\udc06\udc07\udc08\udc09\udc0a\udc0b\udc0c\udc0d\udc0e\udc0f\udc10\udc11\udc12\udc13\udc14\udc15\udc16\udc17\udc18\udc19\udc1a\udc1b\udc1c\udc1d\udc1e\udc1f\udc20\udc21\udc22\udc23\udc24\udc25\udc26\udc27\udc28\udc29\udc2a\udc2b\udc2c\udc2d\udc2e\udc2f\udc30\udc31\udc32\udc33\udc34\udc35\udc36\udc37\udc38\udc39\udc3a\udc3b\udc3c\udc3d\udc3e\udc3f\udc40\udc41\udc42\udc43\udc44\udc45\udc46\udc47\udc48\udc49\udc4a\udc4b\udc4c\udc4d\udc4e\udc4f\udc50\udc51\udc52\udc53\udc54\udc55\udc56\udc57\udc58\udc59\udc5a\udc5b\udc5c\udc5d\udc5e\udc5f\udc60\udc61\udc62\udc63\udc64\udc65\udc66\udc67\udc68\udc69\udc6a\udc6b\udc6c\udc6d\udc6e\udc6f\udc70\udc71\udc72\udc73\udc74\udc75\udc76\udc77\udc78\udc79\udc7a\udc7b\udc7c\udc7d\udc7e\udc7f\udc80\udc81\udc82\udc83\udc84\udc85\udc86\udc87\udc88\udc89\udc8a\udc8b\udc8c\udc8d\udc8e\udc8f\udc90\udc91\udc92\udc93\udc94\udc95\udc96\udc97\udc98\udc99\udc9a\udc9b\udc9c\udc9d\udc9e\udc9f\udca0\udca1\udca2\udca3\udca4\udca5\udca6\udca7\udca8\udca9\udcaa\udcab\udcac\udcad\udcae\udcaf\udcb0\udcb1\udcb2\udcb3\udcb4\udcb5\udcb6\udcb7\udcb8\udcb9\udcba\udcbb\udcbc\udcbd\udcbe\udcbf\udcc0\udcc1\udcc2\udcc3\udcc4\udcc5\udcc6\udcc7\udcc8\udcc9\udcca\udccb\udccc\udccd\udcce\udccf\udcd0\udcd1\udcd2\udcd3\udcd4\udcd5\udcd6\udcd7\udcd8\udcd9\udcda\udcdb\udcdc\udcdd\udcde\udcdf\udce0\udce1\udce2\udce3\udce4\udce5\udce6\udce7\udce8\udce9\udcea\udceb\udcec\udced\udcee\udcef\udcf0\udcf1\udcf2\udcf3\udcf4\udcf5\udcf6\udcf7\udcf8\udcf9\udcfa\udcfb\udcfc\udcfd\udcfe\udcff\udd00\udd01\udd02\udd03\udd04\udd05\udd06\udd07\udd08\udd09\udd0a\udd0b\udd0c\udd0d\udd0e\udd0f\udd10\udd11\udd12\udd13\udd14\udd15\udd16\udd17\udd18\udd19\udd1a\udd1b\udd1c\udd1d\udd1e\udd1f\udd20\udd21\udd22\udd23\udd24\udd25\udd26\udd27\udd28\udd29\udd2a\udd2b\udd2c\udd2d\udd2e\udd2f\udd30\udd31\udd32\udd33\udd34\udd35\udd36\udd37\udd38\udd39\udd3a\udd3b\udd3c\udd3d\udd3e\udd3f\udd40\udd41\udd42\udd43\udd44\udd45\udd46\udd47\udd48\udd49\udd4a\udd4b\udd4c\udd4d\udd4e\udd4f\udd50\udd51\udd52\udd53\udd54\udd55\udd56\udd57\udd58\udd59\udd5a\udd5b\udd5c\udd5d\udd5e\udd5f\udd60\udd61\udd62\udd63\udd64\udd65\udd66\udd67\udd68\udd69\udd6a\udd6b\udd6c\udd6d\udd6e\udd6f\udd70\udd71\udd72\udd73\udd74\udd75\udd76\udd77\udd78\udd79\udd7a\udd7b\udd7c\udd7d\udd7e\udd7f\udd80\udd81\udd82\udd83\udd84\udd85\udd86\udd87\udd88\udd89\udd8a\udd8b\udd8c\udd8d\udd8e\udd8f\udd90\udd91\udd92\udd93\udd94\udd95\udd96\udd97\udd98\udd99\udd9a\udd9b\udd9c\udd9d\udd9e\udd9f\udda0\udda1\udda2\udda3\udda4\udda5\udda6\udda7\udda8\udda9\uddaa\uddab\uddac\uddad\uddae\uddaf\uddb0\uddb1\uddb2\uddb3\uddb4\uddb5\uddb6\uddb7\uddb8\uddb9\uddba\uddbb\uddbc\uddbd\uddbe\uddbf\uddc0\uddc1\uddc2\uddc3\uddc4\uddc5\uddc6\uddc7\uddc8\uddc9\uddca\uddcb\uddcc\uddcd\uddce\uddcf\uddd0\uddd1\uddd2\uddd3\uddd4\uddd5\uddd6\uddd7\uddd8\uddd9\uddda\udddb\udddc\udddd\uddde\udddf\udde0\udde1\udde2\udde3\udde4\udde5\udde6\udde7\udde8\udde9\uddea\uddeb\uddec\udded\uddee\uddef\uddf0\uddf1\uddf2\uddf3\uddf4\uddf5\uddf6\uddf7\uddf8\uddf9\uddfa\uddfb\uddfc\uddfd\uddfe\uddff\ude00\ude01\ude02\ude03\ude04\ude05\ude06\ude07\ude08\ude09\ude0a\ude0b\ude0c\ude0d\ude0e\ude0f\ude10\ude11\ude12\ude13\ude14\ude15\ude16\ude17\ude18\ude19\ude1a\ude1b\ude1c\ude1d\ude1e\ude1f\ude20\ude21\ude22\ude23\ude24\ude25\ude26\ude27\ude28\ude29\ude2a\ude2b\ude2c\ude2d\ude2e\ude2f\ude30\ude31\ude32\ude33\ude34\ude35\ude36\ude37\ude38\ude39\ude3a\ude3b\ude3c\ude3d\ude3e\ude3f\ude40\ude41\ude42\ude43\ude44\ude45\ude46\ude47\ude48\ude49\ude4a\ude4b\ude4c\ude4d\ude4e\ude4f\ude50\ude51\ude52\ude53\ude54\ude55\ude56\ude57\ude58\ude59\ude5a\ude5b\ude5c\ude5d\ude5e\ude5f\ude60\ude61\ude62\ude63\ude64\ude65\ude66\ude67\ude68\ude69\ude6a\ude6b\ude6c\ude6d\ude6e\ude6f\ude70\ude71\ude72\ude73\ude74\ude75\ude76\ude77\ude78\ude79\ude7a\ude7b\ude7c\ude7d\ude7e\ude7f\ude80\ude81\ude82\ude83\ude84\ude85\ude86\ude87\ude88\ude89\ude8a\ude8b\ude8c\ude8d\ude8e\ude8f\ude90\ude91\ude92\ude93\ude94\ude95\ude96\ude97\ude98\ude99\ude9a\ude9b\ude9c\ude9d\ude9e\ude9f\udea0\udea1\udea2\udea3\udea4\udea5\udea6\udea7\udea8\udea9\udeaa\udeab\udeac\udead\udeae\udeaf\udeb0\udeb1\udeb2\udeb3\udeb4\udeb5\udeb6\udeb7\udeb8\udeb9\udeba\udebb\udebc\udebd\udebe\udebf\udec0\udec1\udec2\udec3\udec4\udec5\udec6\udec7\udec8\udec9\udeca\udecb\udecc\udecd\udece\udecf\uded0\uded1\uded2\uded3\uded4\uded5\uded6\uded7\uded8\uded9\udeda\udedb\udedc\udedd\udede\udedf\udee0\udee1\udee2\udee3\udee4\udee5\udee6\udee7\udee8\udee9\udeea\udeeb\udeec\udeed\udeee\udeef\udef0\udef1\udef2\udef3\udef4\udef5\udef6\udef7\udef8\udef9\udefa\udefb\udefc\udefd\udefe\udeff\udf00\udf01\udf02\udf03\udf04\udf05\udf06\udf07\udf08\udf09\udf0a\udf0b\udf0c\udf0d\udf0e\udf0f\udf10\udf11\udf12\udf13\udf14\udf15\udf16\udf17\udf18\udf19\udf1a\udf1b\udf1c\udf1d\udf1e\udf1f\udf20\udf21\udf22\udf23\udf24\udf25\udf26\udf27\udf28\udf29\udf2a\udf2b\udf2c\udf2d\udf2e\udf2f\udf30\udf31\udf32\udf33\udf34\udf35\udf36\udf37\udf38\udf39\udf3a\udf3b\udf3c\udf3d\udf3e\udf3f\udf40\udf41\udf42\udf43\udf44\udf45\udf46\udf47\udf48\udf49\udf4a\udf4b\udf4c\udf4d\udf4e\udf4f\udf50\udf51\udf52\udf53\udf54\udf55\udf56\udf57\udf58\udf59\udf5a\udf5b\udf5c\udf5d\udf5e\udf5f\udf60\udf61\udf62\udf63\udf64\udf65\udf66\udf67\udf68\udf69\udf6a\udf6b\udf6c\udf6d\udf6e\udf6f\udf70\udf71\udf72\udf73\udf74\udf75\udf76\udf77\udf78\udf79\udf7a\udf7b\udf7c\udf7d\udf7e\udf7f\udf80\udf81\udf82\udf83\udf84\udf85\udf86\udf87\udf88\udf89\udf8a\udf8b\udf8c\udf8d\udf8e\udf8f\udf90\udf91\udf92\udf93\udf94\udf95\udf96\udf97\udf98\udf99\udf9a\udf9b\udf9c\udf9d\udf9e\udf9f\udfa0\udfa1\udfa2\udfa3\udfa4\udfa5\udfa6\udfa7\udfa8\udfa9\udfaa\udfab\udfac\udfad\udfae\udfaf\udfb0\udfb1\udfb2\udfb3\udfb4\udfb5\udfb6\udfb7\udfb8\udfb9\udfba\udfbb\udfbc\udfbd\udfbe\udfbf\udfc0\udfc1\udfc2\udfc3\udfc4\udfc5\udfc6\udfc7\udfc8\udfc9\udfca\udfcb\udfcc\udfcd\udfce\udfcf\udfd0\udfd1\udfd2\udfd3\udfd4\udfd5\udfd6\udfd7\udfd8\udfd9\udfda\udfdb\udfdc\udfdd\udfde\udfdf\udfe0\udfe1\udfe2\udfe3\udfe4\udfe5\udfe6\udfe7\udfe8\udfe9\udfea\udfeb\udfec\udfed\udfee\udfef\udff0\udff1\udff2\udff3\udff4\udff5\udff6\udff7\udff8\udff9\udffa\udffb\udffc\udffd\udffe\udfff'")
-except UnicodeDecodeError:
- Cs = '' # Jython can't handle isolated surrogates
-
-Ll = u'abcdefghijklmnopqrstuvwxyz\xaa\xb5\xba\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\u0101\u0103\u0105\u0107\u0109\u010b\u010d\u010f\u0111\u0113\u0115\u0117\u0119\u011b\u011d\u011f\u0121\u0123\u0125\u0127\u0129\u012b\u012d\u012f\u0131\u0133\u0135\u0137\u0138\u013a\u013c\u013e\u0140\u0142\u0144\u0146\u0148\u0149\u014b\u014d\u014f\u0151\u0153\u0155\u0157\u0159\u015b\u015d\u015f\u0161\u0163\u0165\u0167\u0169\u016b\u016d\u016f\u0171\u0173\u0175\u0177\u017a\u017c\u017e\u017f\u0180\u0183\u0185\u0188\u018c\u018d\u0192\u0195\u0199\u019a\u019b\u019e\u01a1\u01a3\u01a5\u01a8\u01aa\u01ab\u01ad\u01b0\u01b4\u01b6\u01b9\u01ba\u01bd\u01be\u01bf\u01c6\u01c9\u01cc\u01ce\u01d0\u01d2\u01d4\u01d6\u01d8\u01da\u01dc\u01dd\u01df\u01e1\u01e3\u01e5\u01e7\u01e9\u01eb\u01ed\u01ef\u01f0\u01f3\u01f5\u01f9\u01fb\u01fd\u01ff\u0201\u0203\u0205\u0207\u0209\u020b\u020d\u020f\u0211\u0213\u0215\u0217\u0219\u021b\u021d\u021f\u0221\u0223\u0225\u0227\u0229\u022b\u022d\u022f\u0231\u0233\u0234\u0235\u0236\u0237\u0238\u0239\u023c\u023f\u0240\u0250\u0251\u0252\u0253\u0254\u0255\u0256\u0257\u0258\u0259\u025a\u025b\u025c\u025d\u025e\u025f\u0260\u0261\u0262\u0263\u0264\u0265\u0266\u0267\u0268\u0269\u026a\u026b\u026c\u026d\u026e\u026f\u0270\u0271\u0272\u0273\u0274\u0275\u0276\u0277\u0278\u0279\u027a\u027b\u027c\u027d\u027e\u027f\u0280\u0281\u0282\u0283\u0284\u0285\u0286\u0287\u0288\u0289\u028a\u028b\u028c\u028d\u028e\u028f\u0290\u0291\u0292\u0293\u0294\u0295\u0296\u0297\u0298\u0299\u029a\u029b\u029c\u029d\u029e\u029f\u02a0\u02a1\u02a2\u02a3\u02a4\u02a5\u02a6\u02a7\u02a8\u02a9\u02aa\u02ab\u02ac\u02ad\u02ae\u02af\u0390\u03ac\u03ad\u03ae\u03af\u03b0\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9\u03ca\u03cb\u03cc\u03cd\u03ce\u03d0\u03d1\u03d5\u03d6\u03d7\u03d9\u03db\u03dd\u03df\u03e1\u03e3\u03e5\u03e7\u03e9\u03eb\u03ed\u03ef\u03f0\u03f1\u03f2\u03f3\u03f5\u03f8\u03fb\u03fc\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f\u0450\u0451\u0452\u0453\u0454\u0455\u0456\u0457\u0458\u0459\u045a\u045b\u045c\u045d\u045e\u045f\u0461\u0463\u0465\u0467\u0469\u046b\u046d\u046f\u0471\u0473\u0475\u0477\u0479\u047b\u047d\u047f\u0481\u048b\u048d\u048f\u0491\u0493\u0495\u0497\u0499\u049b\u049d\u049f\u04a1\u04a3\u04a5\u04a7\u04a9\u04ab\u04ad\u04af\u04b1\u04b3\u04b5\u04b7\u04b9\u04bb\u04bd\u04bf\u04c2\u04c4\u04c6\u04c8\u04ca\u04cc\u04ce\u04d1\u04d3\u04d5\u04d7\u04d9\u04db\u04dd\u04df\u04e1\u04e3\u04e5\u04e7\u04e9\u04eb\u04ed\u04ef\u04f1\u04f3\u04f5\u04f7\u04f9\u0501\u0503\u0505\u0507\u0509\u050b\u050d\u050f\u0561\u0562\u0563\u0564\u0565\u0566\u0567\u0568\u0569\u056a\u056b\u056c\u056d\u056e\u056f\u0570\u0571\u0572\u0573\u0574\u0575\u0576\u0577\u0578\u0579\u057a\u057b\u057c\u057d\u057e\u057f\u0580\u0581\u0582\u0583\u0584\u0585\u0586\u0587\u1d00\u1d01\u1d02\u1d03\u1d04\u1d05\u1d06\u1d07\u1d08\u1d09\u1d0a\u1d0b\u1d0c\u1d0d\u1d0e\u1d0f\u1d10\u1d11\u1d12\u1d13\u1d14\u1d15\u1d16\u1d17\u1d18\u1d19\u1d1a\u1d1b\u1d1c\u1d1d\u1d1e\u1d1f\u1d20\u1d21\u1d22\u1d23\u1d24\u1d25\u1d26\u1d27\u1d28\u1d29\u1d2a\u1d2b\u1d62\u1d63\u1d64\u1d65\u1d66\u1d67\u1d68\u1d69\u1d6a\u1d6b\u1d6c\u1d6d\u1d6e\u1d6f\u1d70\u1d71\u1d72\u1d73\u1d74\u1d75\u1d76\u1d77\u1d79\u1d7a\u1d7b\u1d7c\u1d7d\u1d7e\u1d7f\u1d80\u1d81\u1d82\u1d83\u1d84\u1d85\u1d86\u1d87\u1d88\u1d89\u1d8a\u1d8b\u1d8c\u1d8d\u1d8e\u1d8f\u1d90\u1d91\u1d92\u1d93\u1d94\u1d95\u1d96\u1d97\u1d98\u1d99\u1d9a\u1e01\u1e03\u1e05\u1e07\u1e09\u1e0b\u1e0d\u1e0f\u1e11\u1e13\u1e15\u1e17\u1e19\u1e1b\u1e1d\u1e1f\u1e21\u1e23\u1e25\u1e27\u1e29\u1e2b\u1e2d\u1e2f\u1e31\u1e33\u1e35\u1e37\u1e39\u1e3b\u1e3d\u1e3f\u1e41\u1e43\u1e45\u1e47\u1e49\u1e4b\u1e4d\u1e4f\u1e51\u1e53\u1e55\u1e57\u1e59\u1e5b\u1e5d\u1e5f\u1e61\u1e63\u1e65\u1e67\u1e69\u1e6b\u1e6d\u1e6f\u1e71\u1e73\u1e75\u1e77\u1e79\u1e7b\u1e7d\u1e7f\u1e81\u1e83\u1e85\u1e87\u1e89\u1e8b\u1e8d\u1e8f\u1e91\u1e93\u1e95\u1e96\u1e97\u1e98\u1e99\u1e9a\u1e9b\u1ea1\u1ea3\u1ea5\u1ea7\u1ea9\u1eab\u1ead\u1eaf\u1eb1\u1eb3\u1eb5\u1eb7\u1eb9\u1ebb\u1ebd\u1ebf\u1ec1\u1ec3\u1ec5\u1ec7\u1ec9\u1ecb\u1ecd\u1ecf\u1ed1\u1ed3\u1ed5\u1ed7\u1ed9\u1edb\u1edd\u1edf\u1ee1\u1ee3\u1ee5\u1ee7\u1ee9\u1eeb\u1eed\u1eef\u1ef1\u1ef3\u1ef5\u1ef7\u1ef9\u1f00\u1f01\u1f02\u1f03\u1f04\u1f05\u1f06\u1f07\u1f10\u1f11\u1f12\u1f13\u1f14\u1f15\u1f20\u1f21\u1f22\u1f23\u1f24\u1f25\u1f26\u1f27\u1f30\u1f31\u1f32\u1f33\u1f34\u1f35\u1f36\u1f37\u1f40\u1f41\u1f42\u1f43\u1f44\u1f45\u1f50\u1f51\u1f52\u1f53\u1f54\u1f55\u1f56\u1f57\u1f60\u1f61\u1f62\u1f63\u1f64\u1f65\u1f66\u1f67\u1f70\u1f71\u1f72\u1f73\u1f74\u1f75\u1f76\u1f77\u1f78\u1f79\u1f7a\u1f7b\u1f7c\u1f7d\u1f80\u1f81\u1f82\u1f83\u1f84\u1f85\u1f86\u1f87\u1f90\u1f91\u1f92\u1f93\u1f94\u1f95\u1f96\u1f97\u1fa0\u1fa1\u1fa2\u1fa3\u1fa4\u1fa5\u1fa6\u1fa7\u1fb0\u1fb1\u1fb2\u1fb3\u1fb4\u1fb6\u1fb7\u1fbe\u1fc2\u1fc3\u1fc4\u1fc6\u1fc7\u1fd0\u1fd1\u1fd2\u1fd3\u1fd6\u1fd7\u1fe0\u1fe1\u1fe2\u1fe3\u1fe4\u1fe5\u1fe6\u1fe7\u1ff2\u1ff3\u1ff4\u1ff6\u1ff7\u2071\u207f\u210a\u210e\u210f\u2113\u212f\u2134\u2139\u213c\u213d\u2146\u2147\u2148\u2149\u2c30\u2c31\u2c32\u2c33\u2c34\u2c35\u2c36\u2c37\u2c38\u2c39\u2c3a\u2c3b\u2c3c\u2c3d\u2c3e\u2c3f\u2c40\u2c41\u2c42\u2c43\u2c44\u2c45\u2c46\u2c47\u2c48\u2c49\u2c4a\u2c4b\u2c4c\u2c4d\u2c4e\u2c4f\u2c50\u2c51\u2c52\u2c53\u2c54\u2c55\u2c56\u2c57\u2c58\u2c59\u2c5a\u2c5b\u2c5c\u2c5d\u2c5e\u2c81\u2c83\u2c85\u2c87\u2c89\u2c8b\u2c8d\u2c8f\u2c91\u2c93\u2c95\u2c97\u2c99\u2c9b\u2c9d\u2c9f\u2ca1\u2ca3\u2ca5\u2ca7\u2ca9\u2cab\u2cad\u2caf\u2cb1\u2cb3\u2cb5\u2cb7\u2cb9\u2cbb\u2cbd\u2cbf\u2cc1\u2cc3\u2cc5\u2cc7\u2cc9\u2ccb\u2ccd\u2ccf\u2cd1\u2cd3\u2cd5\u2cd7\u2cd9\u2cdb\u2cdd\u2cdf\u2ce1\u2ce3\u2ce4\u2d00\u2d01\u2d02\u2d03\u2d04\u2d05\u2d06\u2d07\u2d08\u2d09\u2d0a\u2d0b\u2d0c\u2d0d\u2d0e\u2d0f\u2d10\u2d11\u2d12\u2d13\u2d14\u2d15\u2d16\u2d17\u2d18\u2d19\u2d1a\u2d1b\u2d1c\u2d1d\u2d1e\u2d1f\u2d20\u2d21\u2d22\u2d23\u2d24\u2d25\ufb00\ufb01\ufb02\ufb03\ufb04\ufb05\ufb06\ufb13\ufb14\ufb15\ufb16\ufb17\uff41\uff42\uff43\uff44\uff45\uff46\uff47\uff48\uff49\uff4a\uff4b\uff4c\uff4d\uff4e\uff4f\uff50\uff51\uff52\uff53\uff54\uff55\uff56\uff57\uff58\uff59\uff5a'
-
-Lm = u'\u02b0\u02b1\u02b2\u02b3\u02b4\u02b5\u02b6\u02b7\u02b8\u02b9\u02ba\u02bb\u02bc\u02bd\u02be\u02bf\u02c0\u02c1\u02c6\u02c7\u02c8\u02c9\u02ca\u02cb\u02cc\u02cd\u02ce\u02cf\u02d0\u02d1\u02e0\u02e1\u02e2\u02e3\u02e4\u02ee\u037a\u0559\u0640\u06e5\u06e6\u0e46\u0ec6\u10fc\u17d7\u1843\u1d2c\u1d2d\u1d2e\u1d2f\u1d30\u1d31\u1d32\u1d33\u1d34\u1d35\u1d36\u1d37\u1d38\u1d39\u1d3a\u1d3b\u1d3c\u1d3d\u1d3e\u1d3f\u1d40\u1d41\u1d42\u1d43\u1d44\u1d45\u1d46\u1d47\u1d48\u1d49\u1d4a\u1d4b\u1d4c\u1d4d\u1d4e\u1d4f\u1d50\u1d51\u1d52\u1d53\u1d54\u1d55\u1d56\u1d57\u1d58\u1d59\u1d5a\u1d5b\u1d5c\u1d5d\u1d5e\u1d5f\u1d60\u1d61\u1d78\u1d9b\u1d9c\u1d9d\u1d9e\u1d9f\u1da0\u1da1\u1da2\u1da3\u1da4\u1da5\u1da6\u1da7\u1da8\u1da9\u1daa\u1dab\u1dac\u1dad\u1dae\u1daf\u1db0\u1db1\u1db2\u1db3\u1db4\u1db5\u1db6\u1db7\u1db8\u1db9\u1dba\u1dbb\u1dbc\u1dbd\u1dbe\u1dbf\u2090\u2091\u2092\u2093\u2094\u2d6f\u3005\u3031\u3032\u3033\u3034\u3035\u303b\u309d\u309e\u30fc\u30fd\u30fe\ua015\uff70\uff9e\uff9f'
-
-Lo = u'\u01bb\u01c0\u01c1\u01c2\u01c3\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05da\u05db\u05dc\u05dd\u05de\u05df\u05e0\u05e1\u05e2\u05e3\u05e4\u05e5\u05e6\u05e7\u05e8\u05e9\u05ea\u05f0\u05f1\u05f2\u0621\u0622\u0623\u0624\u0625\u0626\u0627\u0628\u0629\u062a\u062b\u062c\u062d\u062e\u062f\u0630\u0631\u0632\u0633\u0634\u0635\u0636\u0637\u0638\u0639\u063a\u0641\u0642\u0643\u0644\u0645\u0646\u0647\u0648\u0649\u064a\u066e\u066f\u0671\u0672\u0673\u0674\u0675\u0676\u0677\u0678\u0679\u067a\u067b\u067c\u067d\u067e\u067f\u0680\u0681\u0682\u0683\u0684\u0685\u0686\u0687\u0688\u0689\u068a\u068b\u068c\u068d\u068e\u068f\u0690\u0691\u0692\u0693\u0694\u0695\u0696\u0697\u0698\u0699\u069a\u069b\u069c\u069d\u069e\u069f\u06a0\u06a1\u06a2\u06a3\u06a4\u06a5\u06a6\u06a7\u06a8\u06a9\u06aa\u06ab\u06ac\u06ad\u06ae\u06af\u06b0\u06b1\u06b2\u06b3\u06b4\u06b5\u06b6\u06b7\u06b8\u06b9\u06ba\u06bb\u06bc\u06bd\u06be\u06bf\u06c0\u06c1\u06c2\u06c3\u06c4\u06c5\u06c6\u06c7\u06c8\u06c9\u06ca\u06cb\u06cc\u06cd\u06ce\u06cf\u06d0\u06d1\u06d2\u06d3\u06d5\u06ee\u06ef\u06fa\u06fb\u06fc\u06ff\u0710\u0712\u0713\u0714\u0715\u0716\u0717\u0718\u0719\u071a\u071b\u071c\u071d\u071e\u071f\u0720\u0721\u0722\u0723\u0724\u0725\u0726\u0727\u0728\u0729\u072a\u072b\u072c\u072d\u072e\u072f\u074d\u074e\u074f\u0750\u0751\u0752\u0753\u0754\u0755\u0756\u0757\u0758\u0759\u075a\u075b\u075c\u075d\u075e\u075f\u0760\u0761\u0762\u0763\u0764\u0765\u0766\u0767\u0768\u0769\u076a\u076b\u076c\u076d\u0780\u0781\u0782\u0783\u0784\u0785\u0786\u0787\u0788\u0789\u078a\u078b\u078c\u078d\u078e\u078f\u0790\u0791\u0792\u0793\u0794\u0795\u0796\u0797\u0798\u0799\u079a\u079b\u079c\u079d\u079e\u079f\u07a0\u07a1\u07a2\u07a3\u07a4\u07a5\u07b1\u0904\u0905\u0906\u0907\u0908\u0909\u090a\u090b\u090c\u090d\u090e\u090f\u0910\u0911\u0912\u0913\u0914\u0915\u0916\u0917\u0918\u0919\u091a\u091b\u091c\u091d\u091e\u091f\u0920\u0921\u0922\u0923\u0924\u0925\u0926\u0927\u0928\u0929\u092a\u092b\u092c\u092d\u092e\u092f\u0930\u0931\u0932\u0933\u0934\u0935\u0936\u0937\u0938\u0939\u093d\u0950\u0958\u0959\u095a\u095b\u095c\u095d\u095e\u095f\u0960\u0961\u097d\u0985\u0986\u0987\u0988\u0989\u098a\u098b\u098c\u098f\u0990\u0993\u0994\u0995\u0996\u0997\u0998\u0999\u099a\u099b\u099c\u099d\u099e\u099f\u09a0\u09a1\u09a2\u09a3\u09a4\u09a5\u09a6\u09a7\u09a8\u09aa\u09ab\u09ac\u09ad\u09ae\u09af\u09b0\u09b2\u09b6\u09b7\u09b8\u09b9\u09bd\u09ce\u09dc\u09dd\u09df\u09e0\u09e1\u09f0\u09f1\u0a05\u0a06\u0a07\u0a08\u0a09\u0a0a\u0a0f\u0a10\u0a13\u0a14\u0a15\u0a16\u0a17\u0a18\u0a19\u0a1a\u0a1b\u0a1c\u0a1d\u0a1e\u0a1f\u0a20\u0a21\u0a22\u0a23\u0a24\u0a25\u0a26\u0a27\u0a28\u0a2a\u0a2b\u0a2c\u0a2d\u0a2e\u0a2f\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59\u0a5a\u0a5b\u0a5c\u0a5e\u0a72\u0a73\u0a74\u0a85\u0a86\u0a87\u0a88\u0a89\u0a8a\u0a8b\u0a8c\u0a8d\u0a8f\u0a90\u0a91\u0a93\u0a94\u0a95\u0a96\u0a97\u0a98\u0a99\u0a9a\u0a9b\u0a9c\u0a9d\u0a9e\u0a9f\u0aa0\u0aa1\u0aa2\u0aa3\u0aa4\u0aa5\u0aa6\u0aa7\u0aa8\u0aaa\u0aab\u0aac\u0aad\u0aae\u0aaf\u0ab0\u0ab2\u0ab3\u0ab5\u0ab6\u0ab7\u0ab8\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05\u0b06\u0b07\u0b08\u0b09\u0b0a\u0b0b\u0b0c\u0b0f\u0b10\u0b13\u0b14\u0b15\u0b16\u0b17\u0b18\u0b19\u0b1a\u0b1b\u0b1c\u0b1d\u0b1e\u0b1f\u0b20\u0b21\u0b22\u0b23\u0b24\u0b25\u0b26\u0b27\u0b28\u0b2a\u0b2b\u0b2c\u0b2d\u0b2e\u0b2f\u0b30\u0b32\u0b33\u0b35\u0b36\u0b37\u0b38\u0b39\u0b3d\u0b5c\u0b5d\u0b5f\u0b60\u0b61\u0b71\u0b83\u0b85\u0b86\u0b87\u0b88\u0b89\u0b8a\u0b8e\u0b8f\u0b90\u0b92\u0b93\u0b94\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8\u0ba9\u0baa\u0bae\u0baf\u0bb0\u0bb1\u0bb2\u0bb3\u0bb4\u0bb5\u0bb6\u0bb7\u0bb8\u0bb9\u0c05\u0c06\u0c07\u0c08\u0c09\u0c0a\u0c0b\u0c0c\u0c0e\u0c0f\u0c10\u0c12\u0c13\u0c14\u0c15\u0c16\u0c17\u0c18\u0c19\u0c1a\u0c1b\u0c1c\u0c1d\u0c1e\u0c1f\u0c20\u0c21\u0c22\u0c23\u0c24\u0c25\u0c26\u0c27\u0c28\u0c2a\u0c2b\u0c2c\u0c2d\u0c2e\u0c2f\u0c30\u0c31\u0c32\u0c33\u0c35\u0c36\u0c37\u0c38\u0c39\u0c60\u0c61\u0c85\u0c86\u0c87\u0c88\u0c89\u0c8a\u0c8b\u0c8c\u0c8e\u0c8f\u0c90\u0c92\u0c93\u0c94\u0c95\u0c96\u0c97\u0c98\u0c99\u0c9a\u0c9b\u0c9c\u0c9d\u0c9e\u0c9f\u0ca0\u0ca1\u0ca2\u0ca3\u0ca4\u0ca5\u0ca6\u0ca7\u0ca8\u0caa\u0cab\u0cac\u0cad\u0cae\u0caf\u0cb0\u0cb1\u0cb2\u0cb3\u0cb5\u0cb6\u0cb7\u0cb8\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0d05\u0d06\u0d07\u0d08\u0d09\u0d0a\u0d0b\u0d0c\u0d0e\u0d0f\u0d10\u0d12\u0d13\u0d14\u0d15\u0d16\u0d17\u0d18\u0d19\u0d1a\u0d1b\u0d1c\u0d1d\u0d1e\u0d1f\u0d20\u0d21\u0d22\u0d23\u0d24\u0d25\u0d26\u0d27\u0d28\u0d2a\u0d2b\u0d2c\u0d2d\u0d2e\u0d2f\u0d30\u0d31\u0d32\u0d33\u0d34\u0d35\u0d36\u0d37\u0d38\u0d39\u0d60\u0d61\u0d85\u0d86\u0d87\u0d88\u0d89\u0d8a\u0d8b\u0d8c\u0d8d\u0d8e\u0d8f\u0d90\u0d91\u0d92\u0d93\u0d94\u0d95\u0d96\u0d9a\u0d9b\u0d9c\u0d9d\u0d9e\u0d9f\u0da0\u0da1\u0da2\u0da3\u0da4\u0da5\u0da6\u0da7\u0da8\u0da9\u0daa\u0dab\u0dac\u0dad\u0dae\u0daf\u0db0\u0db1\u0db3\u0db4\u0db5\u0db6\u0db7\u0db8\u0db9\u0dba\u0dbb\u0dbd\u0dc0\u0dc1\u0dc2\u0dc3\u0dc4\u0dc5\u0dc6\u0e01\u0e02\u0e03\u0e04\u0e05\u0e06\u0e07\u0e08\u0e09\u0e0a\u0e0b\u0e0c\u0e0d\u0e0e\u0e0f\u0e10\u0e11\u0e12\u0e13\u0e14\u0e15\u0e16\u0e17\u0e18\u0e19\u0e1a\u0e1b\u0e1c\u0e1d\u0e1e\u0e1f\u0e20\u0e21\u0e22\u0e23\u0e24\u0e25\u0e26\u0e27\u0e28\u0e29\u0e2a\u0e2b\u0e2c\u0e2d\u0e2e\u0e2f\u0e30\u0e32\u0e33\u0e40\u0e41\u0e42\u0e43\u0e44\u0e45\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94\u0e95\u0e96\u0e97\u0e99\u0e9a\u0e9b\u0e9c\u0e9d\u0e9e\u0e9f\u0ea1\u0ea2\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead\u0eae\u0eaf\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0\u0ec1\u0ec2\u0ec3\u0ec4\u0edc\u0edd\u0f00\u0f40\u0f41\u0f42\u0f43\u0f44\u0f45\u0f46\u0f47\u0f49\u0f4a\u0f4b\u0f4c\u0f4d\u0f4e\u0f4f\u0f50\u0f51\u0f52\u0f53\u0f54\u0f55\u0f56\u0f57\u0f58\u0f59\u0f5a\u0f5b\u0f5c\u0f5d\u0f5e\u0f5f\u0f60\u0f61\u0f62\u0f63\u0f64\u0f65\u0f66\u0f67\u0f68\u0f69\u0f6a\u0f88\u0f89\u0f8a\u0f8b\u1000\u1001\u1002\u1003\u1004\u1005\u1006\u1007\u1008\u1009\u100a\u100b\u100c\u100d\u100e\u100f\u1010\u1011\u1012\u1013\u1014\u1015\u1016\u1017\u1018\u1019\u101a\u101b\u101c\u101d\u101e\u101f\u1020\u1021\u1023\u1024\u1025\u1026\u1027\u1029\u102a\u1050\u1051\u1052\u1053\u1054\u1055\u10d0\u10d1\u10d2\u10d3\u10d4\u10d5\u10d6\u10d7\u10d8\u10d9\u10da\u10db\u10dc\u10dd\u10de\u10df\u10e0\u10e1\u10e2\u10e3\u10e4\u10e5\u10e6\u10e7\u10e8\u10e9\u10ea\u10eb\u10ec\u10ed\u10ee\u10ef\u10f0\u10f1\u10f2\u10f3\u10f4\u10f5\u10f6\u10f7\u10f8\u10f9\u10fa\u1100\u1101\u1102\u1103\u1104\u1105\u1106\u1107\u1108\u1109\u110a\u110b\u110c\u110d\u110e\u110f\u1110\u1111\u1112\u1113\u1114\u1115\u1116\u1117\u1118\u1119\u111a\u111b\u111c\u111d\u111e\u111f\u1120\u1121\u1122\u1123\u1124\u1125\u1126\u1127\u1128\u1129\u112a\u112b\u112c\u112d\u112e\u112f\u1130\u1131\u1132\u1133\u1134\u1135\u1136\u1137\u1138\u1139\u113a\u113b\u113c\u113d\u113e\u113f\u1140\u1141\u1142\u1143\u1144\u1145\u1146\u1147\u1148\u1149\u114a\u114b\u114c\u114d\u114e\u114f\u1150\u1151\u1152\u1153\u1154\u1155\u1156\u1157\u1158\u1159\u115f\u1160\u1161\u1162\u1163\u1164\u1165\u1166\u1167\u1168\u1169\u116a\u116b\u116c\u116d\u116e\u116f\u1170\u1171\u1172\u1173\u1174\u1175\u1176\u1177\u1178\u1179\u117a\u117b\u117c\u117d\u117e\u117f\u1180\u1181\u1182\u1183\u1184\u1185\u1186\u1187\u1188\u1189\u118a\u118b\u118c\u118d\u118e\u118f\u1190\u1191\u1192\u1193\u1194\u1195\u1196\u1197\u1198\u1199\u119a\u119b\u119c\u119d\u119e\u119f\u11a0\u11a1\u11a2\u11a8\u11a9\u11aa\u11ab\u11ac\u11ad\u11ae\u11af\u11b0\u11b1\u11b2\u11b3\u11b4\u11b5\u11b6\u11b7\u11b8\u11b9\u11ba\u11bb\u11bc\u11bd\u11be\u11bf\u11c0\u11c1\u11c2\u11c3\u11c4\u11c5\u11c6\u11c7\u11c8\u11c9\u11ca\u11cb\u11cc\u11cd\u11ce\u11cf\u11d0\u11d1\u11d2\u11d3\u11d4\u11d5\u11d6\u11d7\u11d8\u11d9\u11da\u11db\u11dc\u11dd\u11de\u11df\u11e0\u11e1\u11e2\u11e3\u11e4\u11e5\u11e6\u11e7\u11e8\u11e9\u11ea\u11eb\u11ec\u11ed\u11ee\u11ef\u11f0\u11f1\u11f2\u11f3\u11f4\u11f5\u11f6\u11f7\u11f8\u11f9\u1200\u1201\u1202\u1203\u1204\u1205\u1206\u1207\u1208\u1209\u120a\u120b\u120c\u120d\u120e\u120f\u1210\u1211\u1212\u1213\u1214\u1215\u1216\u1217\u1218\u1219\u121a\u121b\u121c\u121d\u121e\u121f\u1220\u1221\u1222\u1223\u1224\u1225\u1226\u1227\u1228\u1229\u122a\u122b\u122c\u122d\u122e\u122f\u1230\u1231\u1232\u1233\u1234\u1235\u1236\u1237\u1238\u1239\u123a\u123b\u123c\u123d\u123e\u123f\u1240\u1241\u1242\u1243\u1244\u1245\u1246\u1247\u1248\u124a\u124b\u124c\u124d\u1250\u1251\u1252\u1253\u1254\u1255\u1256\u1258\u125a\u125b\u125c\u125d\u1260\u1261\u1262\u1263\u1264\u1265\u1266\u1267\u1268\u1269\u126a\u126b\u126c\u126d\u126e\u126f\u1270\u1271\u1272\u1273\u1274\u1275\u1276\u1277\u1278\u1279\u127a\u127b\u127c\u127d\u127e\u127f\u1280\u1281\u1282\u1283\u1284\u1285\u1286\u1287\u1288\u128a\u128b\u128c\u128d\u1290\u1291\u1292\u1293\u1294\u1295\u1296\u1297\u1298\u1299\u129a\u129b\u129c\u129d\u129e\u129f\u12a0\u12a1\u12a2\u12a3\u12a4\u12a5\u12a6\u12a7\u12a8\u12a9\u12aa\u12ab\u12ac\u12ad\u12ae\u12af\u12b0\u12b2\u12b3\u12b4\u12b5\u12b8\u12b9\u12ba\u12bb\u12bc\u12bd\u12be\u12c0\u12c2\u12c3\u12c4\u12c5\u12c8\u12c9\u12ca\u12cb\u12cc\u12cd\u12ce\u12cf\u12d0\u12d1\u12d2\u12d3\u12d4\u12d5\u12d6\u12d8\u12d9\u12da\u12db\u12dc\u12dd\u12de\u12df\u12e0\u12e1\u12e2\u12e3\u12e4\u12e5\u12e6\u12e7\u12e8\u12e9\u12ea\u12eb\u12ec\u12ed\u12ee\u12ef\u12f0\u12f1\u12f2\u12f3\u12f4\u12f5\u12f6\u12f7\u12f8\u12f9\u12fa\u12fb\u12fc\u12fd\u12fe\u12ff\u1300\u1301\u1302\u1303\u1304\u1305\u1306\u1307\u1308\u1309\u130a\u130b\u130c\u130d\u130e\u130f\u1310\u1312\u1313\u1314\u1315\u1318\u1319\u131a\u131b\u131c\u131d\u131e\u131f\u1320\u1321\u1322\u1323\u1324\u1325\u1326\u1327\u1328\u1329\u132a\u132b\u132c\u132d\u132e\u132f\u1330\u1331\u1332\u1333\u1334\u1335\u1336\u1337\u1338\u1339\u133a\u133b\u133c\u133d\u133e\u133f\u1340\u1341\u1342\u1343\u1344\u1345\u1346\u1347\u1348\u1349\u134a\u134b\u134c\u134d\u134e\u134f\u1350\u1351\u1352\u1353\u1354\u1355\u1356\u1357\u1358\u1359\u135a\u1380\u1381\u1382\u1383\u1384\u1385\u1386\u1387\u1388\u1389\u138a\u138b\u138c\u138d\u138e\u138f\u13a0\u13a1\u13a2\u13a3\u13a4\u13a5\u13a6\u13a7\u13a8\u13a9\u13aa\u13ab\u13ac\u13ad\u13ae\u13af\u13b0\u13b1\u13b2\u13b3\u13b4\u13b5\u13b6\u13b7\u13b8\u13b9\u13ba\u13bb\u13bc\u13bd\u13be\u13bf\u13c0\u13c1\u13c2\u13c3\u13c4\u13c5\u13c6\u13c7\u13c8\u13c9\u13ca\u13cb\u13cc\u13cd\u13ce\u13cf\u13d0\u13d1\u13d2\u13d3\u13d4\u13d5\u13d6\u13d7\u13d8\u13d9\u13da\u13db\u13dc\u13dd\u13de\u13df\u13e0\u13e1\u13e2\u13e3\u13e4\u13e5\u13e6\u13e7\u13e8\u13e9\u13ea\u13eb\u13ec\u13ed\u13ee\u13ef\u13f0\u13f1\u13f2\u13f3\u13f4\u1401\u1402\u1403\u1404\u1405\u1406\u1407\u1408\u1409\u140a\u140b\u140c\u140d\u140e\u140f\u1410\u1411\u1412\u1413\u1414\u1415\u1416\u1417\u1418\u1419\u141a\u141b\u141c\u141d\u141e\u141f\u1420\u1421\u1422\u1423\u1424\u1425\u1426\u1427\u1428\u1429\u142a\u142b\u142c\u142d\u142e\u142f\u1430\u1431\u1432\u1433\u1434\u1435\u1436\u1437\u1438\u1439\u143a\u143b\u143c\u143d\u143e\u143f\u1440\u1441\u1442\u1443\u1444\u1445\u1446\u1447\u1448\u1449\u144a\u144b\u144c\u144d\u144e\u144f\u1450\u1451\u1452\u1453\u1454\u1455\u1456\u1457\u1458\u1459\u145a\u145b\u145c\u145d\u145e\u145f\u1460\u1461\u1462\u1463\u1464\u1465\u1466\u1467\u1468\u1469\u146a\u146b\u146c\u146d\u146e\u146f\u1470\u1471\u1472\u1473\u1474\u1475\u1476\u1477\u1478\u1479\u147a\u147b\u147c\u147d\u147e\u147f\u1480\u1481\u1482\u1483\u1484\u1485\u1486\u1487\u1488\u1489\u148a\u148b\u148c\u148d\u148e\u148f\u1490\u1491\u1492\u1493\u1494\u1495\u1496\u1497\u1498\u1499\u149a\u149b\u149c\u149d\u149e\u149f\u14a0\u14a1\u14a2\u14a3\u14a4\u14a5\u14a6\u14a7\u14a8\u14a9\u14aa\u14ab\u14ac\u14ad\u14ae\u14af\u14b0\u14b1\u14b2\u14b3\u14b4\u14b5\u14b6\u14b7\u14b8\u14b9\u14ba\u14bb\u14bc\u14bd\u14be\u14bf\u14c0\u14c1\u14c2\u14c3\u14c4\u14c5\u14c6\u14c7\u14c8\u14c9\u14ca\u14cb\u14cc\u14cd\u14ce\u14cf\u14d0\u14d1\u14d2\u14d3\u14d4\u14d5\u14d6\u14d7\u14d8\u14d9\u14da\u14db\u14dc\u14dd\u14de\u14df\u14e0\u14e1\u14e2\u14e3\u14e4\u14e5\u14e6\u14e7\u14e8\u14e9\u14ea\u14eb\u14ec\u14ed\u14ee\u14ef\u14f0\u14f1\u14f2\u14f3\u14f4\u14f5\u14f6\u14f7\u14f8\u14f9\u14fa\u14fb\u14fc\u14fd\u14fe\u14ff\u1500\u1501\u1502\u1503\u1504\u1505\u1506\u1507\u1508\u1509\u150a\u150b\u150c\u150d\u150e\u150f\u1510\u1511\u1512\u1513\u1514\u1515\u1516\u1517\u1518\u1519\u151a\u151b\u151c\u151d\u151e\u151f\u1520\u1521\u1522\u1523\u1524\u1525\u1526\u1527\u1528\u1529\u152a\u152b\u152c\u152d\u152e\u152f\u1530\u1531\u1532\u1533\u1534\u1535\u1536\u1537\u1538\u1539\u153a\u153b\u153c\u153d\u153e\u153f\u1540\u1541\u1542\u1543\u1544\u1545\u1546\u1547\u1548\u1549\u154a\u154b\u154c\u154d\u154e\u154f\u1550\u1551\u1552\u1553\u1554\u1555\u1556\u1557\u1558\u1559\u155a\u155b\u155c\u155d\u155e\u155f\u1560\u1561\u1562\u1563\u1564\u1565\u1566\u1567\u1568\u1569\u156a\u156b\u156c\u156d\u156e\u156f\u1570\u1571\u1572\u1573\u1574\u1575\u1576\u1577\u1578\u1579\u157a\u157b\u157c\u157d\u157e\u157f\u1580\u1581\u1582\u1583\u1584\u1585\u1586\u1587\u1588\u1589\u158a\u158b\u158c\u158d\u158e\u158f\u1590\u1591\u1592\u1593\u1594\u1595\u1596\u1597\u1598\u1599\u159a\u159b\u159c\u159d\u159e\u159f\u15a0\u15a1\u15a2\u15a3\u15a4\u15a5\u15a6\u15a7\u15a8\u15a9\u15aa\u15ab\u15ac\u15ad\u15ae\u15af\u15b0\u15b1\u15b2\u15b3\u15b4\u15b5\u15b6\u15b7\u15b8\u15b9\u15ba\u15bb\u15bc\u15bd\u15be\u15bf\u15c0\u15c1\u15c2\u15c3\u15c4\u15c5\u15c6\u15c7\u15c8\u15c9\u15ca\u15cb\u15cc\u15cd\u15ce\u15cf\u15d0\u15d1\u15d2\u15d3\u15d4\u15d5\u15d6\u15d7\u15d8\u15d9\u15da\u15db\u15dc\u15dd\u15de\u15df\u15e0\u15e1\u15e2\u15e3\u15e4\u15e5\u15e6\u15e7\u15e8\u15e9\u15ea\u15eb\u15ec\u15ed\u15ee\u15ef\u15f0\u15f1\u15f2\u15f3\u15f4\u15f5\u15f6\u15f7\u15f8\u15f9\u15fa\u15fb\u15fc\u15fd\u15fe\u15ff\u1600\u1601\u1602\u1603\u1604\u1605\u1606\u1607\u1608\u1609\u160a\u160b\u160c\u160d\u160e\u160f\u1610\u1611\u1612\u1613\u1614\u1615\u1616\u1617\u1618\u1619\u161a\u161b\u161c\u161d\u161e\u161f\u1620\u1621\u1622\u1623\u1624\u1625\u1626\u1627\u1628\u1629\u162a\u162b\u162c\u162d\u162e\u162f\u1630\u1631\u1632\u1633\u1634\u1635\u1636\u1637\u1638\u1639\u163a\u163b\u163c\u163d\u163e\u163f\u1640\u1641\u1642\u1643\u1644\u1645\u1646\u1647\u1648\u1649\u164a\u164b\u164c\u164d\u164e\u164f\u1650\u1651\u1652\u1653\u1654\u1655\u1656\u1657\u1658\u1659\u165a\u165b\u165c\u165d\u165e\u165f\u1660\u1661\u1662\u1663\u1664\u1665\u1666\u1667\u1668\u1669\u166a\u166b\u166c\u166f\u1670\u1671\u1672\u1673\u1674\u1675\u1676\u1681\u1682\u1683\u1684\u1685\u1686\u1687\u1688\u1689\u168a\u168b\u168c\u168d\u168e\u168f\u1690\u1691\u1692\u1693\u1694\u1695\u1696\u1697\u1698\u1699\u169a\u16a0\u16a1\u16a2\u16a3\u16a4\u16a5\u16a6\u16a7\u16a8\u16a9\u16aa\u16ab\u16ac\u16ad\u16ae\u16af\u16b0\u16b1\u16b2\u16b3\u16b4\u16b5\u16b6\u16b7\u16b8\u16b9\u16ba\u16bb\u16bc\u16bd\u16be\u16bf\u16c0\u16c1\u16c2\u16c3\u16c4\u16c5\u16c6\u16c7\u16c8\u16c9\u16ca\u16cb\u16cc\u16cd\u16ce\u16cf\u16d0\u16d1\u16d2\u16d3\u16d4\u16d5\u16d6\u16d7\u16d8\u16d9\u16da\u16db\u16dc\u16dd\u16de\u16df\u16e0\u16e1\u16e2\u16e3\u16e4\u16e5\u16e6\u16e7\u16e8\u16e9\u16ea\u1700\u1701\u1702\u1703\u1704\u1705\u1706\u1707\u1708\u1709\u170a\u170b\u170c\u170e\u170f\u1710\u1711\u1720\u1721\u1722\u1723\u1724\u1725\u1726\u1727\u1728\u1729\u172a\u172b\u172c\u172d\u172e\u172f\u1730\u1731\u1740\u1741\u1742\u1743\u1744\u1745\u1746\u1747\u1748\u1749\u174a\u174b\u174c\u174d\u174e\u174f\u1750\u1751\u1760\u1761\u1762\u1763\u1764\u1765\u1766\u1767\u1768\u1769\u176a\u176b\u176c\u176e\u176f\u1770\u1780\u1781\u1782\u1783\u1784\u1785\u1786\u1787\u1788\u1789\u178a\u178b\u178c\u178d\u178e\u178f\u1790\u1791\u1792\u1793\u1794\u1795\u1796\u1797\u1798\u1799\u179a\u179b\u179c\u179d\u179e\u179f\u17a0\u17a1\u17a2\u17a3\u17a4\u17a5\u17a6\u17a7\u17a8\u17a9\u17aa\u17ab\u17ac\u17ad\u17ae\u17af\u17b0\u17b1\u17b2\u17b3\u17dc\u1820\u1821\u1822\u1823\u1824\u1825\u1826\u1827\u1828\u1829\u182a\u182b\u182c\u182d\u182e\u182f\u1830\u1831\u1832\u1833\u1834\u1835\u1836\u1837\u1838\u1839\u183a\u183b\u183c\u183d\u183e\u183f\u1840\u1841\u1842\u1844\u1845\u1846\u1847\u1848\u1849\u184a\u184b\u184c\u184d\u184e\u184f\u1850\u1851\u1852\u1853\u1854\u1855\u1856\u1857\u1858\u1859\u185a\u185b\u185c\u185d\u185e\u185f\u1860\u1861\u1862\u1863\u1864\u1865\u1866\u1867\u1868\u1869\u186a\u186b\u186c\u186d\u186e\u186f\u1870\u1871\u1872\u1873\u1874\u1875\u1876\u1877\u1880\u1881\u1882\u1883\u1884\u1885\u1886\u1887\u1888\u1889\u188a\u188b\u188c\u188d\u188e\u188f\u1890\u1891\u1892\u1893\u1894\u1895\u1896\u1897\u1898\u1899\u189a\u189b\u189c\u189d\u189e\u189f\u18a0\u18a1\u18a2\u18a3\u18a4\u18a5\u18a6\u18a7\u18a8\u1900\u1901\u1902\u1903\u1904\u1905\u1906\u1907\u1908\u1909\u190a\u190b\u190c\u190d\u190e\u190f\u1910\u1911\u1912\u1913\u1914\u1915\u1916\u1917\u1918\u1919\u191a\u191b\u191c\u1950\u1951\u1952\u1953\u1954\u1955\u1956\u1957\u1958\u1959\u195a\u195b\u195c\u195d\u195e\u195f\u1960\u1961\u1962\u1963\u1964\u1965\u1966\u1967\u1968\u1969\u196a\u196b\u196c\u196d\u1970\u1971\u1972\u1973\u1974\u1980\u1981\u1982\u1983\u1984\u1985\u1986\u1987\u1988\u1989\u198a\u198b\u198c\u198d\u198e\u198f\u1990\u1991\u1992\u1993\u1994\u1995\u1996\u1997\u1998\u1999\u199a\u199b\u199c\u199d\u199e\u199f\u19a0\u19a1\u19a2\u19a3\u19a4\u19a5\u19a6\u19a7\u19a8\u19a9\u19c1\u19c2\u19c3\u19c4\u19c5\u19c6\u19c7\u1a00\u1a01\u1a02\u1a03\u1a04\u1a05\u1a06\u1a07\u1a08\u1a09\u1a0a\u1a0b\u1a0c\u1a0d\u1a0e\u1a0f\u1a10\u1a11\u1a12\u1a13\u1a14\u1a15\u1a16\u2135\u2136\u2137\u2138\u2d30\u2d31\u2d32\u2d33\u2d34\u2d35\u2d36\u2d37\u2d38\u2d39\u2d3a\u2d3b\u2d3c\u2d3d\u2d3e\u2d3f\u2d40\u2d41\u2d42\u2d43\u2d44\u2d45\u2d46\u2d47\u2d48\u2d49\u2d4a\u2d4b\u2d4c\u2d4d\u2d4e\u2d4f\u2d50\u2d51\u2d52\u2d53\u2d54\u2d55\u2d56\u2d57\u2d58\u2d59\u2d5a\u2d5b\u2d5c\u2d5d\u2d5e\u2d5f\u2d60\u2d61\u2d62\u2d63\u2d64\u2d65\u2d80\u2d81\u2d82\u2d83\u2d84\u2d85\u2d86\u2d87\u2d88\u2d89\u2d8a\u2d8b\u2d8c\u2d8d\u2d8e\u2d8f\u2d90\u2d91\u2d92\u2d93\u2d94\u2d95\u2d96\u2da0\u2da1\u2da2\u2da3\u2da4\u2da5\u2da6\u2da8\u2da9\u2daa\u2dab\u2dac\u2dad\u2dae\u2db0\u2db1\u2db2\u2db3\u2db4\u2db5\u2db6\u2db8\u2db9\u2dba\u2dbb\u2dbc\u2dbd\u2dbe\u2dc0\u2dc1\u2dc2\u2dc3\u2dc4\u2dc5\u2dc6\u2dc8\u2dc9\u2dca\u2dcb\u2dcc\u2dcd\u2dce\u2dd0\u2dd1\u2dd2\u2dd3\u2dd4\u2dd5\u2dd6\u2dd8\u2dd9\u2dda\u2ddb\u2ddc\u2ddd\u2dde\u3006\u303c\u3041\u3042\u3043\u3044\u3045\u3046\u3047\u3048\u3049\u304a\u304b\u304c\u304d\u304e\u304f\u3050\u3051\u3052\u3053\u3054\u3055\u3056\u3057\u3058\u3059\u305a\u305b\u305c\u305d\u305e\u305f\u3060\u3061\u3062\u3063\u3064\u3065\u3066\u3067\u3068\u3069\u306a\u306b\u306c\u306d\u306e\u306f\u3070\u3071\u3072\u3073\u3074\u3075\u3076\u3077\u3078\u3079\u307a\u307b\u307c\u307d\u307e\u307f\u3080\u3081\u3082\u3083\u3084\u3085\u3086\u3087\u3088\u3089\u308a\u308b\u308c\u308d\u308e\u308f\u3090\u3091\u3092\u3093\u3094\u3095\u3096\u309f\u30a1\u30a2\u30a3\u30a4\u30a5\u30a6\u30a7\u30a8\u30a9\u30aa\u30ab\u30ac\u30ad\u30ae\u30af\u30b0\u30b1\u30b2\u30b3\u30b4\u30b5\u30b6\u30b7\u30b8\u30b9\u30ba\u30bb\u30bc\u30bd\u30be\u30bf\u30c0\u30c1\u30c2\u30c3\u30c4\u30c5\u30c6\u30c7\u30c8\u30c9\u30ca\u30cb\u30cc\u30cd\u30ce\u30cf\u30d0\u30d1\u30d2\u30d3\u30d4\u30d5\u30d6\u30d7\u30d8\u30d9\u30da\u30db\u30dc\u30dd\u30de\u30df\u30e0\u30e1\u30e2\u30e3\u30e4\u30e5\u30e6\u30e7\u30e8\u30e9\u30ea\u30eb\u30ec\u30ed\u30ee\u30ef\u30f0\u30f1\u30f2\u30f3\u30f4\u30f5\u30f6\u30f7\u30f8\u30f9\u30fa\u30ff\u3105\u3106\u3107\u3108\u3109\u310a\u310b\u310c\u310d\u310e\u310f\u3110\u3111\u3112\u3113\u3114\u3115\u3116\u3117\u3118\u3119\u311a\u311b\u311c\u311d\u311e\u311f\u3120\u3121\u3122\u3123\u3124\u3125\u3126\u3127\u3128\u3129\u312a\u312b\u312c\u3131\u3132\u3133\u3134\u3135\u3136\u3137\u3138\u3139\u313a\u313b\u313c\u313d\u313e\u313f\u3140\u3141\u3142\u3143\u3144\u3145\u3146\u3147\u3148\u3149\u314a\u314b\u314c\u314d\u314e\u314f\u3150\u3151\u3152\u3153\u3154\u3155\u3156\u3157\u3158\u3159\u315a\u315b\u315c\u315d\u315e\u315f\u3160\u3161\u3162\u3163\u3164\u3165\u3166\u3167\u3168\u3169\u316a\u316b\u316c\u316d\u316e\u316f\u3170\u3171\u3172\u3173\u3174\u3175\u3176\u3177\u3178\u3179\u317a\u317b\u317c\u317d\u317e\u317f\u3180\u3181\u3182\u3183\u3184\u3185\u3186\u3187\u3188\u3189\u318a\u318b\u318c\u318d\u318e\u31a0\u31a1\u31a2\u31a3\u31a4\u31a5\u31a6\u31a7\u31a8\u31a9\u31aa\u31ab\u31ac\u31ad\u31ae\u31af\u31b0\u31b1\u31b2\u31b3\u31b4\u31b5\u31b6\u31b7\u31f0\u31f1\u31f2\u31f3\u31f4\u31f5\u31f6\u31f7\u31f8\u31f9\u31fa\u31fb\u31fc\u31fd\u31fe\u31ff\u3400\u3401\u3402\u3403\u3404\u3405\u3406\u3407\u3408\u3409\u340a\u340b\u340c\u340d\u340e\u340f\u3410\u3411\u3412\u3413\u3414\u3415\u3416\u3417\u3418\u3419\u341a\u341b\u341c\u341d\u341e\u341f\u3420\u3421\u3422\u3423\u3424\u3425\u3426\u3427\u3428\u3429\u342a\u342b\u342c\u342d\u342e\u342f\u3430\u3431\u3432\u3433\u3434\u3435\u3436\u3437\u3438\u3439\u343a\u343b\u343c\u343d\u343e\u343f\u3440\u3441\u3442\u3443\u3444\u3445\u3446\u3447\u3448\u3449\u344a\u344b\u344c\u344d\u344e\u344f\u3450\u3451\u3452\u3453\u3454\u3455\u3456\u3457\u3458\u3459\u345a\u345b\u345c\u345d\u345e\u345f\u3460\u3461\u3462\u3463\u3464\u3465\u3466\u3467\u3468\u3469\u346a\u346b\u346c\u346d\u346e\u346f\u3470\u3471\u3472\u3473\u3474\u3475\u3476\u3477\u3478\u3479\u347a\u347b\u347c\u347d\u347e\u347f\u3480\u3481\u3482\u3483\u3484\u3485\u3486\u3487\u3488\u3489\u348a\u348b\u348c\u348d\u348e\u348f\u3490\u3491\u3492\u3493\u3494\u3495\u3496\u3497\u3498\u3499\u349a\u349b\u349c\u349d\u349e\u349f\u34a0\u34a1\u34a2\u34a3\u34a4\u34a5\u34a6\u34a7\u34a8\u34a9\u34aa\u34ab\u34ac\u34ad\u34ae\u34af\u34b0\u34b1\u34b2\u34b3\u34b4\u34b5\u34b6\u34b7\u34b8\u34b9\u34ba\u34bb\u34bc\u34bd\u34be\u34bf\u34c0\u34c1\u34c2\u34c3\u34c4\u34c5\u34c6\u34c7\u34c8\u34c9\u34ca\u34cb\u34cc\u34cd\u34ce\u34cf\u34d0\u34d1\u34d2\u34d3\u34d4\u34d5\u34d6\u34d7\u34d8\u34d9\u34da\u34db\u34dc\u34dd\u34de\u34df\u34e0\u34e1\u34e2\u34e3\u34e4\u34e5\u34e6\u34e7\u34e8\u34e9\u34ea\u34eb\u34ec\u34ed\u34ee\u34ef\u34f0\u34f1\u34f2\u34f3\u34f4\u34f5\u34f6\u34f7\u34f8\u34f9\u34fa\u34fb\u34fc\u34fd\u34fe\u34ff\u3500\u3501\u3502\u3503\u3504\u3505\u3506\u3507\u3508\u3509\u350a\u350b\u350c\u350d\u350e\u350f\u3510\u3511\u3512\u3513\u3514\u3515\u3516\u3517\u3518\u3519\u351a\u351b\u351c\u351d\u351e\u351f\u3520\u3521\u3522\u3523\u3524\u3525\u3526\u3527\u3528\u3529\u352a\u352b\u352c\u352d\u352e\u352f\u3530\u3531\u3532\u3533\u3534\u3535\u3536\u3537\u3538\u3539\u353a\u353b\u353c\u353d\u353e\u353f\u3540\u3541\u3542\u3543\u3544\u3545\u3546\u3547\u3548\u3549\u354a\u354b\u354c\u354d\u354e\u354f\u3550\u3551\u3552\u3553\u3554\u3555\u3556\u3557\u3558\u3559\u355a\u355b\u355c\u355d\u355e\u355f\u3560\u3561\u3562\u3563\u3564\u3565\u3566\u3567\u3568\u3569\u356a\u356b\u356c\u356d\u356e\u356f\u3570\u3571\u3572\u3573\u3574\u3575\u3576\u3577\u3578\u3579\u357a\u357b\u357c\u357d\u357e\u357f\u3580\u3581\u3582\u3583\u3584\u3585\u3586\u3587\u3588\u3589\u358a\u358b\u358c\u358d\u358e\u358f\u3590\u3591\u3592\u3593\u3594\u3595\u3596\u3597\u3598\u3599\u359a\u359b\u359c\u359d\u359e\u359f\u35a0\u35a1\u35a2\u35a3\u35a4\u35a5\u35a6\u35a7\u35a8\u35a9\u35aa\u35ab\u35ac\u35ad\u35ae\u35af\u35b0\u35b1\u35b2\u35b3\u35b4\u35b5\u35b6\u35b7\u35b8\u35b9\u35ba\u35bb\u35bc\u35bd\u35be\u35bf\u35c0\u35c1\u35c2\u35c3\u35c4\u35c5\u35c6\u35c7\u35c8\u35c9\u35ca\u35cb\u35cc\u35cd\u35ce\u35cf\u35d0\u35d1\u35d2\u35d3\u35d4\u35d5\u35d6\u35d7\u35d8\u35d9\u35da\u35db\u35dc\u35dd\u35de\u35df\u35e0\u35e1\u35e2\u35e3\u35e4\u35e5\u35e6\u35e7\u35e8\u35e9\u35ea\u35eb\u35ec\u35ed\u35ee\u35ef\u35f0\u35f1\u35f2\u35f3\u35f4\u35f5\u35f6\u35f7\u35f8\u35f9\u35fa\u35fb\u35fc\u35fd\u35fe\u35ff\u3600\u3601\u3602\u3603\u3604\u3605\u3606\u3607\u3608\u3609\u360a\u360b\u360c\u360d\u360e\u360f\u3610\u3611\u3612\u3613\u3614\u3615\u3616\u3617\u3618\u3619\u361a\u361b\u361c\u361d\u361e\u361f\u3620\u3621\u3622\u3623\u3624\u3625\u3626\u3627\u3628\u3629\u362a\u362b\u362c\u362d\u362e\u362f\u3630\u3631\u3632\u3633\u3634\u3635\u3636\u3637\u3638\u3639\u363a\u363b\u363c\u363d\u363e\u363f\u3640\u3641\u3642\u3643\u3644\u3645\u3646\u3647\u3648\u3649\u364a\u364b\u364c\u364d\u364e\u364f\u3650\u3651\u3652\u3653\u3654\u3655\u3656\u3657\u3658\u3659\u365a\u365b\u365c\u365d\u365e\u365f\u3660\u3661\u3662\u3663\u3664\u3665\u3666\u3667\u3668\u3669\u366a\u366b\u366c\u366d\u366e\u366f\u3670\u3671\u3672\u3673\u3674\u3675\u3676\u3677\u3678\u3679\u367a\u367b\u367c\u367d\u367e\u367f\u3680\u3681\u3682\u3683\u3684\u3685\u3686\u3687\u3688\u3689\u368a\u368b\u368c\u368d\u368e\u368f\u3690\u3691\u3692\u3693\u3694\u3695\u3696\u3697\u3698\u3699\u369a\u369b\u369c\u369d\u369e\u369f\u36a0\u36a1\u36a2\u36a3\u36a4\u36a5\u36a6\u36a7\u36a8\u36a9\u36aa\u36ab\u36ac\u36ad\u36ae\u36af\u36b0\u36b1\u36b2\u36b3\u36b4\u36b5\u36b6\u36b7\u36b8\u36b9\u36ba\u36bb\u36bc\u36bd\u36be\u36bf\u36c0\u36c1\u36c2\u36c3\u36c4\u36c5\u36c6\u36c7\u36c8\u36c9\u36ca\u36cb\u36cc\u36cd\u36ce\u36cf\u36d0\u36d1\u36d2\u36d3\u36d4\u36d5\u36d6\u36d7\u36d8\u36d9\u36da\u36db\u36dc\u36dd\u36de\u36df\u36e0\u36e1\u36e2\u36e3\u36e4\u36e5\u36e6\u36e7\u36e8\u36e9\u36ea\u36eb\u36ec\u36ed\u36ee\u36ef\u36f0\u36f1\u36f2\u36f3\u36f4\u36f5\u36f6\u36f7\u36f8\u36f9\u36fa\u36fb\u36fc\u36fd\u36fe\u36ff\u3700\u3701\u3702\u3703\u3704\u3705\u3706\u3707\u3708\u3709\u370a\u370b\u370c\u370d\u370e\u370f\u3710\u3711\u3712\u3713\u3714\u3715\u3716\u3717\u3718\u3719\u371a\u371b\u371c\u371d\u371e\u371f\u3720\u3721\u3722\u3723\u3724\u3725\u3726\u3727\u3728\u3729\u372a\u372b\u372c\u372d\u372e\u372f\u3730\u3731\u3732\u3733\u3734\u3735\u3736\u3737\u3738\u3739\u373a\u373b\u373c\u373d\u373e\u373f\u3740\u3741\u3742\u3743\u3744\u3745\u3746\u3747\u3748\u3749\u374a\u374b\u374c\u374d\u374e\u374f\u3750\u3751\u3752\u3753\u3754\u3755\u3756\u3757\u3758\u3759\u375a\u375b\u375c\u375d\u375e\u375f\u3760\u3761\u3762\u3763\u3764\u3765\u3766\u3767\u3768\u3769\u376a\u376b\u376c\u376d\u376e\u376f\u3770\u3771\u3772\u3773\u3774\u3775\u3776\u3777\u3778\u3779\u377a\u377b\u377c\u377d\u377e\u377f\u3780\u3781\u3782\u3783\u3784\u3785\u3786\u3787\u3788\u3789\u378a\u378b\u378c\u378d\u378e\u378f\u3790\u3791\u3792\u3793\u3794\u3795\u3796\u3797\u3798\u3799\u379a\u379b\u379c\u379d\u379e\u379f\u37a0\u37a1\u37a2\u37a3\u37a4\u37a5\u37a6\u37a7\u37a8\u37a9\u37aa\u37ab\u37ac\u37ad\u37ae\u37af\u37b0\u37b1\u37b2\u37b3\u37b4\u37b5\u37b6\u37b7\u37b8\u37b9\u37ba\u37bb\u37bc\u37bd\u37be\u37bf\u37c0\u37c1\u37c2\u37c3\u37c4\u37c5\u37c6\u37c7\u37c8\u37c9\u37ca\u37cb\u37cc\u37cd\u37ce\u37cf\u37d0\u37d1\u37d2\u37d3\u37d4\u37d5\u37d6\u37d7\u37d8\u37d9\u37da\u37db\u37dc\u37dd\u37de\u37df\u37e0\u37e1\u37e2\u37e3\u37e4\u37e5\u37e6\u37e7\u37e8\u37e9\u37ea\u37eb\u37ec\u37ed\u37ee\u37ef\u37f0\u37f1\u37f2\u37f3\u37f4\u37f5\u37f6\u37f7\u37f8\u37f9\u37fa\u37fb\u37fc\u37fd\u37fe\u37ff\u3800\u3801\u3802\u3803\u3804\u3805\u3806\u3807\u3808\u3809\u380a\u380b\u380c\u380d\u380e\u380f\u3810\u3811\u3812\u3813\u3814\u3815\u3816\u3817\u3818\u3819\u381a\u381b\u381c\u381d\u381e\u381f\u3820\u3821\u3822\u3823\u3824\u3825\u3826\u3827\u3828\u3829\u382a\u382b\u382c\u382d\u382e\u382f\u3830\u3831\u3832\u3833\u3834\u3835\u3836\u3837\u3838\u3839\u383a\u383b\u383c\u383d\u383e\u383f\u3840\u3841\u3842\u3843\u3844\u3845\u3846\u3847\u3848\u3849\u384a\u384b\u384c\u384d\u384e\u384f\u3850\u3851\u3852\u3853\u3854\u3855\u3856\u3857\u3858\u3859\u385a\u385b\u385c\u385d\u385e\u385f\u3860\u3861\u3862\u3863\u3864\u3865\u3866\u3867\u3868\u3869\u386a\u386b\u386c\u386d\u386e\u386f\u3870\u3871\u3872\u3873\u3874\u3875\u3876\u3877\u3878\u3879\u387a\u387b\u387c\u387d\u387e\u387f\u3880\u3881\u3882\u3883\u3884\u3885\u3886\u3887\u3888\u3889\u388a\u388b\u388c\u388d\u388e\u388f\u3890\u3891\u3892\u3893\u3894\u3895\u3896\u3897\u3898\u3899\u389a\u389b\u389c\u389d\u389e\u389f\u38a0\u38a1\u38a2\u38a3\u38a4\u38a5\u38a6\u38a7\u38a8\u38a9\u38aa\u38ab\u38ac\u38ad\u38ae\u38af\u38b0\u38b1\u38b2\u38b3\u38b4\u38b5\u38b6\u38b7\u38b8\u38b9\u38ba\u38bb\u38bc\u38bd\u38be\u38bf\u38c0\u38c1\u38c2\u38c3\u38c4\u38c5\u38c6\u38c7\u38c8\u38c9\u38ca\u38cb\u38cc\u38cd\u38ce\u38cf\u38d0\u38d1\u38d2\u38d3\u38d4\u38d5\u38d6\u38d7\u38d8\u38d9\u38da\u38db\u38dc\u38dd\u38de\u38df\u38e0\u38e1\u38e2\u38e3\u38e4\u38e5\u38e6\u38e7\u38e8\u38e9\u38ea\u38eb\u38ec\u38ed\u38ee\u38ef\u38f0\u38f1\u38f2\u38f3\u38f4\u38f5\u38f6\u38f7\u38f8\u38f9\u38fa\u38fb\u38fc\u38fd\u38fe\u38ff\u3900\u3901\u3902\u3903\u3904\u3905\u3906\u3907\u3908\u3909\u390a\u390b\u390c\u390d\u390e\u390f\u3910\u3911\u3912\u3913\u3914\u3915\u3916\u3917\u3918\u3919\u391a\u391b\u391c\u391d\u391e\u391f\u3920\u3921\u3922\u3923\u3924\u3925\u3926\u3927\u3928\u3929\u392a\u392b\u392c\u392d\u392e\u392f\u3930\u3931\u3932\u3933\u3934\u3935\u3936\u3937\u3938\u3939\u393a\u393b\u393c\u393d\u393e\u393f\u3940\u3941\u3942\u3943\u3944\u3945\u3946\u3947\u3948\u3949\u394a\u394b\u394c\u394d\u394e\u394f\u3950\u3951\u3952\u3953\u3954\u3955\u3956\u3957\u3958\u3959\u395a\u395b\u395c\u395d\u395e\u395f\u3960\u3961\u3962\u3963\u3964\u3965\u3966\u3967\u3968\u3969\u396a\u396b\u396c\u396d\u396e\u396f\u3970\u3971\u3972\u3973\u3974\u3975\u3976\u3977\u3978\u3979\u397a\u397b\u397c\u397d\u397e\u397f\u3980\u3981\u3982\u3983\u3984\u3985\u3986\u3987\u3988\u3989\u398a\u398b\u398c\u398d\u398e\u398f\u3990\u3991\u3992\u3993\u3994\u3995\u3996\u3997\u3998\u3999\u399a\u399b\u399c\u399d\u399e\u399f\u39a0\u39a1\u39a2\u39a3\u39a4\u39a5\u39a6\u39a7\u39a8\u39a9\u39aa\u39ab\u39ac\u39ad\u39ae\u39af\u39b0\u39b1\u39b2\u39b3\u39b4\u39b5\u39b6\u39b7\u39b8\u39b9\u39ba\u39bb\u39bc\u39bd\u39be\u39bf\u39c0\u39c1\u39c2\u39c3\u39c4\u39c5\u39c6\u39c7\u39c8\u39c9\u39ca\u39cb\u39cc\u39cd\u39ce\u39cf\u39d0\u39d1\u39d2\u39d3\u39d4\u39d5\u39d6\u39d7\u39d8\u39d9\u39da\u39db\u39dc\u39dd\u39de\u39df\u39e0\u39e1\u39e2\u39e3\u39e4\u39e5\u39e6\u39e7\u39e8\u39e9\u39ea\u39eb\u39ec\u39ed\u39ee\u39ef\u39f0\u39f1\u39f2\u39f3\u39f4\u39f5\u39f6\u39f7\u39f8\u39f9\u39fa\u39fb\u39fc\u39fd\u39fe\u39ff\u3a00\u3a01\u3a02\u3a03\u3a04\u3a05\u3a06\u3a07\u3a08\u3a09\u3a0a\u3a0b\u3a0c\u3a0d\u3a0e\u3a0f\u3a10\u3a11\u3a12\u3a13\u3a14\u3a15\u3a16\u3a17\u3a18\u3a19\u3a1a\u3a1b\u3a1c\u3a1d\u3a1e\u3a1f\u3a20\u3a21\u3a22\u3a23\u3a24\u3a25\u3a26\u3a27\u3a28\u3a29\u3a2a\u3a2b\u3a2c\u3a2d\u3a2e\u3a2f\u3a30\u3a31\u3a32\u3a33\u3a34\u3a35\u3a36\u3a37\u3a38\u3a39\u3a3a\u3a3b\u3a3c\u3a3d\u3a3e\u3a3f\u3a40\u3a41\u3a42\u3a43\u3a44\u3a45\u3a46\u3a47\u3a48\u3a49\u3a4a\u3a4b\u3a4c\u3a4d\u3a4e\u3a4f\u3a50\u3a51\u3a52\u3a53\u3a54\u3a55\u3a56\u3a57\u3a58\u3a59\u3a5a\u3a5b\u3a5c\u3a5d\u3a5e\u3a5f\u3a60\u3a61\u3a62\u3a63\u3a64\u3a65\u3a66\u3a67\u3a68\u3a69\u3a6a\u3a6b\u3a6c\u3a6d\u3a6e\u3a6f\u3a70\u3a71\u3a72\u3a73\u3a74\u3a75\u3a76\u3a77\u3a78\u3a79\u3a7a\u3a7b\u3a7c\u3a7d\u3a7e\u3a7f\u3a80\u3a81\u3a82\u3a83\u3a84\u3a85\u3a86\u3a87\u3a88\u3a89\u3a8a\u3a8b\u3a8c\u3a8d\u3a8e\u3a8f\u3a90\u3a91\u3a92\u3a93\u3a94\u3a95\u3a96\u3a97\u3a98\u3a99\u3a9a\u3a9b\u3a9c\u3a9d\u3a9e\u3a9f\u3aa0\u3aa1\u3aa2\u3aa3\u3aa4\u3aa5\u3aa6\u3aa7\u3aa8\u3aa9\u3aaa\u3aab\u3aac\u3aad\u3aae\u3aaf\u3ab0\u3ab1\u3ab2\u3ab3\u3ab4\u3ab5\u3ab6\u3ab7\u3ab8\u3ab9\u3aba\u3abb\u3abc\u3abd\u3abe\u3abf\u3ac0\u3ac1\u3ac2\u3ac3\u3ac4\u3ac5\u3ac6\u3ac7\u3ac8\u3ac9\u3aca\u3acb\u3acc\u3acd\u3ace\u3acf\u3ad0\u3ad1\u3ad2\u3ad3\u3ad4\u3ad5\u3ad6\u3ad7\u3ad8\u3ad9\u3ada\u3adb\u3adc\u3add\u3ade\u3adf\u3ae0\u3ae1\u3ae2\u3ae3\u3ae4\u3ae5\u3ae6\u3ae7\u3ae8\u3ae9\u3aea\u3aeb\u3aec\u3aed\u3aee\u3aef\u3af0\u3af1\u3af2\u3af3\u3af4\u3af5\u3af6\u3af7\u3af8\u3af9\u3afa\u3afb\u3afc\u3afd\u3afe\u3aff\u3b00\u3b01\u3b02\u3b03\u3b04\u3b05\u3b06\u3b07\u3b08\u3b09\u3b0a\u3b0b\u3b0c\u3b0d\u3b0e\u3b0f\u3b10\u3b11\u3b12\u3b13\u3b14\u3b15\u3b16\u3b17\u3b18\u3b19\u3b1a\u3b1b\u3b1c\u3b1d\u3b1e\u3b1f\u3b20\u3b21\u3b22\u3b23\u3b24\u3b25\u3b26\u3b27\u3b28\u3b29\u3b2a\u3b2b\u3b2c\u3b2d\u3b2e\u3b2f\u3b30\u3b31\u3b32\u3b33\u3b34\u3b35\u3b36\u3b37\u3b38\u3b39\u3b3a\u3b3b\u3b3c\u3b3d\u3b3e\u3b3f\u3b40\u3b41\u3b42\u3b43\u3b44\u3b45\u3b46\u3b47\u3b48\u3b49\u3b4a\u3b4b\u3b4c\u3b4d\u3b4e\u3b4f\u3b50\u3b51\u3b52\u3b53\u3b54\u3b55\u3b56\u3b57\u3b58\u3b59\u3b5a\u3b5b\u3b5c\u3b5d\u3b5e\u3b5f\u3b60\u3b61\u3b62\u3b63\u3b64\u3b65\u3b66\u3b67\u3b68\u3b69\u3b6a\u3b6b\u3b6c\u3b6d\u3b6e\u3b6f\u3b70\u3b71\u3b72\u3b73\u3b74\u3b75\u3b76\u3b77\u3b78\u3b79\u3b7a\u3b7b\u3b7c\u3b7d\u3b7e\u3b7f\u3b80\u3b81\u3b82\u3b83\u3b84\u3b85\u3b86\u3b87\u3b88\u3b89\u3b8a\u3b8b\u3b8c\u3b8d\u3b8e\u3b8f\u3b90\u3b91\u3b92\u3b93\u3b94\u3b95\u3b96\u3b97\u3b98\u3b99\u3b9a\u3b9b\u3b9c\u3b9d\u3b9e\u3b9f\u3ba0\u3ba1\u3ba2\u3ba3\u3ba4\u3ba5\u3ba6\u3ba7\u3ba8\u3ba9\u3baa\u3bab\u3bac\u3bad\u3bae\u3baf\u3bb0\u3bb1\u3bb2\u3bb3\u3bb4\u3bb5\u3bb6\u3bb7\u3bb8\u3bb9\u3bba\u3bbb\u3bbc\u3bbd\u3bbe\u3bbf\u3bc0\u3bc1\u3bc2\u3bc3\u3bc4\u3bc5\u3bc6\u3bc7\u3bc8\u3bc9\u3bca\u3bcb\u3bcc\u3bcd\u3bce\u3bcf\u3bd0\u3bd1\u3bd2\u3bd3\u3bd4\u3bd5\u3bd6\u3bd7\u3bd8\u3bd9\u3bda\u3bdb\u3bdc\u3bdd\u3bde\u3bdf\u3be0\u3be1\u3be2\u3be3\u3be4\u3be5\u3be6\u3be7\u3be8\u3be9\u3bea\u3beb\u3bec\u3bed\u3bee\u3bef\u3bf0\u3bf1\u3bf2\u3bf3\u3bf4\u3bf5\u3bf6\u3bf7\u3bf8\u3bf9\u3bfa\u3bfb\u3bfc\u3bfd\u3bfe\u3bff\u3c00\u3c01\u3c02\u3c03\u3c04\u3c05\u3c06\u3c07\u3c08\u3c09\u3c0a\u3c0b\u3c0c\u3c0d\u3c0e\u3c0f\u3c10\u3c11\u3c12\u3c13\u3c14\u3c15\u3c16\u3c17\u3c18\u3c19\u3c1a\u3c1b\u3c1c\u3c1d\u3c1e\u3c1f\u3c20\u3c21\u3c22\u3c23\u3c24\u3c25\u3c26\u3c27\u3c28\u3c29\u3c2a\u3c2b\u3c2c\u3c2d\u3c2e\u3c2f\u3c30\u3c31\u3c32\u3c33\u3c34\u3c35\u3c36\u3c37\u3c38\u3c39\u3c3a\u3c3b\u3c3c\u3c3d\u3c3e\u3c3f\u3c40\u3c41\u3c42\u3c43\u3c44\u3c45\u3c46\u3c47\u3c48\u3c49\u3c4a\u3c4b\u3c4c\u3c4d\u3c4e\u3c4f\u3c50\u3c51\u3c52\u3c53\u3c54\u3c55\u3c56\u3c57\u3c58\u3c59\u3c5a\u3c5b\u3c5c\u3c5d\u3c5e\u3c5f\u3c60\u3c61\u3c62\u3c63\u3c64\u3c65\u3c66\u3c67\u3c68\u3c69\u3c6a\u3c6b\u3c6c\u3c6d\u3c6e\u3c6f\u3c70\u3c71\u3c72\u3c73\u3c74\u3c75\u3c76\u3c77\u3c78\u3c79\u3c7a\u3c7b\u3c7c\u3c7d\u3c7e\u3c7f\u3c80\u3c81\u3c82\u3c83\u3c84\u3c85\u3c86\u3c87\u3c88\u3c89\u3c8a\u3c8b\u3c8c\u3c8d\u3c8e\u3c8f\u3c90\u3c91\u3c92\u3c93\u3c94\u3c95\u3c96\u3c97\u3c98\u3c99\u3c9a\u3c9b\u3c9c\u3c9d\u3c9e\u3c9f\u3ca0\u3ca1\u3ca2\u3ca3\u3ca4\u3ca5\u3ca6\u3ca7\u3ca8\u3ca9\u3caa\u3cab\u3cac\u3cad\u3cae\u3caf\u3cb0\u3cb1\u3cb2\u3cb3\u3cb4\u3cb5\u3cb6\u3cb7\u3cb8\u3cb9\u3cba\u3cbb\u3cbc\u3cbd\u3cbe\u3cbf\u3cc0\u3cc1\u3cc2\u3cc3\u3cc4\u3cc5\u3cc6\u3cc7\u3cc8\u3cc9\u3cca\u3ccb\u3ccc\u3ccd\u3cce\u3ccf\u3cd0\u3cd1\u3cd2\u3cd3\u3cd4\u3cd5\u3cd6\u3cd7\u3cd8\u3cd9\u3cda\u3cdb\u3cdc\u3cdd\u3cde\u3cdf\u3ce0\u3ce1\u3ce2\u3ce3\u3ce4\u3ce5\u3ce6\u3ce7\u3ce8\u3ce9\u3cea\u3ceb\u3cec\u3ced\u3cee\u3cef\u3cf0\u3cf1\u3cf2\u3cf3\u3cf4\u3cf5\u3cf6\u3cf7\u3cf8\u3cf9\u3cfa\u3cfb\u3cfc\u3cfd\u3cfe\u3cff\u3d00\u3d01\u3d02\u3d03\u3d04\u3d05\u3d06\u3d07\u3d08\u3d09\u3d0a\u3d0b\u3d0c\u3d0d\u3d0e\u3d0f\u3d10\u3d11\u3d12\u3d13\u3d14\u3d15\u3d16\u3d17\u3d18\u3d19\u3d1a\u3d1b\u3d1c\u3d1d\u3d1e\u3d1f\u3d20\u3d21\u3d22\u3d23\u3d24\u3d25\u3d26\u3d27\u3d28\u3d29\u3d2a\u3d2b\u3d2c\u3d2d\u3d2e\u3d2f\u3d30\u3d31\u3d32\u3d33\u3d34\u3d35\u3d36\u3d37\u3d38\u3d39\u3d3a\u3d3b\u3d3c\u3d3d\u3d3e\u3d3f\u3d40\u3d41\u3d42\u3d43\u3d44\u3d45\u3d46\u3d47\u3d48\u3d49\u3d4a\u3d4b\u3d4c\u3d4d\u3d4e\u3d4f\u3d50\u3d51\u3d52\u3d53\u3d54\u3d55\u3d56\u3d57\u3d58\u3d59\u3d5a\u3d5b\u3d5c\u3d5d\u3d5e\u3d5f\u3d60\u3d61\u3d62\u3d63\u3d64\u3d65\u3d66\u3d67\u3d68\u3d69\u3d6a\u3d6b\u3d6c\u3d6d\u3d6e\u3d6f\u3d70\u3d71\u3d72\u3d73\u3d74\u3d75\u3d76\u3d77\u3d78\u3d79\u3d7a\u3d7b\u3d7c\u3d7d\u3d7e\u3d7f\u3d80\u3d81\u3d82\u3d83\u3d84\u3d85\u3d86\u3d87\u3d88\u3d89\u3d8a\u3d8b\u3d8c\u3d8d\u3d8e\u3d8f\u3d90\u3d91\u3d92\u3d93\u3d94\u3d95\u3d96\u3d97\u3d98\u3d99\u3d9a\u3d9b\u3d9c\u3d9d\u3d9e\u3d9f\u3da0\u3da1\u3da2\u3da3\u3da4\u3da5\u3da6\u3da7\u3da8\u3da9\u3daa\u3dab\u3dac\u3dad\u3dae\u3daf\u3db0\u3db1\u3db2\u3db3\u3db4\u3db5\u3db6\u3db7\u3db8\u3db9\u3dba\u3dbb\u3dbc\u3dbd\u3dbe\u3dbf\u3dc0\u3dc1\u3dc2\u3dc3\u3dc4\u3dc5\u3dc6\u3dc7\u3dc8\u3dc9\u3dca\u3dcb\u3dcc\u3dcd\u3dce\u3dcf\u3dd0\u3dd1\u3dd2\u3dd3\u3dd4\u3dd5\u3dd6\u3dd7\u3dd8\u3dd9\u3dda\u3ddb\u3ddc\u3ddd\u3dde\u3ddf\u3de0\u3de1\u3de2\u3de3\u3de4\u3de5\u3de6\u3de7\u3de8\u3de9\u3dea\u3deb\u3dec\u3ded\u3dee\u3def\u3df0\u3df1\u3df2\u3df3\u3df4\u3df5\u3df6\u3df7\u3df8\u3df9\u3dfa\u3dfb\u3dfc\u3dfd\u3dfe\u3dff\u3e00\u3e01\u3e02\u3e03\u3e04\u3e05\u3e06\u3e07\u3e08\u3e09\u3e0a\u3e0b\u3e0c\u3e0d\u3e0e\u3e0f\u3e10\u3e11\u3e12\u3e13\u3e14\u3e15\u3e16\u3e17\u3e18\u3e19\u3e1a\u3e1b\u3e1c\u3e1d\u3e1e\u3e1f\u3e20\u3e21\u3e22\u3e23\u3e24\u3e25\u3e26\u3e27\u3e28\u3e29\u3e2a\u3e2b\u3e2c\u3e2d\u3e2e\u3e2f\u3e30\u3e31\u3e32\u3e33\u3e34\u3e35\u3e36\u3e37\u3e38\u3e39\u3e3a\u3e3b\u3e3c\u3e3d\u3e3e\u3e3f\u3e40\u3e41\u3e42\u3e43\u3e44\u3e45\u3e46\u3e47\u3e48\u3e49\u3e4a\u3e4b\u3e4c\u3e4d\u3e4e\u3e4f\u3e50\u3e51\u3e52\u3e53\u3e54\u3e55\u3e56\u3e57\u3e58\u3e59\u3e5a\u3e5b\u3e5c\u3e5d\u3e5e\u3e5f\u3e60\u3e61\u3e62\u3e63\u3e64\u3e65\u3e66\u3e67\u3e68\u3e69\u3e6a\u3e6b\u3e6c\u3e6d\u3e6e\u3e6f\u3e70\u3e71\u3e72\u3e73\u3e74\u3e75\u3e76\u3e77\u3e78\u3e79\u3e7a\u3e7b\u3e7c\u3e7d\u3e7e\u3e7f\u3e80\u3e81\u3e82\u3e83\u3e84\u3e85\u3e86\u3e87\u3e88\u3e89\u3e8a\u3e8b\u3e8c\u3e8d\u3e8e\u3e8f\u3e90\u3e91\u3e92\u3e93\u3e94\u3e95\u3e96\u3e97\u3e98\u3e99\u3e9a\u3e9b\u3e9c\u3e9d\u3e9e\u3e9f\u3ea0\u3ea1\u3ea2\u3ea3\u3ea4\u3ea5\u3ea6\u3ea7\u3ea8\u3ea9\u3eaa\u3eab\u3eac\u3ead\u3eae\u3eaf\u3eb0\u3eb1\u3eb2\u3eb3\u3eb4\u3eb5\u3eb6\u3eb7\u3eb8\u3eb9\u3eba\u3ebb\u3ebc\u3ebd\u3ebe\u3ebf\u3ec0\u3ec1\u3ec2\u3ec3\u3ec4\u3ec5\u3ec6\u3ec7\u3ec8\u3ec9\u3eca\u3ecb\u3ecc\u3ecd\u3ece\u3ecf\u3ed0\u3ed1\u3ed2\u3ed3\u3ed4\u3ed5\u3ed6\u3ed7\u3ed8\u3ed9\u3eda\u3edb\u3edc\u3edd\u3ede\u3edf\u3ee0\u3ee1\u3ee2\u3ee3\u3ee4\u3ee5\u3ee6\u3ee7\u3ee8\u3ee9\u3eea\u3eeb\u3eec\u3eed\u3eee\u3eef\u3ef0\u3ef1\u3ef2\u3ef3\u3ef4\u3ef5\u3ef6\u3ef7\u3ef8\u3ef9\u3efa\u3efb\u3efc\u3efd\u3efe\u3eff\u3f00\u3f01\u3f02\u3f03\u3f04\u3f05\u3f06\u3f07\u3f08\u3f09\u3f0a\u3f0b\u3f0c\u3f0d\u3f0e\u3f0f\u3f10\u3f11\u3f12\u3f13\u3f14\u3f15\u3f16\u3f17\u3f18\u3f19\u3f1a\u3f1b\u3f1c\u3f1d\u3f1e\u3f1f\u3f20\u3f21\u3f22\u3f23\u3f24\u3f25\u3f26\u3f27\u3f28\u3f29\u3f2a\u3f2b\u3f2c\u3f2d\u3f2e\u3f2f\u3f30\u3f31\u3f32\u3f33\u3f34\u3f35\u3f36\u3f37\u3f38\u3f39\u3f3a\u3f3b\u3f3c\u3f3d\u3f3e\u3f3f\u3f40\u3f41\u3f42\u3f43\u3f44\u3f45\u3f46\u3f47\u3f48\u3f49\u3f4a\u3f4b\u3f4c\u3f4d\u3f4e\u3f4f\u3f50\u3f51\u3f52\u3f53\u3f54\u3f55\u3f56\u3f57\u3f58\u3f59\u3f5a\u3f5b\u3f5c\u3f5d\u3f5e\u3f5f\u3f60\u3f61\u3f62\u3f63\u3f64\u3f65\u3f66\u3f67\u3f68\u3f69\u3f6a\u3f6b\u3f6c\u3f6d\u3f6e\u3f6f\u3f70\u3f71\u3f72\u3f73\u3f74\u3f75\u3f76\u3f77\u3f78\u3f79\u3f7a\u3f7b\u3f7c\u3f7d\u3f7e\u3f7f\u3f80\u3f81\u3f82\u3f83\u3f84\u3f85\u3f86\u3f87\u3f88\u3f89\u3f8a\u3f8b\u3f8c\u3f8d\u3f8e\u3f8f\u3f90\u3f91\u3f92\u3f93\u3f94\u3f95\u3f96\u3f97\u3f98\u3f99\u3f9a\u3f9b\u3f9c\u3f9d\u3f9e\u3f9f\u3fa0\u3fa1\u3fa2\u3fa3\u3fa4\u3fa5\u3fa6\u3fa7\u3fa8\u3fa9\u3faa\u3fab\u3fac\u3fad\u3fae\u3faf\u3fb0\u3fb1\u3fb2\u3fb3\u3fb4\u3fb5\u3fb6\u3fb7\u3fb8\u3fb9\u3fba\u3fbb\u3fbc\u3fbd\u3fbe\u3fbf\u3fc0\u3fc1\u3fc2\u3fc3\u3fc4\u3fc5\u3fc6\u3fc7\u3fc8\u3fc9\u3fca\u3fcb\u3fcc\u3fcd\u3fce\u3fcf\u3fd0\u3fd1\u3fd2\u3fd3\u3fd4\u3fd5\u3fd6\u3fd7\u3fd8\u3fd9\u3fda\u3fdb\u3fdc\u3fdd\u3fde\u3fdf\u3fe0\u3fe1\u3fe2\u3fe3\u3fe4\u3fe5\u3fe6\u3fe7\u3fe8\u3fe9\u3fea\u3feb\u3fec\u3fed\u3fee\u3fef\u3ff0\u3ff1\u3ff2\u3ff3\u3ff4\u3ff5\u3ff6\u3ff7\u3ff8\u3ff9\u3ffa\u3ffb\u3ffc\u3ffd\u3ffe\u3fff\u4000\u4001\u4002\u4003\u4004\u4005\u4006\u4007\u4008\u4009\u400a\u400b\u400c\u400d\u400e\u400f\u4010\u4011\u4012\u4013\u4014\u4015\u4016\u4017\u4018\u4019\u401a\u401b\u401c\u401d\u401e\u401f\u4020\u4021\u4022\u4023\u4024\u4025\u4026\u4027\u4028\u4029\u402a\u402b\u402c\u402d\u402e\u402f\u4030\u4031\u4032\u4033\u4034\u4035\u4036\u4037\u4038\u4039\u403a\u403b\u403c\u403d\u403e\u403f\u4040\u4041\u4042\u4043\u4044\u4045\u4046\u4047\u4048\u4049\u404a\u404b\u404c\u404d\u404e\u404f\u4050\u4051\u4052\u4053\u4054\u4055\u4056\u4057\u4058\u4059\u405a\u405b\u405c\u405d\u405e\u405f\u4060\u4061\u4062\u4063\u4064\u4065\u4066\u4067\u4068\u4069\u406a\u406b\u406c\u406d\u406e\u406f\u4070\u4071\u4072\u4073\u4074\u4075\u4076\u4077\u4078\u4079\u407a\u407b\u407c\u407d\u407e\u407f\u4080\u4081\u4082\u4083\u4084\u4085\u4086\u4087\u4088\u4089\u408a\u408b\u408c\u408d\u408e\u408f\u4090\u4091\u4092\u4093\u4094\u4095\u4096\u4097\u4098\u4099\u409a\u409b\u409c\u409d\u409e\u409f\u40a0\u40a1\u40a2\u40a3\u40a4\u40a5\u40a6\u40a7\u40a8\u40a9\u40aa\u40ab\u40ac\u40ad\u40ae\u40af\u40b0\u40b1\u40b2\u40b3\u40b4\u40b5\u40b6\u40b7\u40b8\u40b9\u40ba\u40bb\u40bc\u40bd\u40be\u40bf\u40c0\u40c1\u40c2\u40c3\u40c4\u40c5\u40c6\u40c7\u40c8\u40c9\u40ca\u40cb\u40cc\u40cd\u40ce\u40cf\u40d0\u40d1\u40d2\u40d3\u40d4\u40d5\u40d6\u40d7\u40d8\u40d9\u40da\u40db\u40dc\u40dd\u40de\u40df\u40e0\u40e1\u40e2\u40e3\u40e4\u40e5\u40e6\u40e7\u40e8\u40e9\u40ea\u40eb\u40ec\u40ed\u40ee\u40ef\u40f0\u40f1\u40f2\u40f3\u40f4\u40f5\u40f6\u40f7\u40f8\u40f9\u40fa\u40fb\u40fc\u40fd\u40fe\u40ff\u4100\u4101\u4102\u4103\u4104\u4105\u4106\u4107\u4108\u4109\u410a\u410b\u410c\u410d\u410e\u410f\u4110\u4111\u4112\u4113\u4114\u4115\u4116\u4117\u4118\u4119\u411a\u411b\u411c\u411d\u411e\u411f\u4120\u4121\u4122\u4123\u4124\u4125\u4126\u4127\u4128\u4129\u412a\u412b\u412c\u412d\u412e\u412f\u4130\u4131\u4132\u4133\u4134\u4135\u4136\u4137\u4138\u4139\u413a\u413b\u413c\u413d\u413e\u413f\u4140\u4141\u4142\u4143\u4144\u4145\u4146\u4147\u4148\u4149\u414a\u414b\u414c\u414d\u414e\u414f\u4150\u4151\u4152\u4153\u4154\u4155\u4156\u4157\u4158\u4159\u415a\u415b\u415c\u415d\u415e\u415f\u4160\u4161\u4162\u4163\u4164\u4165\u4166\u4167\u4168\u4169\u416a\u416b\u416c\u416d\u416e\u416f\u4170\u4171\u4172\u4173\u4174\u4175\u4176\u4177\u4178\u4179\u417a\u417b\u417c\u417d\u417e\u417f\u4180\u4181\u4182\u4183\u4184\u4185\u4186\u4187\u4188\u4189\u418a\u418b\u418c\u418d\u418e\u418f\u4190\u4191\u4192\u4193\u4194\u4195\u4196\u4197\u4198\u4199\u419a\u419b\u419c\u419d\u419e\u419f\u41a0\u41a1\u41a2\u41a3\u41a4\u41a5\u41a6\u41a7\u41a8\u41a9\u41aa\u41ab\u41ac\u41ad\u41ae\u41af\u41b0\u41b1\u41b2\u41b3\u41b4\u41b5\u41b6\u41b7\u41b8\u41b9\u41ba\u41bb\u41bc\u41bd\u41be\u41bf\u41c0\u41c1\u41c2\u41c3\u41c4\u41c5\u41c6\u41c7\u41c8\u41c9\u41ca\u41cb\u41cc\u41cd\u41ce\u41cf\u41d0\u41d1\u41d2\u41d3\u41d4\u41d5\u41d6\u41d7\u41d8\u41d9\u41da\u41db\u41dc\u41dd\u41de\u41df\u41e0\u41e1\u41e2\u41e3\u41e4\u41e5\u41e6\u41e7\u41e8\u41e9\u41ea\u41eb\u41ec\u41ed\u41ee\u41ef\u41f0\u41f1\u41f2\u41f3\u41f4\u41f5\u41f6\u41f7\u41f8\u41f9\u41fa\u41fb\u41fc\u41fd\u41fe\u41ff\u4200\u4201\u4202\u4203\u4204\u4205\u4206\u4207\u4208\u4209\u420a\u420b\u420c\u420d\u420e\u420f\u4210\u4211\u4212\u4213\u4214\u4215\u4216\u4217\u4218\u4219\u421a\u421b\u421c\u421d\u421e\u421f\u4220\u4221\u4222\u4223\u4224\u4225\u4226\u4227\u4228\u4229\u422a\u422b\u422c\u422d\u422e\u422f\u4230\u4231\u4232\u4233\u4234\u4235\u4236\u4237\u4238\u4239\u423a\u423b\u423c\u423d\u423e\u423f\u4240\u4241\u4242\u4243\u4244\u4245\u4246\u4247\u4248\u4249\u424a\u424b\u424c\u424d\u424e\u424f\u4250\u4251\u4252\u4253\u4254\u4255\u4256\u4257\u4258\u4259\u425a\u425b\u425c\u425d\u425e\u425f\u4260\u4261\u4262\u4263\u4264\u4265\u4266\u4267\u4268\u4269\u426a\u426b\u426c\u426d\u426e\u426f\u4270\u4271\u4272\u4273\u4274\u4275\u4276\u4277\u4278\u4279\u427a\u427b\u427c\u427d\u427e\u427f\u4280\u4281\u4282\u4283\u4284\u4285\u4286\u4287\u4288\u4289\u428a\u428b\u428c\u428d\u428e\u428f\u4290\u4291\u4292\u4293\u4294\u4295\u4296\u4297\u4298\u4299\u429a\u429b\u429c\u429d\u429e\u429f\u42a0\u42a1\u42a2\u42a3\u42a4\u42a5\u42a6\u42a7\u42a8\u42a9\u42aa\u42ab\u42ac\u42ad\u42ae\u42af\u42b0\u42b1\u42b2\u42b3\u42b4\u42b5\u42b6\u42b7\u42b8\u42b9\u42ba\u42bb\u42bc\u42bd\u42be\u42bf\u42c0\u42c1\u42c2\u42c3\u42c4\u42c5\u42c6\u42c7\u42c8\u42c9\u42ca\u42cb\u42cc\u42cd\u42ce\u42cf\u42d0\u42d1\u42d2\u42d3\u42d4\u42d5\u42d6\u42d7\u42d8\u42d9\u42da\u42db\u42dc\u42dd\u42de\u42df\u42e0\u42e1\u42e2\u42e3\u42e4\u42e5\u42e6\u42e7\u42e8\u42e9\u42ea\u42eb\u42ec\u42ed\u42ee\u42ef\u42f0\u42f1\u42f2\u42f3\u42f4\u42f5\u42f6\u42f7\u42f8\u42f9\u42fa\u42fb\u42fc\u42fd\u42fe\u42ff\u4300\u4301\u4302\u4303\u4304\u4305\u4306\u4307\u4308\u4309\u430a\u430b\u430c\u430d\u430e\u430f\u4310\u4311\u4312\u4313\u4314\u4315\u4316\u4317\u4318\u4319\u431a\u431b\u431c\u431d\u431e\u431f\u4320\u4321\u4322\u4323\u4324\u4325\u4326\u4327\u4328\u4329\u432a\u432b\u432c\u432d\u432e\u432f\u4330\u4331\u4332\u4333\u4334\u4335\u4336\u4337\u4338\u4339\u433a\u433b\u433c\u433d\u433e\u433f\u4340\u4341\u4342\u4343\u4344\u4345\u4346\u4347\u4348\u4349\u434a\u434b\u434c\u434d\u434e\u434f\u4350\u4351\u4352\u4353\u4354\u4355\u4356\u4357\u4358\u4359\u435a\u435b\u435c\u435d\u435e\u435f\u4360\u4361\u4362\u4363\u4364\u4365\u4366\u4367\u4368\u4369\u436a\u436b\u436c\u436d\u436e\u436f\u4370\u4371\u4372\u4373\u4374\u4375\u4376\u4377\u4378\u4379\u437a\u437b\u437c\u437d\u437e\u437f\u4380\u4381\u4382\u4383\u4384\u4385\u4386\u4387\u4388\u4389\u438a\u438b\u438c\u438d\u438e\u438f\u4390\u4391\u4392\u4393\u4394\u4395\u4396\u4397\u4398\u4399\u439a\u439b\u439c\u439d\u439e\u439f\u43a0\u43a1\u43a2\u43a3\u43a4\u43a5\u43a6\u43a7\u43a8\u43a9\u43aa\u43ab\u43ac\u43ad\u43ae\u43af\u43b0\u43b1\u43b2\u43b3\u43b4\u43b5\u43b6\u43b7\u43b8\u43b9\u43ba\u43bb\u43bc\u43bd\u43be\u43bf\u43c0\u43c1\u43c2\u43c3\u43c4\u43c5\u43c6\u43c7\u43c8\u43c9\u43ca\u43cb\u43cc\u43cd\u43ce\u43cf\u43d0\u43d1\u43d2\u43d3\u43d4\u43d5\u43d6\u43d7\u43d8\u43d9\u43da\u43db\u43dc\u43dd\u43de\u43df\u43e0\u43e1\u43e2\u43e3\u43e4\u43e5\u43e6\u43e7\u43e8\u43e9\u43ea\u43eb\u43ec\u43ed\u43ee\u43ef\u43f0\u43f1\u43f2\u43f3\u43f4\u43f5\u43f6\u43f7\u43f8\u43f9\u43fa\u43fb\u43fc\u43fd\u43fe\u43ff\u4400\u4401\u4402\u4403\u4404\u4405\u4406\u4407\u4408\u4409\u440a\u440b\u440c\u440d\u440e\u440f\u4410\u4411\u4412\u4413\u4414\u4415\u4416\u4417\u4418\u4419\u441a\u441b\u441c\u441d\u441e\u441f\u4420\u4421\u4422\u4423\u4424\u4425\u4426\u4427\u4428\u4429\u442a\u442b\u442c\u442d\u442e\u442f\u4430\u4431\u4432\u4433\u4434\u4435\u4436\u4437\u4438\u4439\u443a\u443b\u443c\u443d\u443e\u443f\u4440\u4441\u4442\u4443\u4444\u4445\u4446\u4447\u4448\u4449\u444a\u444b\u444c\u444d\u444e\u444f\u4450\u4451\u4452\u4453\u4454\u4455\u4456\u4457\u4458\u4459\u445a\u445b\u445c\u445d\u445e\u445f\u4460\u4461\u4462\u4463\u4464\u4465\u4466\u4467\u4468\u4469\u446a\u446b\u446c\u446d\u446e\u446f\u4470\u4471\u4472\u4473\u4474\u4475\u4476\u4477\u4478\u4479\u447a\u447b\u447c\u447d\u447e\u447f\u4480\u4481\u4482\u4483\u4484\u4485\u4486\u4487\u4488\u4489\u448a\u448b\u448c\u448d\u448e\u448f\u4490\u4491\u4492\u4493\u4494\u4495\u4496\u4497\u4498\u4499\u449a\u449b\u449c\u449d\u449e\u449f\u44a0\u44a1\u44a2\u44a3\u44a4\u44a5\u44a6\u44a7\u44a8\u44a9\u44aa\u44ab\u44ac\u44ad\u44ae\u44af\u44b0\u44b1\u44b2\u44b3\u44b4\u44b5\u44b6\u44b7\u44b8\u44b9\u44ba\u44bb\u44bc\u44bd\u44be\u44bf\u44c0\u44c1\u44c2\u44c3\u44c4\u44c5\u44c6\u44c7\u44c8\u44c9\u44ca\u44cb\u44cc\u44cd\u44ce\u44cf\u44d0\u44d1\u44d2\u44d3\u44d4\u44d5\u44d6\u44d7\u44d8\u44d9\u44da\u44db\u44dc\u44dd\u44de\u44df\u44e0\u44e1\u44e2\u44e3\u44e4\u44e5\u44e6\u44e7\u44e8\u44e9\u44ea\u44eb\u44ec\u44ed\u44ee\u44ef\u44f0\u44f1\u44f2\u44f3\u44f4\u44f5\u44f6\u44f7\u44f8\u44f9\u44fa\u44fb\u44fc\u44fd\u44fe\u44ff\u4500\u4501\u4502\u4503\u4504\u4505\u4506\u4507\u4508\u4509\u450a\u450b\u450c\u450d\u450e\u450f\u4510\u4511\u4512\u4513\u4514\u4515\u4516\u4517\u4518\u4519\u451a\u451b\u451c\u451d\u451e\u451f\u4520\u4521\u4522\u4523\u4524\u4525\u4526\u4527\u4528\u4529\u452a\u452b\u452c\u452d\u452e\u452f\u4530\u4531\u4532\u4533\u4534\u4535\u4536\u4537\u4538\u4539\u453a\u453b\u453c\u453d\u453e\u453f\u4540\u4541\u4542\u4543\u4544\u4545\u4546\u4547\u4548\u4549\u454a\u454b\u454c\u454d\u454e\u454f\u4550\u4551\u4552\u4553\u4554\u4555\u4556\u4557\u4558\u4559\u455a\u455b\u455c\u455d\u455e\u455f\u4560\u4561\u4562\u4563\u4564\u4565\u4566\u4567\u4568\u4569\u456a\u456b\u456c\u456d\u456e\u456f\u4570\u4571\u4572\u4573\u4574\u4575\u4576\u4577\u4578\u4579\u457a\u457b\u457c\u457d\u457e\u457f\u4580\u4581\u4582\u4583\u4584\u4585\u4586\u4587\u4588\u4589\u458a\u458b\u458c\u458d\u458e\u458f\u4590\u4591\u4592\u4593\u4594\u4595\u4596\u4597\u4598\u4599\u459a\u459b\u459c\u459d\u459e\u459f\u45a0\u45a1\u45a2\u45a3\u45a4\u45a5\u45a6\u45a7\u45a8\u45a9\u45aa\u45ab\u45ac\u45ad\u45ae\u45af\u45b0\u45b1\u45b2\u45b3\u45b4\u45b5\u45b6\u45b7\u45b8\u45b9\u45ba\u45bb\u45bc\u45bd\u45be\u45bf\u45c0\u45c1\u45c2\u45c3\u45c4\u45c5\u45c6\u45c7\u45c8\u45c9\u45ca\u45cb\u45cc\u45cd\u45ce\u45cf\u45d0\u45d1\u45d2\u45d3\u45d4\u45d5\u45d6\u45d7\u45d8\u45d9\u45da\u45db\u45dc\u45dd\u45de\u45df\u45e0\u45e1\u45e2\u45e3\u45e4\u45e5\u45e6\u45e7\u45e8\u45e9\u45ea\u45eb\u45ec\u45ed\u45ee\u45ef\u45f0\u45f1\u45f2\u45f3\u45f4\u45f5\u45f6\u45f7\u45f8\u45f9\u45fa\u45fb\u45fc\u45fd\u45fe\u45ff\u4600\u4601\u4602\u4603\u4604\u4605\u4606\u4607\u4608\u4609\u460a\u460b\u460c\u460d\u460e\u460f\u4610\u4611\u4612\u4613\u4614\u4615\u4616\u4617\u4618\u4619\u461a\u461b\u461c\u461d\u461e\u461f\u4620\u4621\u4622\u4623\u4624\u4625\u4626\u4627\u4628\u4629\u462a\u462b\u462c\u462d\u462e\u462f\u4630\u4631\u4632\u4633\u4634\u4635\u4636\u4637\u4638\u4639\u463a\u463b\u463c\u463d\u463e\u463f\u4640\u4641\u4642\u4643\u4644\u4645\u4646\u4647\u4648\u4649\u464a\u464b\u464c\u464d\u464e\u464f\u4650\u4651\u4652\u4653\u4654\u4655\u4656\u4657\u4658\u4659\u465a\u465b\u465c\u465d\u465e\u465f\u4660\u4661\u4662\u4663\u4664\u4665\u4666\u4667\u4668\u4669\u466a\u466b\u466c\u466d\u466e\u466f\u4670\u4671\u4672\u4673\u4674\u4675\u4676\u4677\u4678\u4679\u467a\u467b\u467c\u467d\u467e\u467f\u4680\u4681\u4682\u4683\u4684\u4685\u4686\u4687\u4688\u4689\u468a\u468b\u468c\u468d\u468e\u468f\u4690\u4691\u4692\u4693\u4694\u4695\u4696\u4697\u4698\u4699\u469a\u469b\u469c\u469d\u469e\u469f\u46a0\u46a1\u46a2\u46a3\u46a4\u46a5\u46a6\u46a7\u46a8\u46a9\u46aa\u46ab\u46ac\u46ad\u46ae\u46af\u46b0\u46b1\u46b2\u46b3\u46b4\u46b5\u46b6\u46b7\u46b8\u46b9\u46ba\u46bb\u46bc\u46bd\u46be\u46bf\u46c0\u46c1\u46c2\u46c3\u46c4\u46c5\u46c6\u46c7\u46c8\u46c9\u46ca\u46cb\u46cc\u46cd\u46ce\u46cf\u46d0\u46d1\u46d2\u46d3\u46d4\u46d5\u46d6\u46d7\u46d8\u46d9\u46da\u46db\u46dc\u46dd\u46de\u46df\u46e0\u46e1\u46e2\u46e3\u46e4\u46e5\u46e6\u46e7\u46e8\u46e9\u46ea\u46eb\u46ec\u46ed\u46ee\u46ef\u46f0\u46f1\u46f2\u46f3\u46f4\u46f5\u46f6\u46f7\u46f8\u46f9\u46fa\u46fb\u46fc\u46fd\u46fe\u46ff\u4700\u4701\u4702\u4703\u4704\u4705\u4706\u4707\u4708\u4709\u470a\u470b\u470c\u470d\u470e\u470f\u4710\u4711\u4712\u4713\u4714\u4715\u4716\u4717\u4718\u4719\u471a\u471b\u471c\u471d\u471e\u471f\u4720\u4721\u4722\u4723\u4724\u4725\u4726\u4727\u4728\u4729\u472a\u472b\u472c\u472d\u472e\u472f\u4730\u4731\u4732\u4733\u4734\u4735\u4736\u4737\u4738\u4739\u473a\u473b\u473c\u473d\u473e\u473f\u4740\u4741\u4742\u4743\u4744\u4745\u4746\u4747\u4748\u4749\u474a\u474b\u474c\u474d\u474e\u474f\u4750\u4751\u4752\u4753\u4754\u4755\u4756\u4757\u4758\u4759\u475a\u475b\u475c\u475d\u475e\u475f\u4760\u4761\u4762\u4763\u4764\u4765\u4766\u4767\u4768\u4769\u476a\u476b\u476c\u476d\u476e\u476f\u4770\u4771\u4772\u4773\u4774\u4775\u4776\u4777\u4778\u4779\u477a\u477b\u477c\u477d\u477e\u477f\u4780\u4781\u4782\u4783\u4784\u4785\u4786\u4787\u4788\u4789\u478a\u478b\u478c\u478d\u478e\u478f\u4790\u4791\u4792\u4793\u4794\u4795\u4796\u4797\u4798\u4799\u479a\u479b\u479c\u479d\u479e\u479f\u47a0\u47a1\u47a2\u47a3\u47a4\u47a5\u47a6\u47a7\u47a8\u47a9\u47aa\u47ab\u47ac\u47ad\u47ae\u47af\u47b0\u47b1\u47b2\u47b3\u47b4\u47b5\u47b6\u47b7\u47b8\u47b9\u47ba\u47bb\u47bc\u47bd\u47be\u47bf\u47c0\u47c1\u47c2\u47c3\u47c4\u47c5\u47c6\u47c7\u47c8\u47c9\u47ca\u47cb\u47cc\u47cd\u47ce\u47cf\u47d0\u47d1\u47d2\u47d3\u47d4\u47d5\u47d6\u47d7\u47d8\u47d9\u47da\u47db\u47dc\u47dd\u47de\u47df\u47e0\u47e1\u47e2\u47e3\u47e4\u47e5\u47e6\u47e7\u47e8\u47e9\u47ea\u47eb\u47ec\u47ed\u47ee\u47ef\u47f0\u47f1\u47f2\u47f3\u47f4\u47f5\u47f6\u47f7\u47f8\u47f9\u47fa\u47fb\u47fc\u47fd\u47fe\u47ff\u4800\u4801\u4802\u4803\u4804\u4805\u4806\u4807\u4808\u4809\u480a\u480b\u480c\u480d\u480e\u480f\u4810\u4811\u4812\u4813\u4814\u4815\u4816\u4817\u4818\u4819\u481a\u481b\u481c\u481d\u481e\u481f\u4820\u4821\u4822\u4823\u4824\u4825\u4826\u4827\u4828\u4829\u482a\u482b\u482c\u482d\u482e\u482f\u4830\u4831\u4832\u4833\u4834\u4835\u4836\u4837\u4838\u4839\u483a\u483b\u483c\u483d\u483e\u483f\u4840\u4841\u4842\u4843\u4844\u4845\u4846\u4847\u4848\u4849\u484a\u484b\u484c\u484d\u484e\u484f\u4850\u4851\u4852\u4853\u4854\u4855\u4856\u4857\u4858\u4859\u485a\u485b\u485c\u485d\u485e\u485f\u4860\u4861\u4862\u4863\u4864\u4865\u4866\u4867\u4868\u4869\u486a\u486b\u486c\u486d\u486e\u486f\u4870\u4871\u4872\u4873\u4874\u4875\u4876\u4877\u4878\u4879\u487a\u487b\u487c\u487d\u487e\u487f\u4880\u4881\u4882\u4883\u4884\u4885\u4886\u4887\u4888\u4889\u488a\u488b\u488c\u488d\u488e\u488f\u4890\u4891\u4892\u4893\u4894\u4895\u4896\u4897\u4898\u4899\u489a\u489b\u489c\u489d\u489e\u489f\u48a0\u48a1\u48a2\u48a3\u48a4\u48a5\u48a6\u48a7\u48a8\u48a9\u48aa\u48ab\u48ac\u48ad\u48ae\u48af\u48b0\u48b1\u48b2\u48b3\u48b4\u48b5\u48b6\u48b7\u48b8\u48b9\u48ba\u48bb\u48bc\u48bd\u48be\u48bf\u48c0\u48c1\u48c2\u48c3\u48c4\u48c5\u48c6\u48c7\u48c8\u48c9\u48ca\u48cb\u48cc\u48cd\u48ce\u48cf\u48d0\u48d1\u48d2\u48d3\u48d4\u48d5\u48d6\u48d7\u48d8\u48d9\u48da\u48db\u48dc\u48dd\u48de\u48df\u48e0\u48e1\u48e2\u48e3\u48e4\u48e5\u48e6\u48e7\u48e8\u48e9\u48ea\u48eb\u48ec\u48ed\u48ee\u48ef\u48f0\u48f1\u48f2\u48f3\u48f4\u48f5\u48f6\u48f7\u48f8\u48f9\u48fa\u48fb\u48fc\u48fd\u48fe\u48ff\u4900\u4901\u4902\u4903\u4904\u4905\u4906\u4907\u4908\u4909\u490a\u490b\u490c\u490d\u490e\u490f\u4910\u4911\u4912\u4913\u4914\u4915\u4916\u4917\u4918\u4919\u491a\u491b\u491c\u491d\u491e\u491f\u4920\u4921\u4922\u4923\u4924\u4925\u4926\u4927\u4928\u4929\u492a\u492b\u492c\u492d\u492e\u492f\u4930\u4931\u4932\u4933\u4934\u4935\u4936\u4937\u4938\u4939\u493a\u493b\u493c\u493d\u493e\u493f\u4940\u4941\u4942\u4943\u4944\u4945\u4946\u4947\u4948\u4949\u494a\u494b\u494c\u494d\u494e\u494f\u4950\u4951\u4952\u4953\u4954\u4955\u4956\u4957\u4958\u4959\u495a\u495b\u495c\u495d\u495e\u495f\u4960\u4961\u4962\u4963\u4964\u4965\u4966\u4967\u4968\u4969\u496a\u496b\u496c\u496d\u496e\u496f\u4970\u4971\u4972\u4973\u4974\u4975\u4976\u4977\u4978\u4979\u497a\u497b\u497c\u497d\u497e\u497f\u4980\u4981\u4982\u4983\u4984\u4985\u4986\u4987\u4988\u4989\u498a\u498b\u498c\u498d\u498e\u498f\u4990\u4991\u4992\u4993\u4994\u4995\u4996\u4997\u4998\u4999\u499a\u499b\u499c\u499d\u499e\u499f\u49a0\u49a1\u49a2\u49a3\u49a4\u49a5\u49a6\u49a7\u49a8\u49a9\u49aa\u49ab\u49ac\u49ad\u49ae\u49af\u49b0\u49b1\u49b2\u49b3\u49b4\u49b5\u49b6\u49b7\u49b8\u49b9\u49ba\u49bb\u49bc\u49bd\u49be\u49bf\u49c0\u49c1\u49c2\u49c3\u49c4\u49c5\u49c6\u49c7\u49c8\u49c9\u49ca\u49cb\u49cc\u49cd\u49ce\u49cf\u49d0\u49d1\u49d2\u49d3\u49d4\u49d5\u49d6\u49d7\u49d8\u49d9\u49da\u49db\u49dc\u49dd\u49de\u49df\u49e0\u49e1\u49e2\u49e3\u49e4\u49e5\u49e6\u49e7\u49e8\u49e9\u49ea\u49eb\u49ec\u49ed\u49ee\u49ef\u49f0\u49f1\u49f2\u49f3\u49f4\u49f5\u49f6\u49f7\u49f8\u49f9\u49fa\u49fb\u49fc\u49fd\u49fe\u49ff\u4a00\u4a01\u4a02\u4a03\u4a04\u4a05\u4a06\u4a07\u4a08\u4a09\u4a0a\u4a0b\u4a0c\u4a0d\u4a0e\u4a0f\u4a10\u4a11\u4a12\u4a13\u4a14\u4a15\u4a16\u4a17\u4a18\u4a19\u4a1a\u4a1b\u4a1c\u4a1d\u4a1e\u4a1f\u4a20\u4a21\u4a22\u4a23\u4a24\u4a25\u4a26\u4a27\u4a28\u4a29\u4a2a\u4a2b\u4a2c\u4a2d\u4a2e\u4a2f\u4a30\u4a31\u4a32\u4a33\u4a34\u4a35\u4a36\u4a37\u4a38\u4a39\u4a3a\u4a3b\u4a3c\u4a3d\u4a3e\u4a3f\u4a40\u4a41\u4a42\u4a43\u4a44\u4a45\u4a46\u4a47\u4a48\u4a49\u4a4a\u4a4b\u4a4c\u4a4d\u4a4e\u4a4f\u4a50\u4a51\u4a52\u4a53\u4a54\u4a55\u4a56\u4a57\u4a58\u4a59\u4a5a\u4a5b\u4a5c\u4a5d\u4a5e\u4a5f\u4a60\u4a61\u4a62\u4a63\u4a64\u4a65\u4a66\u4a67\u4a68\u4a69\u4a6a\u4a6b\u4a6c\u4a6d\u4a6e\u4a6f\u4a70\u4a71\u4a72\u4a73\u4a74\u4a75\u4a76\u4a77\u4a78\u4a79\u4a7a\u4a7b\u4a7c\u4a7d\u4a7e\u4a7f\u4a80\u4a81\u4a82\u4a83\u4a84\u4a85\u4a86\u4a87\u4a88\u4a89\u4a8a\u4a8b\u4a8c\u4a8d\u4a8e\u4a8f\u4a90\u4a91\u4a92\u4a93\u4a94\u4a95\u4a96\u4a97\u4a98\u4a99\u4a9a\u4a9b\u4a9c\u4a9d\u4a9e\u4a9f\u4aa0\u4aa1\u4aa2\u4aa3\u4aa4\u4aa5\u4aa6\u4aa7\u4aa8\u4aa9\u4aaa\u4aab\u4aac\u4aad\u4aae\u4aaf\u4ab0\u4ab1\u4ab2\u4ab3\u4ab4\u4ab5\u4ab6\u4ab7\u4ab8\u4ab9\u4aba\u4abb\u4abc\u4abd\u4abe\u4abf\u4ac0\u4ac1\u4ac2\u4ac3\u4ac4\u4ac5\u4ac6\u4ac7\u4ac8\u4ac9\u4aca\u4acb\u4acc\u4acd\u4ace\u4acf\u4ad0\u4ad1\u4ad2\u4ad3\u4ad4\u4ad5\u4ad6\u4ad7\u4ad8\u4ad9\u4ada\u4adb\u4adc\u4add\u4ade\u4adf\u4ae0\u4ae1\u4ae2\u4ae3\u4ae4\u4ae5\u4ae6\u4ae7\u4ae8\u4ae9\u4aea\u4aeb\u4aec\u4aed\u4aee\u4aef\u4af0\u4af1\u4af2\u4af3\u4af4\u4af5\u4af6\u4af7\u4af8\u4af9\u4afa\u4afb\u4afc\u4afd\u4afe\u4aff\u4b00\u4b01\u4b02\u4b03\u4b04\u4b05\u4b06\u4b07\u4b08\u4b09\u4b0a\u4b0b\u4b0c\u4b0d\u4b0e\u4b0f\u4b10\u4b11\u4b12\u4b13\u4b14\u4b15\u4b16\u4b17\u4b18\u4b19\u4b1a\u4b1b\u4b1c\u4b1d\u4b1e\u4b1f\u4b20\u4b21\u4b22\u4b23\u4b24\u4b25\u4b26\u4b27\u4b28\u4b29\u4b2a\u4b2b\u4b2c\u4b2d\u4b2e\u4b2f\u4b30\u4b31\u4b32\u4b33\u4b34\u4b35\u4b36\u4b37\u4b38\u4b39\u4b3a\u4b3b\u4b3c\u4b3d\u4b3e\u4b3f\u4b40\u4b41\u4b42\u4b43\u4b44\u4b45\u4b46\u4b47\u4b48\u4b49\u4b4a\u4b4b\u4b4c\u4b4d\u4b4e\u4b4f\u4b50\u4b51\u4b52\u4b53\u4b54\u4b55\u4b56\u4b57\u4b58\u4b59\u4b5a\u4b5b\u4b5c\u4b5d\u4b5e\u4b5f\u4b60\u4b61\u4b62\u4b63\u4b64\u4b65\u4b66\u4b67\u4b68\u4b69\u4b6a\u4b6b\u4b6c\u4b6d\u4b6e\u4b6f\u4b70\u4b71\u4b72\u4b73\u4b74\u4b75\u4b76\u4b77\u4b78\u4b79\u4b7a\u4b7b\u4b7c\u4b7d\u4b7e\u4b7f\u4b80\u4b81\u4b82\u4b83\u4b84\u4b85\u4b86\u4b87\u4b88\u4b89\u4b8a\u4b8b\u4b8c\u4b8d\u4b8e\u4b8f\u4b90\u4b91\u4b92\u4b93\u4b94\u4b95\u4b96\u4b97\u4b98\u4b99\u4b9a\u4b9b\u4b9c\u4b9d\u4b9e\u4b9f\u4ba0\u4ba1\u4ba2\u4ba3\u4ba4\u4ba5\u4ba6\u4ba7\u4ba8\u4ba9\u4baa\u4bab\u4bac\u4bad\u4bae\u4baf\u4bb0\u4bb1\u4bb2\u4bb3\u4bb4\u4bb5\u4bb6\u4bb7\u4bb8\u4bb9\u4bba\u4bbb\u4bbc\u4bbd\u4bbe\u4bbf\u4bc0\u4bc1\u4bc2\u4bc3\u4bc4\u4bc5\u4bc6\u4bc7\u4bc8\u4bc9\u4bca\u4bcb\u4bcc\u4bcd\u4bce\u4bcf\u4bd0\u4bd1\u4bd2\u4bd3\u4bd4\u4bd5\u4bd6\u4bd7\u4bd8\u4bd9\u4bda\u4bdb\u4bdc\u4bdd\u4bde\u4bdf\u4be0\u4be1\u4be2\u4be3\u4be4\u4be5\u4be6\u4be7\u4be8\u4be9\u4bea\u4beb\u4bec\u4bed\u4bee\u4bef\u4bf0\u4bf1\u4bf2\u4bf3\u4bf4\u4bf5\u4bf6\u4bf7\u4bf8\u4bf9\u4bfa\u4bfb\u4bfc\u4bfd\u4bfe\u4bff\u4c00\u4c01\u4c02\u4c03\u4c04\u4c05\u4c06\u4c07\u4c08\u4c09\u4c0a\u4c0b\u4c0c\u4c0d\u4c0e\u4c0f\u4c10\u4c11\u4c12\u4c13\u4c14\u4c15\u4c16\u4c17\u4c18\u4c19\u4c1a\u4c1b\u4c1c\u4c1d\u4c1e\u4c1f\u4c20\u4c21\u4c22\u4c23\u4c24\u4c25\u4c26\u4c27\u4c28\u4c29\u4c2a\u4c2b\u4c2c\u4c2d\u4c2e\u4c2f\u4c30\u4c31\u4c32\u4c33\u4c34\u4c35\u4c36\u4c37\u4c38\u4c39\u4c3a\u4c3b\u4c3c\u4c3d\u4c3e\u4c3f\u4c40\u4c41\u4c42\u4c43\u4c44\u4c45\u4c46\u4c47\u4c48\u4c49\u4c4a\u4c4b\u4c4c\u4c4d\u4c4e\u4c4f\u4c50\u4c51\u4c52\u4c53\u4c54\u4c55\u4c56\u4c57\u4c58\u4c59\u4c5a\u4c5b\u4c5c\u4c5d\u4c5e\u4c5f\u4c60\u4c61\u4c62\u4c63\u4c64\u4c65\u4c66\u4c67\u4c68\u4c69\u4c6a\u4c6b\u4c6c\u4c6d\u4c6e\u4c6f\u4c70\u4c71\u4c72\u4c73\u4c74\u4c75\u4c76\u4c77\u4c78\u4c79\u4c7a\u4c7b\u4c7c\u4c7d\u4c7e\u4c7f\u4c80\u4c81\u4c82\u4c83\u4c84\u4c85\u4c86\u4c87\u4c88\u4c89\u4c8a\u4c8b\u4c8c\u4c8d\u4c8e\u4c8f\u4c90\u4c91\u4c92\u4c93\u4c94\u4c95\u4c96\u4c97\u4c98\u4c99\u4c9a\u4c9b\u4c9c\u4c9d\u4c9e\u4c9f\u4ca0\u4ca1\u4ca2\u4ca3\u4ca4\u4ca5\u4ca6\u4ca7\u4ca8\u4ca9\u4caa\u4cab\u4cac\u4cad\u4cae\u4caf\u4cb0\u4cb1\u4cb2\u4cb3\u4cb4\u4cb5\u4cb6\u4cb7\u4cb8\u4cb9\u4cba\u4cbb\u4cbc\u4cbd\u4cbe\u4cbf\u4cc0\u4cc1\u4cc2\u4cc3\u4cc4\u4cc5\u4cc6\u4cc7\u4cc8\u4cc9\u4cca\u4ccb\u4ccc\u4ccd\u4cce\u4ccf\u4cd0\u4cd1\u4cd2\u4cd3\u4cd4\u4cd5\u4cd6\u4cd7\u4cd8\u4cd9\u4cda\u4cdb\u4cdc\u4cdd\u4cde\u4cdf\u4ce0\u4ce1\u4ce2\u4ce3\u4ce4\u4ce5\u4ce6\u4ce7\u4ce8\u4ce9\u4cea\u4ceb\u4cec\u4ced\u4cee\u4cef\u4cf0\u4cf1\u4cf2\u4cf3\u4cf4\u4cf5\u4cf6\u4cf7\u4cf8\u4cf9\u4cfa\u4cfb\u4cfc\u4cfd\u4cfe\u4cff\u4d00\u4d01\u4d02\u4d03\u4d04\u4d05\u4d06\u4d07\u4d08\u4d09\u4d0a\u4d0b\u4d0c\u4d0d\u4d0e\u4d0f\u4d10\u4d11\u4d12\u4d13\u4d14\u4d15\u4d16\u4d17\u4d18\u4d19\u4d1a\u4d1b\u4d1c\u4d1d\u4d1e\u4d1f\u4d20\u4d21\u4d22\u4d23\u4d24\u4d25\u4d26\u4d27\u4d28\u4d29\u4d2a\u4d2b\u4d2c\u4d2d\u4d2e\u4d2f\u4d30\u4d31\u4d32\u4d33\u4d34\u4d35\u4d36\u4d37\u4d38\u4d39\u4d3a\u4d3b\u4d3c\u4d3d\u4d3e\u4d3f\u4d40\u4d41\u4d42\u4d43\u4d44\u4d45\u4d46\u4d47\u4d48\u4d49\u4d4a\u4d4b\u4d4c\u4d4d\u4d4e\u4d4f\u4d50\u4d51\u4d52\u4d53\u4d54\u4d55\u4d56\u4d57\u4d58\u4d59\u4d5a\u4d5b\u4d5c\u4d5d\u4d5e\u4d5f\u4d60\u4d61\u4d62\u4d63\u4d64\u4d65\u4d66\u4d67\u4d68\u4d69\u4d6a\u4d6b\u4d6c\u4d6d\u4d6e\u4d6f\u4d70\u4d71\u4d72\u4d73\u4d74\u4d75\u4d76\u4d77\u4d78\u4d79\u4d7a\u4d7b\u4d7c\u4d7d\u4d7e\u4d7f\u4d80\u4d81\u4d82\u4d83\u4d84\u4d85\u4d86\u4d87\u4d88\u4d89\u4d8a\u4d8b\u4d8c\u4d8d\u4d8e\u4d8f\u4d90\u4d91\u4d92\u4d93\u4d94\u4d95\u4d96\u4d97\u4d98\u4d99\u4d9a\u4d9b\u4d9c\u4d9d\u4d9e\u4d9f\u4da0\u4da1\u4da2\u4da3\u4da4\u4da5\u4da6\u4da7\u4da8\u4da9\u4daa\u4dab\u4dac\u4dad\u4dae\u4daf\u4db0\u4db1\u4db2\u4db3\u4db4\u4db5\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08\u4e09\u4e0a\u4e0b\u4e0c\u4e0d\u4e0e\u4e0f\u4e10\u4e11\u4e12\u4e13\u4e14\u4e15\u4e16\u4e17\u4e18\u4e19\u4e1a\u4e1b\u4e1c\u4e1d\u4e1e\u4e1f\u4e20\u4e21\u4e22\u4e23\u4e24\u4e25\u4e26\u4e27\u4e28\u4e29\u4e2a\u4e2b\u4e2c\u4e2d\u4e2e\u4e2f\u4e30\u4e31\u4e32\u4e33\u4e34\u4e35\u4e36\u4e37\u4e38\u4e39\u4e3a\u4e3b\u4e3c\u4e3d\u4e3e\u4e3f\u4e40\u4e41\u4e42\u4e43\u4e44\u4e45\u4e46\u4e47\u4e48\u4e49\u4e4a\u4e4b\u4e4c\u4e4d\u4e4e\u4e4f\u4e50\u4e51\u4e52\u4e53\u4e54\u4e55\u4e56\u4e57\u4e58\u4e59\u4e5a\u4e5b\u4e5c\u4e5d\u4e5e\u4e5f\u4e60\u4e61\u4e62\u4e63\u4e64\u4e65\u4e66\u4e67\u4e68\u4e69\u4e6a\u4e6b\u4e6c\u4e6d\u4e6e\u4e6f\u4e70\u4e71\u4e72\u4e73\u4e74\u4e75\u4e76\u4e77\u4e78\u4e79\u4e7a\u4e7b\u4e7c\u4e7d\u4e7e\u4e7f\u4e80\u4e81\u4e82\u4e83\u4e84\u4e85\u4e86\u4e87\u4e88\u4e89\u4e8a\u4e8b\u4e8c\u4e8d\u4e8e\u4e8f\u4e90\u4e91\u4e92\u4e93\u4e94\u4e95\u4e96\u4e97\u4e98\u4e99\u4e9a\u4e9b\u4e9c\u4e9d\u4e9e\u4e9f\u4ea0\u4ea1\u4ea2\u4ea3\u4ea4\u4ea5\u4ea6\u4ea7\u4ea8\u4ea9\u4eaa\u4eab\u4eac\u4ead\u4eae\u4eaf\u4eb0\u4eb1\u4eb2\u4eb3\u4eb4\u4eb5\u4eb6\u4eb7\u4eb8\u4eb9\u4eba\u4ebb\u4ebc\u4ebd\u4ebe\u4ebf\u4ec0\u4ec1\u4ec2\u4ec3\u4ec4\u4ec5\u4ec6\u4ec7\u4ec8\u4ec9\u4eca\u4ecb\u4ecc\u4ecd\u4ece\u4ecf\u4ed0\u4ed1\u4ed2\u4ed3\u4ed4\u4ed5\u4ed6\u4ed7\u4ed8\u4ed9\u4eda\u4edb\u4edc\u4edd\u4ede\u4edf\u4ee0\u4ee1\u4ee2\u4ee3\u4ee4\u4ee5\u4ee6\u4ee7\u4ee8\u4ee9\u4eea\u4eeb\u4eec\u4eed\u4eee\u4eef\u4ef0\u4ef1\u4ef2\u4ef3\u4ef4\u4ef5\u4ef6\u4ef7\u4ef8\u4ef9\u4efa\u4efb\u4efc\u4efd\u4efe\u4eff\u4f00\u4f01\u4f02\u4f03\u4f04\u4f05\u4f06\u4f07\u4f08\u4f09\u4f0a\u4f0b\u4f0c\u4f0d\u4f0e\u4f0f\u4f10\u4f11\u4f12\u4f13\u4f14\u4f15\u4f16\u4f17\u4f18\u4f19\u4f1a\u4f1b\u4f1c\u4f1d\u4f1e\u4f1f\u4f20\u4f21\u4f22\u4f23\u4f24\u4f25\u4f26\u4f27\u4f28\u4f29\u4f2a\u4f2b\u4f2c\u4f2d\u4f2e\u4f2f\u4f30\u4f31\u4f32\u4f33\u4f34\u4f35\u4f36\u4f37\u4f38\u4f39\u4f3a\u4f3b\u4f3c\u4f3d\u4f3e\u4f3f\u4f40\u4f41\u4f42\u4f43\u4f44\u4f45\u4f46\u4f47\u4f48\u4f49\u4f4a\u4f4b\u4f4c\u4f4d\u4f4e\u4f4f\u4f50\u4f51\u4f52\u4f53\u4f54\u4f55\u4f56\u4f57\u4f58\u4f59\u4f5a\u4f5b\u4f5c\u4f5d\u4f5e\u4f5f\u4f60\u4f61\u4f62\u4f63\u4f64\u4f65\u4f66\u4f67\u4f68\u4f69\u4f6a\u4f6b\u4f6c\u4f6d\u4f6e\u4f6f\u4f70\u4f71\u4f72\u4f73\u4f74\u4f75\u4f76\u4f77\u4f78\u4f79\u4f7a\u4f7b\u4f7c\u4f7d\u4f7e\u4f7f\u4f80\u4f81\u4f82\u4f83\u4f84\u4f85\u4f86\u4f87\u4f88\u4f89\u4f8a\u4f8b\u4f8c\u4f8d\u4f8e\u4f8f\u4f90\u4f91\u4f92\u4f93\u4f94\u4f95\u4f96\u4f97\u4f98\u4f99\u4f9a\u4f9b\u4f9c\u4f9d\u4f9e\u4f9f\u4fa0\u4fa1\u4fa2\u4fa3\u4fa4\u4fa5\u4fa6\u4fa7\u4fa8\u4fa9\u4faa\u4fab\u4fac\u4fad\u4fae\u4faf\u4fb0\u4fb1\u4fb2\u4fb3\u4fb4\u4fb5\u4fb6\u4fb7\u4fb8\u4fb9\u4fba\u4fbb\u4fbc\u4fbd\u4fbe\u4fbf\u4fc0\u4fc1\u4fc2\u4fc3\u4fc4\u4fc5\u4fc6\u4fc7\u4fc8\u4fc9\u4fca\u4fcb\u4fcc\u4fcd\u4fce\u4fcf\u4fd0\u4fd1\u4fd2\u4fd3\u4fd4\u4fd5\u4fd6\u4fd7\u4fd8\u4fd9\u4fda\u4fdb\u4fdc\u4fdd\u4fde\u4fdf\u4fe0\u4fe1\u4fe2\u4fe3\u4fe4\u4fe5\u4fe6\u4fe7\u4fe8\u4fe9\u4fea\u4feb\u4fec\u4fed\u4fee\u4fef\u4ff0\u4ff1\u4ff2\u4ff3\u4ff4\u4ff5\u4ff6\u4ff7\u4ff8\u4ff9\u4ffa\u4ffb\u4ffc\u4ffd\u4ffe\u4fff\u5000\u5001\u5002\u5003\u5004\u5005\u5006\u5007\u5008\u5009\u500a\u500b\u500c\u500d\u500e\u500f\u5010\u5011\u5012\u5013\u5014\u5015\u5016\u5017\u5018\u5019\u501a\u501b\u501c\u501d\u501e\u501f\u5020\u5021\u5022\u5023\u5024\u5025\u5026\u5027\u5028\u5029\u502a\u502b\u502c\u502d\u502e\u502f\u5030\u5031\u5032\u5033\u5034\u5035\u5036\u5037\u5038\u5039\u503a\u503b\u503c\u503d\u503e\u503f\u5040\u5041\u5042\u5043\u5044\u5045\u5046\u5047\u5048\u5049\u504a\u504b\u504c\u504d\u504e\u504f\u5050\u5051\u5052\u5053\u5054\u5055\u5056\u5057\u5058\u5059\u505a\u505b\u505c\u505d\u505e\u505f\u5060\u5061\u5062\u5063\u5064\u5065\u5066\u5067\u5068\u5069\u506a\u506b\u506c\u506d\u506e\u506f\u5070\u5071\u5072\u5073\u5074\u5075\u5076\u5077\u5078\u5079\u507a\u507b\u507c\u507d\u507e\u507f\u5080\u5081\u5082\u5083\u5084\u5085\u5086\u5087\u5088\u5089\u508a\u508b\u508c\u508d\u508e\u508f\u5090\u5091\u5092\u5093\u5094\u5095\u5096\u5097\u5098\u5099\u509a\u509b\u509c\u509d\u509e\u509f\u50a0\u50a1\u50a2\u50a3\u50a4\u50a5\u50a6\u50a7\u50a8\u50a9\u50aa\u50ab\u50ac\u50ad\u50ae\u50af\u50b0\u50b1\u50b2\u50b3\u50b4\u50b5\u50b6\u50b7\u50b8\u50b9\u50ba\u50bb\u50bc\u50bd\u50be\u50bf\u50c0\u50c1\u50c2\u50c3\u50c4\u50c5\u50c6\u50c7\u50c8\u50c9\u50ca\u50cb\u50cc\u50cd\u50ce\u50cf\u50d0\u50d1\u50d2\u50d3\u50d4\u50d5\u50d6\u50d7\u50d8\u50d9\u50da\u50db\u50dc\u50dd\u50de\u50df\u50e0\u50e1\u50e2\u50e3\u50e4\u50e5\u50e6\u50e7\u50e8\u50e9\u50ea\u50eb\u50ec\u50ed\u50ee\u50ef\u50f0\u50f1\u50f2\u50f3\u50f4\u50f5\u50f6\u50f7\u50f8\u50f9\u50fa\u50fb\u50fc\u50fd\u50fe\u50ff\u5100\u5101\u5102\u5103\u5104\u5105\u5106\u5107\u5108\u5109\u510a\u510b\u510c\u510d\u510e\u510f\u5110\u5111\u5112\u5113\u5114\u5115\u5116\u5117\u5118\u5119\u511a\u511b\u511c\u511d\u511e\u511f\u5120\u5121\u5122\u5123\u5124\u5125\u5126\u5127\u5128\u5129\u512a\u512b\u512c\u512d\u512e\u512f\u5130\u5131\u5132\u5133\u5134\u5135\u5136\u5137\u5138\u5139\u513a\u513b\u513c\u513d\u513e\u513f\u5140\u5141\u5142\u5143\u5144\u5145\u5146\u5147\u5148\u5149\u514a\u514b\u514c\u514d\u514e\u514f\u5150\u5151\u5152\u5153\u5154\u5155\u5156\u5157\u5158\u5159\u515a\u515b\u515c\u515d\u515e\u515f\u5160\u5161\u5162\u5163\u5164\u5165\u5166\u5167\u5168\u5169\u516a\u516b\u516c\u516d\u516e\u516f\u5170\u5171\u5172\u5173\u5174\u5175\u5176\u5177\u5178\u5179\u517a\u517b\u517c\u517d\u517e\u517f\u5180\u5181\u5182\u5183\u5184\u5185\u5186\u5187\u5188\u5189\u518a\u518b\u518c\u518d\u518e\u518f\u5190\u5191\u5192\u5193\u5194\u5195\u5196\u5197\u5198\u5199\u519a\u519b\u519c\u519d\u519e\u519f\u51a0\u51a1\u51a2\u51a3\u51a4\u51a5\u51a6\u51a7\u51a8\u51a9\u51aa\u51ab\u51ac\u51ad\u51ae\u51af\u51b0\u51b1\u51b2\u51b3\u51b4\u51b5\u51b6\u51b7\u51b8\u51b9\u51ba\u51bb\u51bc\u51bd\u51be\u51bf\u51c0\u51c1\u51c2\u51c3\u51c4\u51c5\u51c6\u51c7\u51c8\u51c9\u51ca\u51cb\u51cc\u51cd\u51ce\u51cf\u51d0\u51d1\u51d2\u51d3\u51d4\u51d5\u51d6\u51d7\u51d8\u51d9\u51da\u51db\u51dc\u51dd\u51de\u51df\u51e0\u51e1\u51e2\u51e3\u51e4\u51e5\u51e6\u51e7\u51e8\u51e9\u51ea\u51eb\u51ec\u51ed\u51ee\u51ef\u51f0\u51f1\u51f2\u51f3\u51f4\u51f5\u51f6\u51f7\u51f8\u51f9\u51fa\u51fb\u51fc\u51fd\u51fe\u51ff\u5200\u5201\u5202\u5203\u5204\u5205\u5206\u5207\u5208\u5209\u520a\u520b\u520c\u520d\u520e\u520f\u5210\u5211\u5212\u5213\u5214\u5215\u5216\u5217\u5218\u5219\u521a\u521b\u521c\u521d\u521e\u521f\u5220\u5221\u5222\u5223\u5224\u5225\u5226\u5227\u5228\u5229\u522a\u522b\u522c\u522d\u522e\u522f\u5230\u5231\u5232\u5233\u5234\u5235\u5236\u5237\u5238\u5239\u523a\u523b\u523c\u523d\u523e\u523f\u5240\u5241\u5242\u5243\u5244\u5245\u5246\u5247\u5248\u5249\u524a\u524b\u524c\u524d\u524e\u524f\u5250\u5251\u5252\u5253\u5254\u5255\u5256\u5257\u5258\u5259\u525a\u525b\u525c\u525d\u525e\u525f\u5260\u5261\u5262\u5263\u5264\u5265\u5266\u5267\u5268\u5269\u526a\u526b\u526c\u526d\u526e\u526f\u5270\u5271\u5272\u5273\u5274\u5275\u5276\u5277\u5278\u5279\u527a\u527b\u527c\u527d\u527e\u527f\u5280\u5281\u5282\u5283\u5284\u5285\u5286\u5287\u5288\u5289\u528a\u528b\u528c\u528d\u528e\u528f\u5290\u5291\u5292\u5293\u5294\u5295\u5296\u5297\u5298\u5299\u529a\u529b\u529c\u529d\u529e\u529f\u52a0\u52a1\u52a2\u52a3\u52a4\u52a5\u52a6\u52a7\u52a8\u52a9\u52aa\u52ab\u52ac\u52ad\u52ae\u52af\u52b0\u52b1\u52b2\u52b3\u52b4\u52b5\u52b6\u52b7\u52b8\u52b9\u52ba\u52bb\u52bc\u52bd\u52be\u52bf\u52c0\u52c1\u52c2\u52c3\u52c4\u52c5\u52c6\u52c7\u52c8\u52c9\u52ca\u52cb\u52cc\u52cd\u52ce\u52cf\u52d0\u52d1\u52d2\u52d3\u52d4\u52d5\u52d6\u52d7\u52d8\u52d9\u52da\u52db\u52dc\u52dd\u52de\u52df\u52e0\u52e1\u52e2\u52e3\u52e4\u52e5\u52e6\u52e7\u52e8\u52e9\u52ea\u52eb\u52ec\u52ed\u52ee\u52ef\u52f0\u52f1\u52f2\u52f3\u52f4\u52f5\u52f6\u52f7\u52f8\u52f9\u52fa\u52fb\u52fc\u52fd\u52fe\u52ff\u5300\u5301\u5302\u5303\u5304\u5305\u5306\u5307\u5308\u5309\u530a\u530b\u530c\u530d\u530e\u530f\u5310\u5311\u5312\u5313\u5314\u5315\u5316\u5317\u5318\u5319\u531a\u531b\u531c\u531d\u531e\u531f\u5320\u5321\u5322\u5323\u5324\u5325\u5326\u5327\u5328\u5329\u532a\u532b\u532c\u532d\u532e\u532f\u5330\u5331\u5332\u5333\u5334\u5335\u5336\u5337\u5338\u5339\u533a\u533b\u533c\u533d\u533e\u533f\u5340\u5341\u5342\u5343\u5344\u5345\u5346\u5347\u5348\u5349\u534a\u534b\u534c\u534d\u534e\u534f\u5350\u5351\u5352\u5353\u5354\u5355\u5356\u5357\u5358\u5359\u535a\u535b\u535c\u535d\u535e\u535f\u5360\u5361\u5362\u5363\u5364\u5365\u5366\u5367\u5368\u5369\u536a\u536b\u536c\u536d\u536e\u536f\u5370\u5371\u5372\u5373\u5374\u5375\u5376\u5377\u5378\u5379\u537a\u537b\u537c\u537d\u537e\u537f\u5380\u5381\u5382\u5383\u5384\u5385\u5386\u5387\u5388\u5389\u538a\u538b\u538c\u538d\u538e\u538f\u5390\u5391\u5392\u5393\u5394\u5395\u5396\u5397\u5398\u5399\u539a\u539b\u539c\u539d\u539e\u539f\u53a0\u53a1\u53a2\u53a3\u53a4\u53a5\u53a6\u53a7\u53a8\u53a9\u53aa\u53ab\u53ac\u53ad\u53ae\u53af\u53b0\u53b1\u53b2\u53b3\u53b4\u53b5\u53b6\u53b7\u53b8\u53b9\u53ba\u53bb\u53bc\u53bd\u53be\u53bf\u53c0\u53c1\u53c2\u53c3\u53c4\u53c5\u53c6\u53c7\u53c8\u53c9\u53ca\u53cb\u53cc\u53cd\u53ce\u53cf\u53d0\u53d1\u53d2\u53d3\u53d4\u53d5\u53d6\u53d7\u53d8\u53d9\u53da\u53db\u53dc\u53dd\u53de\u53df\u53e0\u53e1\u53e2\u53e3\u53e4\u53e5\u53e6\u53e7\u53e8\u53e9\u53ea\u53eb\u53ec\u53ed\u53ee\u53ef\u53f0\u53f1\u53f2\u53f3\u53f4\u53f5\u53f6\u53f7\u53f8\u53f9\u53fa\u53fb\u53fc\u53fd\u53fe\u53ff\u5400\u5401\u5402\u5403\u5404\u5405\u5406\u5407\u5408\u5409\u540a\u540b\u540c\u540d\u540e\u540f\u5410\u5411\u5412\u5413\u5414\u5415\u5416\u5417\u5418\u5419\u541a\u541b\u541c\u541d\u541e\u541f\u5420\u5421\u5422\u5423\u5424\u5425\u5426\u5427\u5428\u5429\u542a\u542b\u542c\u542d\u542e\u542f\u5430\u5431\u5432\u5433\u5434\u5435\u5436\u5437\u5438\u5439\u543a\u543b\u543c\u543d\u543e\u543f\u5440\u5441\u5442\u5443\u5444\u5445\u5446\u5447\u5448\u5449\u544a\u544b\u544c\u544d\u544e\u544f\u5450\u5451\u5452\u5453\u5454\u5455\u5456\u5457\u5458\u5459\u545a\u545b\u545c\u545d\u545e\u545f\u5460\u5461\u5462\u5463\u5464\u5465\u5466\u5467\u5468\u5469\u546a\u546b\u546c\u546d\u546e\u546f\u5470\u5471\u5472\u5473\u5474\u5475\u5476\u5477\u5478\u5479\u547a\u547b\u547c\u547d\u547e\u547f\u5480\u5481\u5482\u5483\u5484\u5485\u5486\u5487\u5488\u5489\u548a\u548b\u548c\u548d\u548e\u548f\u5490\u5491\u5492\u5493\u5494\u5495\u5496\u5497\u5498\u5499\u549a\u549b\u549c\u549d\u549e\u549f\u54a0\u54a1\u54a2\u54a3\u54a4\u54a5\u54a6\u54a7\u54a8\u54a9\u54aa\u54ab\u54ac\u54ad\u54ae\u54af\u54b0\u54b1\u54b2\u54b3\u54b4\u54b5\u54b6\u54b7\u54b8\u54b9\u54ba\u54bb\u54bc\u54bd\u54be\u54bf\u54c0\u54c1\u54c2\u54c3\u54c4\u54c5\u54c6\u54c7\u54c8\u54c9\u54ca\u54cb\u54cc\u54cd\u54ce\u54cf\u54d0\u54d1\u54d2\u54d3\u54d4\u54d5\u54d6\u54d7\u54d8\u54d9\u54da\u54db\u54dc\u54dd\u54de\u54df\u54e0\u54e1\u54e2\u54e3\u54e4\u54e5\u54e6\u54e7\u54e8\u54e9\u54ea\u54eb\u54ec\u54ed\u54ee\u54ef\u54f0\u54f1\u54f2\u54f3\u54f4\u54f5\u54f6\u54f7\u54f8\u54f9\u54fa\u54fb\u54fc\u54fd\u54fe\u54ff\u5500\u5501\u5502\u5503\u5504\u5505\u5506\u5507\u5508\u5509\u550a\u550b\u550c\u550d\u550e\u550f\u5510\u5511\u5512\u5513\u5514\u5515\u5516\u5517\u5518\u5519\u551a\u551b\u551c\u551d\u551e\u551f\u5520\u5521\u5522\u5523\u5524\u5525\u5526\u5527\u5528\u5529\u552a\u552b\u552c\u552d\u552e\u552f\u5530\u5531\u5532\u5533\u5534\u5535\u5536\u5537\u5538\u5539\u553a\u553b\u553c\u553d\u553e\u553f\u5540\u5541\u5542\u5543\u5544\u5545\u5546\u5547\u5548\u5549\u554a\u554b\u554c\u554d\u554e\u554f\u5550\u5551\u5552\u5553\u5554\u5555\u5556\u5557\u5558\u5559\u555a\u555b\u555c\u555d\u555e\u555f\u5560\u5561\u5562\u5563\u5564\u5565\u5566\u5567\u5568\u5569\u556a\u556b\u556c\u556d\u556e\u556f\u5570\u5571\u5572\u5573\u5574\u5575\u5576\u5577\u5578\u5579\u557a\u557b\u557c\u557d\u557e\u557f\u5580\u5581\u5582\u5583\u5584\u5585\u5586\u5587\u5588\u5589\u558a\u558b\u558c\u558d\u558e\u558f\u5590\u5591\u5592\u5593\u5594\u5595\u5596\u5597\u5598\u5599\u559a\u559b\u559c\u559d\u559e\u559f\u55a0\u55a1\u55a2\u55a3\u55a4\u55a5\u55a6\u55a7\u55a8\u55a9\u55aa\u55ab\u55ac\u55ad\u55ae\u55af\u55b0\u55b1\u55b2\u55b3\u55b4\u55b5\u55b6\u55b7\u55b8\u55b9\u55ba\u55bb\u55bc\u55bd\u55be\u55bf\u55c0\u55c1\u55c2\u55c3\u55c4\u55c5\u55c6\u55c7\u55c8\u55c9\u55ca\u55cb\u55cc\u55cd\u55ce\u55cf\u55d0\u55d1\u55d2\u55d3\u55d4\u55d5\u55d6\u55d7\u55d8\u55d9\u55da\u55db\u55dc\u55dd\u55de\u55df\u55e0\u55e1\u55e2\u55e3\u55e4\u55e5\u55e6\u55e7\u55e8\u55e9\u55ea\u55eb\u55ec\u55ed\u55ee\u55ef\u55f0\u55f1\u55f2\u55f3\u55f4\u55f5\u55f6\u55f7\u55f8\u55f9\u55fa\u55fb\u55fc\u55fd\u55fe\u55ff\u5600\u5601\u5602\u5603\u5604\u5605\u5606\u5607\u5608\u5609\u560a\u560b\u560c\u560d\u560e\u560f\u5610\u5611\u5612\u5613\u5614\u5615\u5616\u5617\u5618\u5619\u561a\u561b\u561c\u561d\u561e\u561f\u5620\u5621\u5622\u5623\u5624\u5625\u5626\u5627\u5628\u5629\u562a\u562b\u562c\u562d\u562e\u562f\u5630\u5631\u5632\u5633\u5634\u5635\u5636\u5637\u5638\u5639\u563a\u563b\u563c\u563d\u563e\u563f\u5640\u5641\u5642\u5643\u5644\u5645\u5646\u5647\u5648\u5649\u564a\u564b\u564c\u564d\u564e\u564f\u5650\u5651\u5652\u5653\u5654\u5655\u5656\u5657\u5658\u5659\u565a\u565b\u565c\u565d\u565e\u565f\u5660\u5661\u5662\u5663\u5664\u5665\u5666\u5667\u5668\u5669\u566a\u566b\u566c\u566d\u566e\u566f\u5670\u5671\u5672\u5673\u5674\u5675\u5676\u5677\u5678\u5679\u567a\u567b\u567c\u567d\u567e\u567f\u5680\u5681\u5682\u5683\u5684\u5685\u5686\u5687\u5688\u5689\u568a\u568b\u568c\u568d\u568e\u568f\u5690\u5691\u5692\u5693\u5694\u5695\u5696\u5697\u5698\u5699\u569a\u569b\u569c\u569d\u569e\u569f\u56a0\u56a1\u56a2\u56a3\u56a4\u56a5\u56a6\u56a7\u56a8\u56a9\u56aa\u56ab\u56ac\u56ad\u56ae\u56af\u56b0\u56b1\u56b2\u56b3\u56b4\u56b5\u56b6\u56b7\u56b8\u56b9\u56ba\u56bb\u56bc\u56bd\u56be\u56bf\u56c0\u56c1\u56c2\u56c3\u56c4\u56c5\u56c6\u56c7\u56c8\u56c9\u56ca\u56cb\u56cc\u56cd\u56ce\u56cf\u56d0\u56d1\u56d2\u56d3\u56d4\u56d5\u56d6\u56d7\u56d8\u56d9\u56da\u56db\u56dc\u56dd\u56de\u56df\u56e0\u56e1\u56e2\u56e3\u56e4\u56e5\u56e6\u56e7\u56e8\u56e9\u56ea\u56eb\u56ec\u56ed\u56ee\u56ef\u56f0\u56f1\u56f2\u56f3\u56f4\u56f5\u56f6\u56f7\u56f8\u56f9\u56fa\u56fb\u56fc\u56fd\u56fe\u56ff\u5700\u5701\u5702\u5703\u5704\u5705\u5706\u5707\u5708\u5709\u570a\u570b\u570c\u570d\u570e\u570f\u5710\u5711\u5712\u5713\u5714\u5715\u5716\u5717\u5718\u5719\u571a\u571b\u571c\u571d\u571e\u571f\u5720\u5721\u5722\u5723\u5724\u5725\u5726\u5727\u5728\u5729\u572a\u572b\u572c\u572d\u572e\u572f\u5730\u5731\u5732\u5733\u5734\u5735\u5736\u5737\u5738\u5739\u573a\u573b\u573c\u573d\u573e\u573f\u5740\u5741\u5742\u5743\u5744\u5745\u5746\u5747\u5748\u5749\u574a\u574b\u574c\u574d\u574e\u574f\u5750\u5751\u5752\u5753\u5754\u5755\u5756\u5757\u5758\u5759\u575a\u575b\u575c\u575d\u575e\u575f\u5760\u5761\u5762\u5763\u5764\u5765\u5766\u5767\u5768\u5769\u576a\u576b\u576c\u576d\u576e\u576f\u5770\u5771\u5772\u5773\u5774\u5775\u5776\u5777\u5778\u5779\u577a\u577b\u577c\u577d\u577e\u577f\u5780\u5781\u5782\u5783\u5784\u5785\u5786\u5787\u5788\u5789\u578a\u578b\u578c\u578d\u578e\u578f\u5790\u5791\u5792\u5793\u5794\u5795\u5796\u5797\u5798\u5799\u579a\u579b\u579c\u579d\u579e\u579f\u57a0\u57a1\u57a2\u57a3\u57a4\u57a5\u57a6\u57a7\u57a8\u57a9\u57aa\u57ab\u57ac\u57ad\u57ae\u57af\u57b0\u57b1\u57b2\u57b3\u57b4\u57b5\u57b6\u57b7\u57b8\u57b9\u57ba\u57bb\u57bc\u57bd\u57be\u57bf\u57c0\u57c1\u57c2\u57c3\u57c4\u57c5\u57c6\u57c7\u57c8\u57c9\u57ca\u57cb\u57cc\u57cd\u57ce\u57cf\u57d0\u57d1\u57d2\u57d3\u57d4\u57d5\u57d6\u57d7\u57d8\u57d9\u57da\u57db\u57dc\u57dd\u57de\u57df\u57e0\u57e1\u57e2\u57e3\u57e4\u57e5\u57e6\u57e7\u57e8\u57e9\u57ea\u57eb\u57ec\u57ed\u57ee\u57ef\u57f0\u57f1\u57f2\u57f3\u57f4\u57f5\u57f6\u57f7\u57f8\u57f9\u57fa\u57fb\u57fc\u57fd\u57fe\u57ff\u5800\u5801\u5802\u5803\u5804\u5805\u5806\u5807\u5808\u5809\u580a\u580b\u580c\u580d\u580e\u580f\u5810\u5811\u5812\u5813\u5814\u5815\u5816\u5817\u5818\u5819\u581a\u581b\u581c\u581d\u581e\u581f\u5820\u5821\u5822\u5823\u5824\u5825\u5826\u5827\u5828\u5829\u582a\u582b\u582c\u582d\u582e\u582f\u5830\u5831\u5832\u5833\u5834\u5835\u5836\u5837\u5838\u5839\u583a\u583b\u583c\u583d\u583e\u583f\u5840\u5841\u5842\u5843\u5844\u5845\u5846\u5847\u5848\u5849\u584a\u584b\u584c\u584d\u584e\u584f\u5850\u5851\u5852\u5853\u5854\u5855\u5856\u5857\u5858\u5859\u585a\u585b\u585c\u585d\u585e\u585f\u5860\u5861\u5862\u5863\u5864\u5865\u5866\u5867\u5868\u5869\u586a\u586b\u586c\u586d\u586e\u586f\u5870\u5871\u5872\u5873\u5874\u5875\u5876\u5877\u5878\u5879\u587a\u587b\u587c\u587d\u587e\u587f\u5880\u5881\u5882\u5883\u5884\u5885\u5886\u5887\u5888\u5889\u588a\u588b\u588c\u588d\u588e\u588f\u5890\u5891\u5892\u5893\u5894\u5895\u5896\u5897\u5898\u5899\u589a\u589b\u589c\u589d\u589e\u589f\u58a0\u58a1\u58a2\u58a3\u58a4\u58a5\u58a6\u58a7\u58a8\u58a9\u58aa\u58ab\u58ac\u58ad\u58ae\u58af\u58b0\u58b1\u58b2\u58b3\u58b4\u58b5\u58b6\u58b7\u58b8\u58b9\u58ba\u58bb\u58bc\u58bd\u58be\u58bf\u58c0\u58c1\u58c2\u58c3\u58c4\u58c5\u58c6\u58c7\u58c8\u58c9\u58ca\u58cb\u58cc\u58cd\u58ce\u58cf\u58d0\u58d1\u58d2\u58d3\u58d4\u58d5\u58d6\u58d7\u58d8\u58d9\u58da\u58db\u58dc\u58dd\u58de\u58df\u58e0\u58e1\u58e2\u58e3\u58e4\u58e5\u58e6\u58e7\u58e8\u58e9\u58ea\u58eb\u58ec\u58ed\u58ee\u58ef\u58f0\u58f1\u58f2\u58f3\u58f4\u58f5\u58f6\u58f7\u58f8\u58f9\u58fa\u58fb\u58fc\u58fd\u58fe\u58ff\u5900\u5901\u5902\u5903\u5904\u5905\u5906\u5907\u5908\u5909\u590a\u590b\u590c\u590d\u590e\u590f\u5910\u5911\u5912\u5913\u5914\u5915\u5916\u5917\u5918\u5919\u591a\u591b\u591c\u591d\u591e\u591f\u5920\u5921\u5922\u5923\u5924\u5925\u5926\u5927\u5928\u5929\u592a\u592b\u592c\u592d\u592e\u592f\u5930\u5931\u5932\u5933\u5934\u5935\u5936\u5937\u5938\u5939\u593a\u593b\u593c\u593d\u593e\u593f\u5940\u5941\u5942\u5943\u5944\u5945\u5946\u5947\u5948\u5949\u594a\u594b\u594c\u594d\u594e\u594f\u5950\u5951\u5952\u5953\u5954\u5955\u5956\u5957\u5958\u5959\u595a\u595b\u595c\u595d\u595e\u595f\u5960\u5961\u5962\u5963\u5964\u5965\u5966\u5967\u5968\u5969\u596a\u596b\u596c\u596d\u596e\u596f\u5970\u5971\u5972\u5973\u5974\u5975\u5976\u5977\u5978\u5979\u597a\u597b\u597c\u597d\u597e\u597f\u5980\u5981\u5982\u5983\u5984\u5985\u5986\u5987\u5988\u5989\u598a\u598b\u598c\u598d\u598e\u598f\u5990\u5991\u5992\u5993\u5994\u5995\u5996\u5997\u5998\u5999\u599a\u599b\u599c\u599d\u599e\u599f\u59a0\u59a1\u59a2\u59a3\u59a4\u59a5\u59a6\u59a7\u59a8\u59a9\u59aa\u59ab\u59ac\u59ad\u59ae\u59af\u59b0\u59b1\u59b2\u59b3\u59b4\u59b5\u59b6\u59b7\u59b8\u59b9\u59ba\u59bb\u59bc\u59bd\u59be\u59bf\u59c0\u59c1\u59c2\u59c3\u59c4\u59c5\u59c6\u59c7\u59c8\u59c9\u59ca\u59cb\u59cc\u59cd\u59ce\u59cf\u59d0\u59d1\u59d2\u59d3\u59d4\u59d5\u59d6\u59d7\u59d8\u59d9\u59da\u59db\u59dc\u59dd\u59de\u59df\u59e0\u59e1\u59e2\u59e3\u59e4\u59e5\u59e6\u59e7\u59e8\u59e9\u59ea\u59eb\u59ec\u59ed\u59ee\u59ef\u59f0\u59f1\u59f2\u59f3\u59f4\u59f5\u59f6\u59f7\u59f8\u59f9\u59fa\u59fb\u59fc\u59fd\u59fe\u59ff\u5a00\u5a01\u5a02\u5a03\u5a04\u5a05\u5a06\u5a07\u5a08\u5a09\u5a0a\u5a0b\u5a0c\u5a0d\u5a0e\u5a0f\u5a10\u5a11\u5a12\u5a13\u5a14\u5a15\u5a16\u5a17\u5a18\u5a19\u5a1a\u5a1b\u5a1c\u5a1d\u5a1e\u5a1f\u5a20\u5a21\u5a22\u5a23\u5a24\u5a25\u5a26\u5a27\u5a28\u5a29\u5a2a\u5a2b\u5a2c\u5a2d\u5a2e\u5a2f\u5a30\u5a31\u5a32\u5a33\u5a34\u5a35\u5a36\u5a37\u5a38\u5a39\u5a3a\u5a3b\u5a3c\u5a3d\u5a3e\u5a3f\u5a40\u5a41\u5a42\u5a43\u5a44\u5a45\u5a46\u5a47\u5a48\u5a49\u5a4a\u5a4b\u5a4c\u5a4d\u5a4e\u5a4f\u5a50\u5a51\u5a52\u5a53\u5a54\u5a55\u5a56\u5a57\u5a58\u5a59\u5a5a\u5a5b\u5a5c\u5a5d\u5a5e\u5a5f\u5a60\u5a61\u5a62\u5a63\u5a64\u5a65\u5a66\u5a67\u5a68\u5a69\u5a6a\u5a6b\u5a6c\u5a6d\u5a6e\u5a6f\u5a70\u5a71\u5a72\u5a73\u5a74\u5a75\u5a76\u5a77\u5a78\u5a79\u5a7a\u5a7b\u5a7c\u5a7d\u5a7e\u5a7f\u5a80\u5a81\u5a82\u5a83\u5a84\u5a85\u5a86\u5a87\u5a88\u5a89\u5a8a\u5a8b\u5a8c\u5a8d\u5a8e\u5a8f\u5a90\u5a91\u5a92\u5a93\u5a94\u5a95\u5a96\u5a97\u5a98\u5a99\u5a9a\u5a9b\u5a9c\u5a9d\u5a9e\u5a9f\u5aa0\u5aa1\u5aa2\u5aa3\u5aa4\u5aa5\u5aa6\u5aa7\u5aa8\u5aa9\u5aaa\u5aab\u5aac\u5aad\u5aae\u5aaf\u5ab0\u5ab1\u5ab2\u5ab3\u5ab4\u5ab5\u5ab6\u5ab7\u5ab8\u5ab9\u5aba\u5abb\u5abc\u5abd\u5abe\u5abf\u5ac0\u5ac1\u5ac2\u5ac3\u5ac4\u5ac5\u5ac6\u5ac7\u5ac8\u5ac9\u5aca\u5acb\u5acc\u5acd\u5ace\u5acf\u5ad0\u5ad1\u5ad2\u5ad3\u5ad4\u5ad5\u5ad6\u5ad7\u5ad8\u5ad9\u5ada\u5adb\u5adc\u5add\u5ade\u5adf\u5ae0\u5ae1\u5ae2\u5ae3\u5ae4\u5ae5\u5ae6\u5ae7\u5ae8\u5ae9\u5aea\u5aeb\u5aec\u5aed\u5aee\u5aef\u5af0\u5af1\u5af2\u5af3\u5af4\u5af5\u5af6\u5af7\u5af8\u5af9\u5afa\u5afb\u5afc\u5afd\u5afe\u5aff\u5b00\u5b01\u5b02\u5b03\u5b04\u5b05\u5b06\u5b07\u5b08\u5b09\u5b0a\u5b0b\u5b0c\u5b0d\u5b0e\u5b0f\u5b10\u5b11\u5b12\u5b13\u5b14\u5b15\u5b16\u5b17\u5b18\u5b19\u5b1a\u5b1b\u5b1c\u5b1d\u5b1e\u5b1f\u5b20\u5b21\u5b22\u5b23\u5b24\u5b25\u5b26\u5b27\u5b28\u5b29\u5b2a\u5b2b\u5b2c\u5b2d\u5b2e\u5b2f\u5b30\u5b31\u5b32\u5b33\u5b34\u5b35\u5b36\u5b37\u5b38\u5b39\u5b3a\u5b3b\u5b3c\u5b3d\u5b3e\u5b3f\u5b40\u5b41\u5b42\u5b43\u5b44\u5b45\u5b46\u5b47\u5b48\u5b49\u5b4a\u5b4b\u5b4c\u5b4d\u5b4e\u5b4f\u5b50\u5b51\u5b52\u5b53\u5b54\u5b55\u5b56\u5b57\u5b58\u5b59\u5b5a\u5b5b\u5b5c\u5b5d\u5b5e\u5b5f\u5b60\u5b61\u5b62\u5b63\u5b64\u5b65\u5b66\u5b67\u5b68\u5b69\u5b6a\u5b6b\u5b6c\u5b6d\u5b6e\u5b6f\u5b70\u5b71\u5b72\u5b73\u5b74\u5b75\u5b76\u5b77\u5b78\u5b79\u5b7a\u5b7b\u5b7c\u5b7d\u5b7e\u5b7f\u5b80\u5b81\u5b82\u5b83\u5b84\u5b85\u5b86\u5b87\u5b88\u5b89\u5b8a\u5b8b\u5b8c\u5b8d\u5b8e\u5b8f\u5b90\u5b91\u5b92\u5b93\u5b94\u5b95\u5b96\u5b97\u5b98\u5b99\u5b9a\u5b9b\u5b9c\u5b9d\u5b9e\u5b9f\u5ba0\u5ba1\u5ba2\u5ba3\u5ba4\u5ba5\u5ba6\u5ba7\u5ba8\u5ba9\u5baa\u5bab\u5bac\u5bad\u5bae\u5baf\u5bb0\u5bb1\u5bb2\u5bb3\u5bb4\u5bb5\u5bb6\u5bb7\u5bb8\u5bb9\u5bba\u5bbb\u5bbc\u5bbd\u5bbe\u5bbf\u5bc0\u5bc1\u5bc2\u5bc3\u5bc4\u5bc5\u5bc6\u5bc7\u5bc8\u5bc9\u5bca\u5bcb\u5bcc\u5bcd\u5bce\u5bcf\u5bd0\u5bd1\u5bd2\u5bd3\u5bd4\u5bd5\u5bd6\u5bd7\u5bd8\u5bd9\u5bda\u5bdb\u5bdc\u5bdd\u5bde\u5bdf\u5be0\u5be1\u5be2\u5be3\u5be4\u5be5\u5be6\u5be7\u5be8\u5be9\u5bea\u5beb\u5bec\u5bed\u5bee\u5bef\u5bf0\u5bf1\u5bf2\u5bf3\u5bf4\u5bf5\u5bf6\u5bf7\u5bf8\u5bf9\u5bfa\u5bfb\u5bfc\u5bfd\u5bfe\u5bff\u5c00\u5c01\u5c02\u5c03\u5c04\u5c05\u5c06\u5c07\u5c08\u5c09\u5c0a\u5c0b\u5c0c\u5c0d\u5c0e\u5c0f\u5c10\u5c11\u5c12\u5c13\u5c14\u5c15\u5c16\u5c17\u5c18\u5c19\u5c1a\u5c1b\u5c1c\u5c1d\u5c1e\u5c1f\u5c20\u5c21\u5c22\u5c23\u5c24\u5c25\u5c26\u5c27\u5c28\u5c29\u5c2a\u5c2b\u5c2c\u5c2d\u5c2e\u5c2f\u5c30\u5c31\u5c32\u5c33\u5c34\u5c35\u5c36\u5c37\u5c38\u5c39\u5c3a\u5c3b\u5c3c\u5c3d\u5c3e\u5c3f\u5c40\u5c41\u5c42\u5c43\u5c44\u5c45\u5c46\u5c47\u5c48\u5c49\u5c4a\u5c4b\u5c4c\u5c4d\u5c4e\u5c4f\u5c50\u5c51\u5c52\u5c53\u5c54\u5c55\u5c56\u5c57\u5c58\u5c59\u5c5a\u5c5b\u5c5c\u5c5d\u5c5e\u5c5f\u5c60\u5c61\u5c62\u5c63\u5c64\u5c65\u5c66\u5c67\u5c68\u5c69\u5c6a\u5c6b\u5c6c\u5c6d\u5c6e\u5c6f\u5c70\u5c71\u5c72\u5c73\u5c74\u5c75\u5c76\u5c77\u5c78\u5c79\u5c7a\u5c7b\u5c7c\u5c7d\u5c7e\u5c7f\u5c80\u5c81\u5c82\u5c83\u5c84\u5c85\u5c86\u5c87\u5c88\u5c89\u5c8a\u5c8b\u5c8c\u5c8d\u5c8e\u5c8f\u5c90\u5c91\u5c92\u5c93\u5c94\u5c95\u5c96\u5c97\u5c98\u5c99\u5c9a\u5c9b\u5c9c\u5c9d\u5c9e\u5c9f\u5ca0\u5ca1\u5ca2\u5ca3\u5ca4\u5ca5\u5ca6\u5ca7\u5ca8\u5ca9\u5caa\u5cab\u5cac\u5cad\u5cae\u5caf\u5cb0\u5cb1\u5cb2\u5cb3\u5cb4\u5cb5\u5cb6\u5cb7\u5cb8\u5cb9\u5cba\u5cbb\u5cbc\u5cbd\u5cbe\u5cbf\u5cc0\u5cc1\u5cc2\u5cc3\u5cc4\u5cc5\u5cc6\u5cc7\u5cc8\u5cc9\u5cca\u5ccb\u5ccc\u5ccd\u5cce\u5ccf\u5cd0\u5cd1\u5cd2\u5cd3\u5cd4\u5cd5\u5cd6\u5cd7\u5cd8\u5cd9\u5cda\u5cdb\u5cdc\u5cdd\u5cde\u5cdf\u5ce0\u5ce1\u5ce2\u5ce3\u5ce4\u5ce5\u5ce6\u5ce7\u5ce8\u5ce9\u5cea\u5ceb\u5cec\u5ced\u5cee\u5cef\u5cf0\u5cf1\u5cf2\u5cf3\u5cf4\u5cf5\u5cf6\u5cf7\u5cf8\u5cf9\u5cfa\u5cfb\u5cfc\u5cfd\u5cfe\u5cff\u5d00\u5d01\u5d02\u5d03\u5d04\u5d05\u5d06\u5d07\u5d08\u5d09\u5d0a\u5d0b\u5d0c\u5d0d\u5d0e\u5d0f\u5d10\u5d11\u5d12\u5d13\u5d14\u5d15\u5d16\u5d17\u5d18\u5d19\u5d1a\u5d1b\u5d1c\u5d1d\u5d1e\u5d1f\u5d20\u5d21\u5d22\u5d23\u5d24\u5d25\u5d26\u5d27\u5d28\u5d29\u5d2a\u5d2b\u5d2c\u5d2d\u5d2e\u5d2f\u5d30\u5d31\u5d32\u5d33\u5d34\u5d35\u5d36\u5d37\u5d38\u5d39\u5d3a\u5d3b\u5d3c\u5d3d\u5d3e\u5d3f\u5d40\u5d41\u5d42\u5d43\u5d44\u5d45\u5d46\u5d47\u5d48\u5d49\u5d4a\u5d4b\u5d4c\u5d4d\u5d4e\u5d4f\u5d50\u5d51\u5d52\u5d53\u5d54\u5d55\u5d56\u5d57\u5d58\u5d59\u5d5a\u5d5b\u5d5c\u5d5d\u5d5e\u5d5f\u5d60\u5d61\u5d62\u5d63\u5d64\u5d65\u5d66\u5d67\u5d68\u5d69\u5d6a\u5d6b\u5d6c\u5d6d\u5d6e\u5d6f\u5d70\u5d71\u5d72\u5d73\u5d74\u5d75\u5d76\u5d77\u5d78\u5d79\u5d7a\u5d7b\u5d7c\u5d7d\u5d7e\u5d7f\u5d80\u5d81\u5d82\u5d83\u5d84\u5d85\u5d86\u5d87\u5d88\u5d89\u5d8a\u5d8b\u5d8c\u5d8d\u5d8e\u5d8f\u5d90\u5d91\u5d92\u5d93\u5d94\u5d95\u5d96\u5d97\u5d98\u5d99\u5d9a\u5d9b\u5d9c\u5d9d\u5d9e\u5d9f\u5da0\u5da1\u5da2\u5da3\u5da4\u5da5\u5da6\u5da7\u5da8\u5da9\u5daa\u5dab\u5dac\u5dad\u5dae\u5daf\u5db0\u5db1\u5db2\u5db3\u5db4\u5db5\u5db6\u5db7\u5db8\u5db9\u5dba\u5dbb\u5dbc\u5dbd\u5dbe\u5dbf\u5dc0\u5dc1\u5dc2\u5dc3\u5dc4\u5dc5\u5dc6\u5dc7\u5dc8\u5dc9\u5dca\u5dcb\u5dcc\u5dcd\u5dce\u5dcf\u5dd0\u5dd1\u5dd2\u5dd3\u5dd4\u5dd5\u5dd6\u5dd7\u5dd8\u5dd9\u5dda\u5ddb\u5ddc\u5ddd\u5dde\u5ddf\u5de0\u5de1\u5de2\u5de3\u5de4\u5de5\u5de6\u5de7\u5de8\u5de9\u5dea\u5deb\u5dec\u5ded\u5dee\u5def\u5df0\u5df1\u5df2\u5df3\u5df4\u5df5\u5df6\u5df7\u5df8\u5df9\u5dfa\u5dfb\u5dfc\u5dfd\u5dfe\u5dff\u5e00\u5e01\u5e02\u5e03\u5e04\u5e05\u5e06\u5e07\u5e08\u5e09\u5e0a\u5e0b\u5e0c\u5e0d\u5e0e\u5e0f\u5e10\u5e11\u5e12\u5e13\u5e14\u5e15\u5e16\u5e17\u5e18\u5e19\u5e1a\u5e1b\u5e1c\u5e1d\u5e1e\u5e1f\u5e20\u5e21\u5e22\u5e23\u5e24\u5e25\u5e26\u5e27\u5e28\u5e29\u5e2a\u5e2b\u5e2c\u5e2d\u5e2e\u5e2f\u5e30\u5e31\u5e32\u5e33\u5e34\u5e35\u5e36\u5e37\u5e38\u5e39\u5e3a\u5e3b\u5e3c\u5e3d\u5e3e\u5e3f\u5e40\u5e41\u5e42\u5e43\u5e44\u5e45\u5e46\u5e47\u5e48\u5e49\u5e4a\u5e4b\u5e4c\u5e4d\u5e4e\u5e4f\u5e50\u5e51\u5e52\u5e53\u5e54\u5e55\u5e56\u5e57\u5e58\u5e59\u5e5a\u5e5b\u5e5c\u5e5d\u5e5e\u5e5f\u5e60\u5e61\u5e62\u5e63\u5e64\u5e65\u5e66\u5e67\u5e68\u5e69\u5e6a\u5e6b\u5e6c\u5e6d\u5e6e\u5e6f\u5e70\u5e71\u5e72\u5e73\u5e74\u5e75\u5e76\u5e77\u5e78\u5e79\u5e7a\u5e7b\u5e7c\u5e7d\u5e7e\u5e7f\u5e80\u5e81\u5e82\u5e83\u5e84\u5e85\u5e86\u5e87\u5e88\u5e89\u5e8a\u5e8b\u5e8c\u5e8d\u5e8e\u5e8f\u5e90\u5e91\u5e92\u5e93\u5e94\u5e95\u5e96\u5e97\u5e98\u5e99\u5e9a\u5e9b\u5e9c\u5e9d\u5e9e\u5e9f\u5ea0\u5ea1\u5ea2\u5ea3\u5ea4\u5ea5\u5ea6\u5ea7\u5ea8\u5ea9\u5eaa\u5eab\u5eac\u5ead\u5eae\u5eaf\u5eb0\u5eb1\u5eb2\u5eb3\u5eb4\u5eb5\u5eb6\u5eb7\u5eb8\u5eb9\u5eba\u5ebb\u5ebc\u5ebd\u5ebe\u5ebf\u5ec0\u5ec1\u5ec2\u5ec3\u5ec4\u5ec5\u5ec6\u5ec7\u5ec8\u5ec9\u5eca\u5ecb\u5ecc\u5ecd\u5ece\u5ecf\u5ed0\u5ed1\u5ed2\u5ed3\u5ed4\u5ed5\u5ed6\u5ed7\u5ed8\u5ed9\u5eda\u5edb\u5edc\u5edd\u5ede\u5edf\u5ee0\u5ee1\u5ee2\u5ee3\u5ee4\u5ee5\u5ee6\u5ee7\u5ee8\u5ee9\u5eea\u5eeb\u5eec\u5eed\u5eee\u5eef\u5ef0\u5ef1\u5ef2\u5ef3\u5ef4\u5ef5\u5ef6\u5ef7\u5ef8\u5ef9\u5efa\u5efb\u5efc\u5efd\u5efe\u5eff\u5f00\u5f01\u5f02\u5f03\u5f04\u5f05\u5f06\u5f07\u5f08\u5f09\u5f0a\u5f0b\u5f0c\u5f0d\u5f0e\u5f0f\u5f10\u5f11\u5f12\u5f13\u5f14\u5f15\u5f16\u5f17\u5f18\u5f19\u5f1a\u5f1b\u5f1c\u5f1d\u5f1e\u5f1f\u5f20\u5f21\u5f22\u5f23\u5f24\u5f25\u5f26\u5f27\u5f28\u5f29\u5f2a\u5f2b\u5f2c\u5f2d\u5f2e\u5f2f\u5f30\u5f31\u5f32\u5f33\u5f34\u5f35\u5f36\u5f37\u5f38\u5f39\u5f3a\u5f3b\u5f3c\u5f3d\u5f3e\u5f3f\u5f40\u5f41\u5f42\u5f43\u5f44\u5f45\u5f46\u5f47\u5f48\u5f49\u5f4a\u5f4b\u5f4c\u5f4d\u5f4e\u5f4f\u5f50\u5f51\u5f52\u5f53\u5f54\u5f55\u5f56\u5f57\u5f58\u5f59\u5f5a\u5f5b\u5f5c\u5f5d\u5f5e\u5f5f\u5f60\u5f61\u5f62\u5f63\u5f64\u5f65\u5f66\u5f67\u5f68\u5f69\u5f6a\u5f6b\u5f6c\u5f6d\u5f6e\u5f6f\u5f70\u5f71\u5f72\u5f73\u5f74\u5f75\u5f76\u5f77\u5f78\u5f79\u5f7a\u5f7b\u5f7c\u5f7d\u5f7e\u5f7f\u5f80\u5f81\u5f82\u5f83\u5f84\u5f85\u5f86\u5f87\u5f88\u5f89\u5f8a\u5f8b\u5f8c\u5f8d\u5f8e\u5f8f\u5f90\u5f91\u5f92\u5f93\u5f94\u5f95\u5f96\u5f97\u5f98\u5f99\u5f9a\u5f9b\u5f9c\u5f9d\u5f9e\u5f9f\u5fa0\u5fa1\u5fa2\u5fa3\u5fa4\u5fa5\u5fa6\u5fa7\u5fa8\u5fa9\u5faa\u5fab\u5fac\u5fad\u5fae\u5faf\u5fb0\u5fb1\u5fb2\u5fb3\u5fb4\u5fb5\u5fb6\u5fb7\u5fb8\u5fb9\u5fba\u5fbb\u5fbc\u5fbd\u5fbe\u5fbf\u5fc0\u5fc1\u5fc2\u5fc3\u5fc4\u5fc5\u5fc6\u5fc7\u5fc8\u5fc9\u5fca\u5fcb\u5fcc\u5fcd\u5fce\u5fcf\u5fd0\u5fd1\u5fd2\u5fd3\u5fd4\u5fd5\u5fd6\u5fd7\u5fd8\u5fd9\u5fda\u5fdb\u5fdc\u5fdd\u5fde\u5fdf\u5fe0\u5fe1\u5fe2\u5fe3\u5fe4\u5fe5\u5fe6\u5fe7\u5fe8\u5fe9\u5fea\u5feb\u5fec\u5fed\u5fee\u5fef\u5ff0\u5ff1\u5ff2\u5ff3\u5ff4\u5ff5\u5ff6\u5ff7\u5ff8\u5ff9\u5ffa\u5ffb\u5ffc\u5ffd\u5ffe\u5fff\u6000\u6001\u6002\u6003\u6004\u6005\u6006\u6007\u6008\u6009\u600a\u600b\u600c\u600d\u600e\u600f\u6010\u6011\u6012\u6013\u6014\u6015\u6016\u6017\u6018\u6019\u601a\u601b\u601c\u601d\u601e\u601f\u6020\u6021\u6022\u6023\u6024\u6025\u6026\u6027\u6028\u6029\u602a\u602b\u602c\u602d\u602e\u602f\u6030\u6031\u6032\u6033\u6034\u6035\u6036\u6037\u6038\u6039\u603a\u603b\u603c\u603d\u603e\u603f\u6040\u6041\u6042\u6043\u6044\u6045\u6046\u6047\u6048\u6049\u604a\u604b\u604c\u604d\u604e\u604f\u6050\u6051\u6052\u6053\u6054\u6055\u6056\u6057\u6058\u6059\u605a\u605b\u605c\u605d\u605e\u605f\u6060\u6061\u6062\u6063\u6064\u6065\u6066\u6067\u6068\u6069\u606a\u606b\u606c\u606d\u606e\u606f\u6070\u6071\u6072\u6073\u6074\u6075\u6076\u6077\u6078\u6079\u607a\u607b\u607c\u607d\u607e\u607f\u6080\u6081\u6082\u6083\u6084\u6085\u6086\u6087\u6088\u6089\u608a\u608b\u608c\u608d\u608e\u608f\u6090\u6091\u6092\u6093\u6094\u6095\u6096\u6097\u6098\u6099\u609a\u609b\u609c\u609d\u609e\u609f\u60a0\u60a1\u60a2\u60a3\u60a4\u60a5\u60a6\u60a7\u60a8\u60a9\u60aa\u60ab\u60ac\u60ad\u60ae\u60af\u60b0\u60b1\u60b2\u60b3\u60b4\u60b5\u60b6\u60b7\u60b8\u60b9\u60ba\u60bb\u60bc\u60bd\u60be\u60bf\u60c0\u60c1\u60c2\u60c3\u60c4\u60c5\u60c6\u60c7\u60c8\u60c9\u60ca\u60cb\u60cc\u60cd\u60ce\u60cf\u60d0\u60d1\u60d2\u60d3\u60d4\u60d5\u60d6\u60d7\u60d8\u60d9\u60da\u60db\u60dc\u60dd\u60de\u60df\u60e0\u60e1\u60e2\u60e3\u60e4\u60e5\u60e6\u60e7\u60e8\u60e9\u60ea\u60eb\u60ec\u60ed\u60ee\u60ef\u60f0\u60f1\u60f2\u60f3\u60f4\u60f5\u60f6\u60f7\u60f8\u60f9\u60fa\u60fb\u60fc\u60fd\u60fe\u60ff\u6100\u6101\u6102\u6103\u6104\u6105\u6106\u6107\u6108\u6109\u610a\u610b\u610c\u610d\u610e\u610f\u6110\u6111\u6112\u6113\u6114\u6115\u6116\u6117\u6118\u6119\u611a\u611b\u611c\u611d\u611e\u611f\u6120\u6121\u6122\u6123\u6124\u6125\u6126\u6127\u6128\u6129\u612a\u612b\u612c\u612d\u612e\u612f\u6130\u6131\u6132\u6133\u6134\u6135\u6136\u6137\u6138\u6139\u613a\u613b\u613c\u613d\u613e\u613f\u6140\u6141\u6142\u6143\u6144\u6145\u6146\u6147\u6148\u6149\u614a\u614b\u614c\u614d\u614e\u614f\u6150\u6151\u6152\u6153\u6154\u6155\u6156\u6157\u6158\u6159\u615a\u615b\u615c\u615d\u615e\u615f\u6160\u6161\u6162\u6163\u6164\u6165\u6166\u6167\u6168\u6169\u616a\u616b\u616c\u616d\u616e\u616f\u6170\u6171\u6172\u6173\u6174\u6175\u6176\u6177\u6178\u6179\u617a\u617b\u617c\u617d\u617e\u617f\u6180\u6181\u6182\u6183\u6184\u6185\u6186\u6187\u6188\u6189\u618a\u618b\u618c\u618d\u618e\u618f\u6190\u6191\u6192\u6193\u6194\u6195\u6196\u6197\u6198\u6199\u619a\u619b\u619c\u619d\u619e\u619f\u61a0\u61a1\u61a2\u61a3\u61a4\u61a5\u61a6\u61a7\u61a8\u61a9\u61aa\u61ab\u61ac\u61ad\u61ae\u61af\u61b0\u61b1\u61b2\u61b3\u61b4\u61b5\u61b6\u61b7\u61b8\u61b9\u61ba\u61bb\u61bc\u61bd\u61be\u61bf\u61c0\u61c1\u61c2\u61c3\u61c4\u61c5\u61c6\u61c7\u61c8\u61c9\u61ca\u61cb\u61cc\u61cd\u61ce\u61cf\u61d0\u61d1\u61d2\u61d3\u61d4\u61d5\u61d6\u61d7\u61d8\u61d9\u61da\u61db\u61dc\u61dd\u61de\u61df\u61e0\u61e1\u61e2\u61e3\u61e4\u61e5\u61e6\u61e7\u61e8\u61e9\u61ea\u61eb\u61ec\u61ed\u61ee\u61ef\u61f0\u61f1\u61f2\u61f3\u61f4\u61f5\u61f6\u61f7\u61f8\u61f9\u61fa\u61fb\u61fc\u61fd\u61fe\u61ff\u6200\u6201\u6202\u6203\u6204\u6205\u6206\u6207\u6208\u6209\u620a\u620b\u620c\u620d\u620e\u620f\u6210\u6211\u6212\u6213\u6214\u6215\u6216\u6217\u6218\u6219\u621a\u621b\u621c\u621d\u621e\u621f\u6220\u6221\u6222\u6223\u6224\u6225\u6226\u6227\u6228\u6229\u622a\u622b\u622c\u622d\u622e\u622f\u6230\u6231\u6232\u6233\u6234\u6235\u6236\u6237\u6238\u6239\u623a\u623b\u623c\u623d\u623e\u623f\u6240\u6241\u6242\u6243\u6244\u6245\u6246\u6247\u6248\u6249\u624a\u624b\u624c\u624d\u624e\u624f\u6250\u6251\u6252\u6253\u6254\u6255\u6256\u6257\u6258\u6259\u625a\u625b\u625c\u625d\u625e\u625f\u6260\u6261\u6262\u6263\u6264\u6265\u6266\u6267\u6268\u6269\u626a\u626b\u626c\u626d\u626e\u626f\u6270\u6271\u6272\u6273\u6274\u6275\u6276\u6277\u6278\u6279\u627a\u627b\u627c\u627d\u627e\u627f\u6280\u6281\u6282\u6283\u6284\u6285\u6286\u6287\u6288\u6289\u628a\u628b\u628c\u628d\u628e\u628f\u6290\u6291\u6292\u6293\u6294\u6295\u6296\u6297\u6298\u6299\u629a\u629b\u629c\u629d\u629e\u629f\u62a0\u62a1\u62a2\u62a3\u62a4\u62a5\u62a6\u62a7\u62a8\u62a9\u62aa\u62ab\u62ac\u62ad\u62ae\u62af\u62b0\u62b1\u62b2\u62b3\u62b4\u62b5\u62b6\u62b7\u62b8\u62b9\u62ba\u62bb\u62bc\u62bd\u62be\u62bf\u62c0\u62c1\u62c2\u62c3\u62c4\u62c5\u62c6\u62c7\u62c8\u62c9\u62ca\u62cb\u62cc\u62cd\u62ce\u62cf\u62d0\u62d1\u62d2\u62d3\u62d4\u62d5\u62d6\u62d7\u62d8\u62d9\u62da\u62db\u62dc\u62dd\u62de\u62df\u62e0\u62e1\u62e2\u62e3\u62e4\u62e5\u62e6\u62e7\u62e8\u62e9\u62ea\u62eb\u62ec\u62ed\u62ee\u62ef\u62f0\u62f1\u62f2\u62f3\u62f4\u62f5\u62f6\u62f7\u62f8\u62f9\u62fa\u62fb\u62fc\u62fd\u62fe\u62ff\u6300\u6301\u6302\u6303\u6304\u6305\u6306\u6307\u6308\u6309\u630a\u630b\u630c\u630d\u630e\u630f\u6310\u6311\u6312\u6313\u6314\u6315\u6316\u6317\u6318\u6319\u631a\u631b\u631c\u631d\u631e\u631f\u6320\u6321\u6322\u6323\u6324\u6325\u6326\u6327\u6328\u6329\u632a\u632b\u632c\u632d\u632e\u632f\u6330\u6331\u6332\u6333\u6334\u6335\u6336\u6337\u6338\u6339\u633a\u633b\u633c\u633d\u633e\u633f\u6340\u6341\u6342\u6343\u6344\u6345\u6346\u6347\u6348\u6349\u634a\u634b\u634c\u634d\u634e\u634f\u6350\u6351\u6352\u6353\u6354\u6355\u6356\u6357\u6358\u6359\u635a\u635b\u635c\u635d\u635e\u635f\u6360\u6361\u6362\u6363\u6364\u6365\u6366\u6367\u6368\u6369\u636a\u636b\u636c\u636d\u636e\u636f\u6370\u6371\u6372\u6373\u6374\u6375\u6376\u6377\u6378\u6379\u637a\u637b\u637c\u637d\u637e\u637f\u6380\u6381\u6382\u6383\u6384\u6385\u6386\u6387\u6388\u6389\u638a\u638b\u638c\u638d\u638e\u638f\u6390\u6391\u6392\u6393\u6394\u6395\u6396\u6397\u6398\u6399\u639a\u639b\u639c\u639d\u639e\u639f\u63a0\u63a1\u63a2\u63a3\u63a4\u63a5\u63a6\u63a7\u63a8\u63a9\u63aa\u63ab\u63ac\u63ad\u63ae\u63af\u63b0\u63b1\u63b2\u63b3\u63b4\u63b5\u63b6\u63b7\u63b8\u63b9\u63ba\u63bb\u63bc\u63bd\u63be\u63bf\u63c0\u63c1\u63c2\u63c3\u63c4\u63c5\u63c6\u63c7\u63c8\u63c9\u63ca\u63cb\u63cc\u63cd\u63ce\u63cf\u63d0\u63d1\u63d2\u63d3\u63d4\u63d5\u63d6\u63d7\u63d8\u63d9\u63da\u63db\u63dc\u63dd\u63de\u63df\u63e0\u63e1\u63e2\u63e3\u63e4\u63e5\u63e6\u63e7\u63e8\u63e9\u63ea\u63eb\u63ec\u63ed\u63ee\u63ef\u63f0\u63f1\u63f2\u63f3\u63f4\u63f5\u63f6\u63f7\u63f8\u63f9\u63fa\u63fb\u63fc\u63fd\u63fe\u63ff\u6400\u6401\u6402\u6403\u6404\u6405\u6406\u6407\u6408\u6409\u640a\u640b\u640c\u640d\u640e\u640f\u6410\u6411\u6412\u6413\u6414\u6415\u6416\u6417\u6418\u6419\u641a\u641b\u641c\u641d\u641e\u641f\u6420\u6421\u6422\u6423\u6424\u6425\u6426\u6427\u6428\u6429\u642a\u642b\u642c\u642d\u642e\u642f\u6430\u6431\u6432\u6433\u6434\u6435\u6436\u6437\u6438\u6439\u643a\u643b\u643c\u643d\u643e\u643f\u6440\u6441\u6442\u6443\u6444\u6445\u6446\u6447\u6448\u6449\u644a\u644b\u644c\u644d\u644e\u644f\u6450\u6451\u6452\u6453\u6454\u6455\u6456\u6457\u6458\u6459\u645a\u645b\u645c\u645d\u645e\u645f\u6460\u6461\u6462\u6463\u6464\u6465\u6466\u6467\u6468\u6469\u646a\u646b\u646c\u646d\u646e\u646f\u6470\u6471\u6472\u6473\u6474\u6475\u6476\u6477\u6478\u6479\u647a\u647b\u647c\u647d\u647e\u647f\u6480\u6481\u6482\u6483\u6484\u6485\u6486\u6487\u6488\u6489\u648a\u648b\u648c\u648d\u648e\u648f\u6490\u6491\u6492\u6493\u6494\u6495\u6496\u6497\u6498\u6499\u649a\u649b\u649c\u649d\u649e\u649f\u64a0\u64a1\u64a2\u64a3\u64a4\u64a5\u64a6\u64a7\u64a8\u64a9\u64aa\u64ab\u64ac\u64ad\u64ae\u64af\u64b0\u64b1\u64b2\u64b3\u64b4\u64b5\u64b6\u64b7\u64b8\u64b9\u64ba\u64bb\u64bc\u64bd\u64be\u64bf\u64c0\u64c1\u64c2\u64c3\u64c4\u64c5\u64c6\u64c7\u64c8\u64c9\u64ca\u64cb\u64cc\u64cd\u64ce\u64cf\u64d0\u64d1\u64d2\u64d3\u64d4\u64d5\u64d6\u64d7\u64d8\u64d9\u64da\u64db\u64dc\u64dd\u64de\u64df\u64e0\u64e1\u64e2\u64e3\u64e4\u64e5\u64e6\u64e7\u64e8\u64e9\u64ea\u64eb\u64ec\u64ed\u64ee\u64ef\u64f0\u64f1\u64f2\u64f3\u64f4\u64f5\u64f6\u64f7\u64f8\u64f9\u64fa\u64fb\u64fc\u64fd\u64fe\u64ff\u6500\u6501\u6502\u6503\u6504\u6505\u6506\u6507\u6508\u6509\u650a\u650b\u650c\u650d\u650e\u650f\u6510\u6511\u6512\u6513\u6514\u6515\u6516\u6517\u6518\u6519\u651a\u651b\u651c\u651d\u651e\u651f\u6520\u6521\u6522\u6523\u6524\u6525\u6526\u6527\u6528\u6529\u652a\u652b\u652c\u652d\u652e\u652f\u6530\u6531\u6532\u6533\u6534\u6535\u6536\u6537\u6538\u6539\u653a\u653b\u653c\u653d\u653e\u653f\u6540\u6541\u6542\u6543\u6544\u6545\u6546\u6547\u6548\u6549\u654a\u654b\u654c\u654d\u654e\u654f\u6550\u6551\u6552\u6553\u6554\u6555\u6556\u6557\u6558\u6559\u655a\u655b\u655c\u655d\u655e\u655f\u6560\u6561\u6562\u6563\u6564\u6565\u6566\u6567\u6568\u6569\u656a\u656b\u656c\u656d\u656e\u656f\u6570\u6571\u6572\u6573\u6574\u6575\u6576\u6577\u6578\u6579\u657a\u657b\u657c\u657d\u657e\u657f\u6580\u6581\u6582\u6583\u6584\u6585\u6586\u6587\u6588\u6589\u658a\u658b\u658c\u658d\u658e\u658f\u6590\u6591\u6592\u6593\u6594\u6595\u6596\u6597\u6598\u6599\u659a\u659b\u659c\u659d\u659e\u659f\u65a0\u65a1\u65a2\u65a3\u65a4\u65a5\u65a6\u65a7\u65a8\u65a9\u65aa\u65ab\u65ac\u65ad\u65ae\u65af\u65b0\u65b1\u65b2\u65b3\u65b4\u65b5\u65b6\u65b7\u65b8\u65b9\u65ba\u65bb\u65bc\u65bd\u65be\u65bf\u65c0\u65c1\u65c2\u65c3\u65c4\u65c5\u65c6\u65c7\u65c8\u65c9\u65ca\u65cb\u65cc\u65cd\u65ce\u65cf\u65d0\u65d1\u65d2\u65d3\u65d4\u65d5\u65d6\u65d7\u65d8\u65d9\u65da\u65db\u65dc\u65dd\u65de\u65df\u65e0\u65e1\u65e2\u65e3\u65e4\u65e5\u65e6\u65e7\u65e8\u65e9\u65ea\u65eb\u65ec\u65ed\u65ee\u65ef\u65f0\u65f1\u65f2\u65f3\u65f4\u65f5\u65f6\u65f7\u65f8\u65f9\u65fa\u65fb\u65fc\u65fd\u65fe\u65ff\u6600\u6601\u6602\u6603\u6604\u6605\u6606\u6607\u6608\u6609\u660a\u660b\u660c\u660d\u660e\u660f\u6610\u6611\u6612\u6613\u6614\u6615\u6616\u6617\u6618\u6619\u661a\u661b\u661c\u661d\u661e\u661f\u6620\u6621\u6622\u6623\u6624\u6625\u6626\u6627\u6628\u6629\u662a\u662b\u662c\u662d\u662e\u662f\u6630\u6631\u6632\u6633\u6634\u6635\u6636\u6637\u6638\u6639\u663a\u663b\u663c\u663d\u663e\u663f\u6640\u6641\u6642\u6643\u6644\u6645\u6646\u6647\u6648\u6649\u664a\u664b\u664c\u664d\u664e\u664f\u6650\u6651\u6652\u6653\u6654\u6655\u6656\u6657\u6658\u6659\u665a\u665b\u665c\u665d\u665e\u665f\u6660\u6661\u6662\u6663\u6664\u6665\u6666\u6667\u6668\u6669\u666a\u666b\u666c\u666d\u666e\u666f\u6670\u6671\u6672\u6673\u6674\u6675\u6676\u6677\u6678\u6679\u667a\u667b\u667c\u667d\u667e\u667f\u6680\u6681\u6682\u6683\u6684\u6685\u6686\u6687\u6688\u6689\u668a\u668b\u668c\u668d\u668e\u668f\u6690\u6691\u6692\u6693\u6694\u6695\u6696\u6697\u6698\u6699\u669a\u669b\u669c\u669d\u669e\u669f\u66a0\u66a1\u66a2\u66a3\u66a4\u66a5\u66a6\u66a7\u66a8\u66a9\u66aa\u66ab\u66ac\u66ad\u66ae\u66af\u66b0\u66b1\u66b2\u66b3\u66b4\u66b5\u66b6\u66b7\u66b8\u66b9\u66ba\u66bb\u66bc\u66bd\u66be\u66bf\u66c0\u66c1\u66c2\u66c3\u66c4\u66c5\u66c6\u66c7\u66c8\u66c9\u66ca\u66cb\u66cc\u66cd\u66ce\u66cf\u66d0\u66d1\u66d2\u66d3\u66d4\u66d5\u66d6\u66d7\u66d8\u66d9\u66da\u66db\u66dc\u66dd\u66de\u66df\u66e0\u66e1\u66e2\u66e3\u66e4\u66e5\u66e6\u66e7\u66e8\u66e9\u66ea\u66eb\u66ec\u66ed\u66ee\u66ef\u66f0\u66f1\u66f2\u66f3\u66f4\u66f5\u66f6\u66f7\u66f8\u66f9\u66fa\u66fb\u66fc\u66fd\u66fe\u66ff\u6700\u6701\u6702\u6703\u6704\u6705\u6706\u6707\u6708\u6709\u670a\u670b\u670c\u670d\u670e\u670f\u6710\u6711\u6712\u6713\u6714\u6715\u6716\u6717\u6718\u6719\u671a\u671b\u671c\u671d\u671e\u671f\u6720\u6721\u6722\u6723\u6724\u6725\u6726\u6727\u6728\u6729\u672a\u672b\u672c\u672d\u672e\u672f\u6730\u6731\u6732\u6733\u6734\u6735\u6736\u6737\u6738\u6739\u673a\u673b\u673c\u673d\u673e\u673f\u6740\u6741\u6742\u6743\u6744\u6745\u6746\u6747\u6748\u6749\u674a\u674b\u674c\u674d\u674e\u674f\u6750\u6751\u6752\u6753\u6754\u6755\u6756\u6757\u6758\u6759\u675a\u675b\u675c\u675d\u675e\u675f\u6760\u6761\u6762\u6763\u6764\u6765\u6766\u6767\u6768\u6769\u676a\u676b\u676c\u676d\u676e\u676f\u6770\u6771\u6772\u6773\u6774\u6775\u6776\u6777\u6778\u6779\u677a\u677b\u677c\u677d\u677e\u677f\u6780\u6781\u6782\u6783\u6784\u6785\u6786\u6787\u6788\u6789\u678a\u678b\u678c\u678d\u678e\u678f\u6790\u6791\u6792\u6793\u6794\u6795\u6796\u6797\u6798\u6799\u679a\u679b\u679c\u679d\u679e\u679f\u67a0\u67a1\u67a2\u67a3\u67a4\u67a5\u67a6\u67a7\u67a8\u67a9\u67aa\u67ab\u67ac\u67ad\u67ae\u67af\u67b0\u67b1\u67b2\u67b3\u67b4\u67b5\u67b6\u67b7\u67b8\u67b9\u67ba\u67bb\u67bc\u67bd\u67be\u67bf\u67c0\u67c1\u67c2\u67c3\u67c4\u67c5\u67c6\u67c7\u67c8\u67c9\u67ca\u67cb\u67cc\u67cd\u67ce\u67cf\u67d0\u67d1\u67d2\u67d3\u67d4\u67d5\u67d6\u67d7\u67d8\u67d9\u67da\u67db\u67dc\u67dd\u67de\u67df\u67e0\u67e1\u67e2\u67e3\u67e4\u67e5\u67e6\u67e7\u67e8\u67e9\u67ea\u67eb\u67ec\u67ed\u67ee\u67ef\u67f0\u67f1\u67f2\u67f3\u67f4\u67f5\u67f6\u67f7\u67f8\u67f9\u67fa\u67fb\u67fc\u67fd\u67fe\u67ff\u6800\u6801\u6802\u6803\u6804\u6805\u6806\u6807\u6808\u6809\u680a\u680b\u680c\u680d\u680e\u680f\u6810\u6811\u6812\u6813\u6814\u6815\u6816\u6817\u6818\u6819\u681a\u681b\u681c\u681d\u681e\u681f\u6820\u6821\u6822\u6823\u6824\u6825\u6826\u6827\u6828\u6829\u682a\u682b\u682c\u682d\u682e\u682f\u6830\u6831\u6832\u6833\u6834\u6835\u6836\u6837\u6838\u6839\u683a\u683b\u683c\u683d\u683e\u683f\u6840\u6841\u6842\u6843\u6844\u6845\u6846\u6847\u6848\u6849\u684a\u684b\u684c\u684d\u684e\u684f\u6850\u6851\u6852\u6853\u6854\u6855\u6856\u6857\u6858\u6859\u685a\u685b\u685c\u685d\u685e\u685f\u6860\u6861\u6862\u6863\u6864\u6865\u6866\u6867\u6868\u6869\u686a\u686b\u686c\u686d\u686e\u686f\u6870\u6871\u6872\u6873\u6874\u6875\u6876\u6877\u6878\u6879\u687a\u687b\u687c\u687d\u687e\u687f\u6880\u6881\u6882\u6883\u6884\u6885\u6886\u6887\u6888\u6889\u688a\u688b\u688c\u688d\u688e\u688f\u6890\u6891\u6892\u6893\u6894\u6895\u6896\u6897\u6898\u6899\u689a\u689b\u689c\u689d\u689e\u689f\u68a0\u68a1\u68a2\u68a3\u68a4\u68a5\u68a6\u68a7\u68a8\u68a9\u68aa\u68ab\u68ac\u68ad\u68ae\u68af\u68b0\u68b1\u68b2\u68b3\u68b4\u68b5\u68b6\u68b7\u68b8\u68b9\u68ba\u68bb\u68bc\u68bd\u68be\u68bf\u68c0\u68c1\u68c2\u68c3\u68c4\u68c5\u68c6\u68c7\u68c8\u68c9\u68ca\u68cb\u68cc\u68cd\u68ce\u68cf\u68d0\u68d1\u68d2\u68d3\u68d4\u68d5\u68d6\u68d7\u68d8\u68d9\u68da\u68db\u68dc\u68dd\u68de\u68df\u68e0\u68e1\u68e2\u68e3\u68e4\u68e5\u68e6\u68e7\u68e8\u68e9\u68ea\u68eb\u68ec\u68ed\u68ee\u68ef\u68f0\u68f1\u68f2\u68f3\u68f4\u68f5\u68f6\u68f7\u68f8\u68f9\u68fa\u68fb\u68fc\u68fd\u68fe\u68ff\u6900\u6901\u6902\u6903\u6904\u6905\u6906\u6907\u6908\u6909\u690a\u690b\u690c\u690d\u690e\u690f\u6910\u6911\u6912\u6913\u6914\u6915\u6916\u6917\u6918\u6919\u691a\u691b\u691c\u691d\u691e\u691f\u6920\u6921\u6922\u6923\u6924\u6925\u6926\u6927\u6928\u6929\u692a\u692b\u692c\u692d\u692e\u692f\u6930\u6931\u6932\u6933\u6934\u6935\u6936\u6937\u6938\u6939\u693a\u693b\u693c\u693d\u693e\u693f\u6940\u6941\u6942\u6943\u6944\u6945\u6946\u6947\u6948\u6949\u694a\u694b\u694c\u694d\u694e\u694f\u6950\u6951\u6952\u6953\u6954\u6955\u6956\u6957\u6958\u6959\u695a\u695b\u695c\u695d\u695e\u695f\u6960\u6961\u6962\u6963\u6964\u6965\u6966\u6967\u6968\u6969\u696a\u696b\u696c\u696d\u696e\u696f\u6970\u6971\u6972\u6973\u6974\u6975\u6976\u6977\u6978\u6979\u697a\u697b\u697c\u697d\u697e\u697f\u6980\u6981\u6982\u6983\u6984\u6985\u6986\u6987\u6988\u6989\u698a\u698b\u698c\u698d\u698e\u698f\u6990\u6991\u6992\u6993\u6994\u6995\u6996\u6997\u6998\u6999\u699a\u699b\u699c\u699d\u699e\u699f\u69a0\u69a1\u69a2\u69a3\u69a4\u69a5\u69a6\u69a7\u69a8\u69a9\u69aa\u69ab\u69ac\u69ad\u69ae\u69af\u69b0\u69b1\u69b2\u69b3\u69b4\u69b5\u69b6\u69b7\u69b8\u69b9\u69ba\u69bb\u69bc\u69bd\u69be\u69bf\u69c0\u69c1\u69c2\u69c3\u69c4\u69c5\u69c6\u69c7\u69c8\u69c9\u69ca\u69cb\u69cc\u69cd\u69ce\u69cf\u69d0\u69d1\u69d2\u69d3\u69d4\u69d5\u69d6\u69d7\u69d8\u69d9\u69da\u69db\u69dc\u69dd\u69de\u69df\u69e0\u69e1\u69e2\u69e3\u69e4\u69e5\u69e6\u69e7\u69e8\u69e9\u69ea\u69eb\u69ec\u69ed\u69ee\u69ef\u69f0\u69f1\u69f2\u69f3\u69f4\u69f5\u69f6\u69f7\u69f8\u69f9\u69fa\u69fb\u69fc\u69fd\u69fe\u69ff\u6a00\u6a01\u6a02\u6a03\u6a04\u6a05\u6a06\u6a07\u6a08\u6a09\u6a0a\u6a0b\u6a0c\u6a0d\u6a0e\u6a0f\u6a10\u6a11\u6a12\u6a13\u6a14\u6a15\u6a16\u6a17\u6a18\u6a19\u6a1a\u6a1b\u6a1c\u6a1d\u6a1e\u6a1f\u6a20\u6a21\u6a22\u6a23\u6a24\u6a25\u6a26\u6a27\u6a28\u6a29\u6a2a\u6a2b\u6a2c\u6a2d\u6a2e\u6a2f\u6a30\u6a31\u6a32\u6a33\u6a34\u6a35\u6a36\u6a37\u6a38\u6a39\u6a3a\u6a3b\u6a3c\u6a3d\u6a3e\u6a3f\u6a40\u6a41\u6a42\u6a43\u6a44\u6a45\u6a46\u6a47\u6a48\u6a49\u6a4a\u6a4b\u6a4c\u6a4d\u6a4e\u6a4f\u6a50\u6a51\u6a52\u6a53\u6a54\u6a55\u6a56\u6a57\u6a58\u6a59\u6a5a\u6a5b\u6a5c\u6a5d\u6a5e\u6a5f\u6a60\u6a61\u6a62\u6a63\u6a64\u6a65\u6a66\u6a67\u6a68\u6a69\u6a6a\u6a6b\u6a6c\u6a6d\u6a6e\u6a6f\u6a70\u6a71\u6a72\u6a73\u6a74\u6a75\u6a76\u6a77\u6a78\u6a79\u6a7a\u6a7b\u6a7c\u6a7d\u6a7e\u6a7f\u6a80\u6a81\u6a82\u6a83\u6a84\u6a85\u6a86\u6a87\u6a88\u6a89\u6a8a\u6a8b\u6a8c\u6a8d\u6a8e\u6a8f\u6a90\u6a91\u6a92\u6a93\u6a94\u6a95\u6a96\u6a97\u6a98\u6a99\u6a9a\u6a9b\u6a9c\u6a9d\u6a9e\u6a9f\u6aa0\u6aa1\u6aa2\u6aa3\u6aa4\u6aa5\u6aa6\u6aa7\u6aa8\u6aa9\u6aaa\u6aab\u6aac\u6aad\u6aae\u6aaf\u6ab0\u6ab1\u6ab2\u6ab3\u6ab4\u6ab5\u6ab6\u6ab7\u6ab8\u6ab9\u6aba\u6abb\u6abc\u6abd\u6abe\u6abf\u6ac0\u6ac1\u6ac2\u6ac3\u6ac4\u6ac5\u6ac6\u6ac7\u6ac8\u6ac9\u6aca\u6acb\u6acc\u6acd\u6ace\u6acf\u6ad0\u6ad1\u6ad2\u6ad3\u6ad4\u6ad5\u6ad6\u6ad7\u6ad8\u6ad9\u6ada\u6adb\u6adc\u6add\u6ade\u6adf\u6ae0\u6ae1\u6ae2\u6ae3\u6ae4\u6ae5\u6ae6\u6ae7\u6ae8\u6ae9\u6aea\u6aeb\u6aec\u6aed\u6aee\u6aef\u6af0\u6af1\u6af2\u6af3\u6af4\u6af5\u6af6\u6af7\u6af8\u6af9\u6afa\u6afb\u6afc\u6afd\u6afe\u6aff\u6b00\u6b01\u6b02\u6b03\u6b04\u6b05\u6b06\u6b07\u6b08\u6b09\u6b0a\u6b0b\u6b0c\u6b0d\u6b0e\u6b0f\u6b10\u6b11\u6b12\u6b13\u6b14\u6b15\u6b16\u6b17\u6b18\u6b19\u6b1a\u6b1b\u6b1c\u6b1d\u6b1e\u6b1f\u6b20\u6b21\u6b22\u6b23\u6b24\u6b25\u6b26\u6b27\u6b28\u6b29\u6b2a\u6b2b\u6b2c\u6b2d\u6b2e\u6b2f\u6b30\u6b31\u6b32\u6b33\u6b34\u6b35\u6b36\u6b37\u6b38\u6b39\u6b3a\u6b3b\u6b3c\u6b3d\u6b3e\u6b3f\u6b40\u6b41\u6b42\u6b43\u6b44\u6b45\u6b46\u6b47\u6b48\u6b49\u6b4a\u6b4b\u6b4c\u6b4d\u6b4e\u6b4f\u6b50\u6b51\u6b52\u6b53\u6b54\u6b55\u6b56\u6b57\u6b58\u6b59\u6b5a\u6b5b\u6b5c\u6b5d\u6b5e\u6b5f\u6b60\u6b61\u6b62\u6b63\u6b64\u6b65\u6b66\u6b67\u6b68\u6b69\u6b6a\u6b6b\u6b6c\u6b6d\u6b6e\u6b6f\u6b70\u6b71\u6b72\u6b73\u6b74\u6b75\u6b76\u6b77\u6b78\u6b79\u6b7a\u6b7b\u6b7c\u6b7d\u6b7e\u6b7f\u6b80\u6b81\u6b82\u6b83\u6b84\u6b85\u6b86\u6b87\u6b88\u6b89\u6b8a\u6b8b\u6b8c\u6b8d\u6b8e\u6b8f\u6b90\u6b91\u6b92\u6b93\u6b94\u6b95\u6b96\u6b97\u6b98\u6b99\u6b9a\u6b9b\u6b9c\u6b9d\u6b9e\u6b9f\u6ba0\u6ba1\u6ba2\u6ba3\u6ba4\u6ba5\u6ba6\u6ba7\u6ba8\u6ba9\u6baa\u6bab\u6bac\u6bad\u6bae\u6baf\u6bb0\u6bb1\u6bb2\u6bb3\u6bb4\u6bb5\u6bb6\u6bb7\u6bb8\u6bb9\u6bba\u6bbb\u6bbc\u6bbd\u6bbe\u6bbf\u6bc0\u6bc1\u6bc2\u6bc3\u6bc4\u6bc5\u6bc6\u6bc7\u6bc8\u6bc9\u6bca\u6bcb\u6bcc\u6bcd\u6bce\u6bcf\u6bd0\u6bd1\u6bd2\u6bd3\u6bd4\u6bd5\u6bd6\u6bd7\u6bd8\u6bd9\u6bda\u6bdb\u6bdc\u6bdd\u6bde\u6bdf\u6be0\u6be1\u6be2\u6be3\u6be4\u6be5\u6be6\u6be7\u6be8\u6be9\u6bea\u6beb\u6bec\u6bed\u6bee\u6bef\u6bf0\u6bf1\u6bf2\u6bf3\u6bf4\u6bf5\u6bf6\u6bf7\u6bf8\u6bf9\u6bfa\u6bfb\u6bfc\u6bfd\u6bfe\u6bff\u6c00\u6c01\u6c02\u6c03\u6c04\u6c05\u6c06\u6c07\u6c08\u6c09\u6c0a\u6c0b\u6c0c\u6c0d\u6c0e\u6c0f\u6c10\u6c11\u6c12\u6c13\u6c14\u6c15\u6c16\u6c17\u6c18\u6c19\u6c1a\u6c1b\u6c1c\u6c1d\u6c1e\u6c1f\u6c20\u6c21\u6c22\u6c23\u6c24\u6c25\u6c26\u6c27\u6c28\u6c29\u6c2a\u6c2b\u6c2c\u6c2d\u6c2e\u6c2f\u6c30\u6c31\u6c32\u6c33\u6c34\u6c35\u6c36\u6c37\u6c38\u6c39\u6c3a\u6c3b\u6c3c\u6c3d\u6c3e\u6c3f\u6c40\u6c41\u6c42\u6c43\u6c44\u6c45\u6c46\u6c47\u6c48\u6c49\u6c4a\u6c4b\u6c4c\u6c4d\u6c4e\u6c4f\u6c50\u6c51\u6c52\u6c53\u6c54\u6c55\u6c56\u6c57\u6c58\u6c59\u6c5a\u6c5b\u6c5c\u6c5d\u6c5e\u6c5f\u6c60\u6c61\u6c62\u6c63\u6c64\u6c65\u6c66\u6c67\u6c68\u6c69\u6c6a\u6c6b\u6c6c\u6c6d\u6c6e\u6c6f\u6c70\u6c71\u6c72\u6c73\u6c74\u6c75\u6c76\u6c77\u6c78\u6c79\u6c7a\u6c7b\u6c7c\u6c7d\u6c7e\u6c7f\u6c80\u6c81\u6c82\u6c83\u6c84\u6c85\u6c86\u6c87\u6c88\u6c89\u6c8a\u6c8b\u6c8c\u6c8d\u6c8e\u6c8f\u6c90\u6c91\u6c92\u6c93\u6c94\u6c95\u6c96\u6c97\u6c98\u6c99\u6c9a\u6c9b\u6c9c\u6c9d\u6c9e\u6c9f\u6ca0\u6ca1\u6ca2\u6ca3\u6ca4\u6ca5\u6ca6\u6ca7\u6ca8\u6ca9\u6caa\u6cab\u6cac\u6cad\u6cae\u6caf\u6cb0\u6cb1\u6cb2\u6cb3\u6cb4\u6cb5\u6cb6\u6cb7\u6cb8\u6cb9\u6cba\u6cbb\u6cbc\u6cbd\u6cbe\u6cbf\u6cc0\u6cc1\u6cc2\u6cc3\u6cc4\u6cc5\u6cc6\u6cc7\u6cc8\u6cc9\u6cca\u6ccb\u6ccc\u6ccd\u6cce\u6ccf\u6cd0\u6cd1\u6cd2\u6cd3\u6cd4\u6cd5\u6cd6\u6cd7\u6cd8\u6cd9\u6cda\u6cdb\u6cdc\u6cdd\u6cde\u6cdf\u6ce0\u6ce1\u6ce2\u6ce3\u6ce4\u6ce5\u6ce6\u6ce7\u6ce8\u6ce9\u6cea\u6ceb\u6cec\u6ced\u6cee\u6cef\u6cf0\u6cf1\u6cf2\u6cf3\u6cf4\u6cf5\u6cf6\u6cf7\u6cf8\u6cf9\u6cfa\u6cfb\u6cfc\u6cfd\u6cfe\u6cff\u6d00\u6d01\u6d02\u6d03\u6d04\u6d05\u6d06\u6d07\u6d08\u6d09\u6d0a\u6d0b\u6d0c\u6d0d\u6d0e\u6d0f\u6d10\u6d11\u6d12\u6d13\u6d14\u6d15\u6d16\u6d17\u6d18\u6d19\u6d1a\u6d1b\u6d1c\u6d1d\u6d1e\u6d1f\u6d20\u6d21\u6d22\u6d23\u6d24\u6d25\u6d26\u6d27\u6d28\u6d29\u6d2a\u6d2b\u6d2c\u6d2d\u6d2e\u6d2f\u6d30\u6d31\u6d32\u6d33\u6d34\u6d35\u6d36\u6d37\u6d38\u6d39\u6d3a\u6d3b\u6d3c\u6d3d\u6d3e\u6d3f\u6d40\u6d41\u6d42\u6d43\u6d44\u6d45\u6d46\u6d47\u6d48\u6d49\u6d4a\u6d4b\u6d4c\u6d4d\u6d4e\u6d4f\u6d50\u6d51\u6d52\u6d53\u6d54\u6d55\u6d56\u6d57\u6d58\u6d59\u6d5a\u6d5b\u6d5c\u6d5d\u6d5e\u6d5f\u6d60\u6d61\u6d62\u6d63\u6d64\u6d65\u6d66\u6d67\u6d68\u6d69\u6d6a\u6d6b\u6d6c\u6d6d\u6d6e\u6d6f\u6d70\u6d71\u6d72\u6d73\u6d74\u6d75\u6d76\u6d77\u6d78\u6d79\u6d7a\u6d7b\u6d7c\u6d7d\u6d7e\u6d7f\u6d80\u6d81\u6d82\u6d83\u6d84\u6d85\u6d86\u6d87\u6d88\u6d89\u6d8a\u6d8b\u6d8c\u6d8d\u6d8e\u6d8f\u6d90\u6d91\u6d92\u6d93\u6d94\u6d95\u6d96\u6d97\u6d98\u6d99\u6d9a\u6d9b\u6d9c\u6d9d\u6d9e\u6d9f\u6da0\u6da1\u6da2\u6da3\u6da4\u6da5\u6da6\u6da7\u6da8\u6da9\u6daa\u6dab\u6dac\u6dad\u6dae\u6daf\u6db0\u6db1\u6db2\u6db3\u6db4\u6db5\u6db6\u6db7\u6db8\u6db9\u6dba\u6dbb\u6dbc\u6dbd\u6dbe\u6dbf\u6dc0\u6dc1\u6dc2\u6dc3\u6dc4\u6dc5\u6dc6\u6dc7\u6dc8\u6dc9\u6dca\u6dcb\u6dcc\u6dcd\u6dce\u6dcf\u6dd0\u6dd1\u6dd2\u6dd3\u6dd4\u6dd5\u6dd6\u6dd7\u6dd8\u6dd9\u6dda\u6ddb\u6ddc\u6ddd\u6dde\u6ddf\u6de0\u6de1\u6de2\u6de3\u6de4\u6de5\u6de6\u6de7\u6de8\u6de9\u6dea\u6deb\u6dec\u6ded\u6dee\u6def\u6df0\u6df1\u6df2\u6df3\u6df4\u6df5\u6df6\u6df7\u6df8\u6df9\u6dfa\u6dfb\u6dfc\u6dfd\u6dfe\u6dff\u6e00\u6e01\u6e02\u6e03\u6e04\u6e05\u6e06\u6e07\u6e08\u6e09\u6e0a\u6e0b\u6e0c\u6e0d\u6e0e\u6e0f\u6e10\u6e11\u6e12\u6e13\u6e14\u6e15\u6e16\u6e17\u6e18\u6e19\u6e1a\u6e1b\u6e1c\u6e1d\u6e1e\u6e1f\u6e20\u6e21\u6e22\u6e23\u6e24\u6e25\u6e26\u6e27\u6e28\u6e29\u6e2a\u6e2b\u6e2c\u6e2d\u6e2e\u6e2f\u6e30\u6e31\u6e32\u6e33\u6e34\u6e35\u6e36\u6e37\u6e38\u6e39\u6e3a\u6e3b\u6e3c\u6e3d\u6e3e\u6e3f\u6e40\u6e41\u6e42\u6e43\u6e44\u6e45\u6e46\u6e47\u6e48\u6e49\u6e4a\u6e4b\u6e4c\u6e4d\u6e4e\u6e4f\u6e50\u6e51\u6e52\u6e53\u6e54\u6e55\u6e56\u6e57\u6e58\u6e59\u6e5a\u6e5b\u6e5c\u6e5d\u6e5e\u6e5f\u6e60\u6e61\u6e62\u6e63\u6e64\u6e65\u6e66\u6e67\u6e68\u6e69\u6e6a\u6e6b\u6e6c\u6e6d\u6e6e\u6e6f\u6e70\u6e71\u6e72\u6e73\u6e74\u6e75\u6e76\u6e77\u6e78\u6e79\u6e7a\u6e7b\u6e7c\u6e7d\u6e7e\u6e7f\u6e80\u6e81\u6e82\u6e83\u6e84\u6e85\u6e86\u6e87\u6e88\u6e89\u6e8a\u6e8b\u6e8c\u6e8d\u6e8e\u6e8f\u6e90\u6e91\u6e92\u6e93\u6e94\u6e95\u6e96\u6e97\u6e98\u6e99\u6e9a\u6e9b\u6e9c\u6e9d\u6e9e\u6e9f\u6ea0\u6ea1\u6ea2\u6ea3\u6ea4\u6ea5\u6ea6\u6ea7\u6ea8\u6ea9\u6eaa\u6eab\u6eac\u6ead\u6eae\u6eaf\u6eb0\u6eb1\u6eb2\u6eb3\u6eb4\u6eb5\u6eb6\u6eb7\u6eb8\u6eb9\u6eba\u6ebb\u6ebc\u6ebd\u6ebe\u6ebf\u6ec0\u6ec1\u6ec2\u6ec3\u6ec4\u6ec5\u6ec6\u6ec7\u6ec8\u6ec9\u6eca\u6ecb\u6ecc\u6ecd\u6ece\u6ecf\u6ed0\u6ed1\u6ed2\u6ed3\u6ed4\u6ed5\u6ed6\u6ed7\u6ed8\u6ed9\u6eda\u6edb\u6edc\u6edd\u6ede\u6edf\u6ee0\u6ee1\u6ee2\u6ee3\u6ee4\u6ee5\u6ee6\u6ee7\u6ee8\u6ee9\u6eea\u6eeb\u6eec\u6eed\u6eee\u6eef\u6ef0\u6ef1\u6ef2\u6ef3\u6ef4\u6ef5\u6ef6\u6ef7\u6ef8\u6ef9\u6efa\u6efb\u6efc\u6efd\u6efe\u6eff\u6f00\u6f01\u6f02\u6f03\u6f04\u6f05\u6f06\u6f07\u6f08\u6f09\u6f0a\u6f0b\u6f0c\u6f0d\u6f0e\u6f0f\u6f10\u6f11\u6f12\u6f13\u6f14\u6f15\u6f16\u6f17\u6f18\u6f19\u6f1a\u6f1b\u6f1c\u6f1d\u6f1e\u6f1f\u6f20\u6f21\u6f22\u6f23\u6f24\u6f25\u6f26\u6f27\u6f28\u6f29\u6f2a\u6f2b\u6f2c\u6f2d\u6f2e\u6f2f\u6f30\u6f31\u6f32\u6f33\u6f34\u6f35\u6f36\u6f37\u6f38\u6f39\u6f3a\u6f3b\u6f3c\u6f3d\u6f3e\u6f3f\u6f40\u6f41\u6f42\u6f43\u6f44\u6f45\u6f46\u6f47\u6f48\u6f49\u6f4a\u6f4b\u6f4c\u6f4d\u6f4e\u6f4f\u6f50\u6f51\u6f52\u6f53\u6f54\u6f55\u6f56\u6f57\u6f58\u6f59\u6f5a\u6f5b\u6f5c\u6f5d\u6f5e\u6f5f\u6f60\u6f61\u6f62\u6f63\u6f64\u6f65\u6f66\u6f67\u6f68\u6f69\u6f6a\u6f6b\u6f6c\u6f6d\u6f6e\u6f6f\u6f70\u6f71\u6f72\u6f73\u6f74\u6f75\u6f76\u6f77\u6f78\u6f79\u6f7a\u6f7b\u6f7c\u6f7d\u6f7e\u6f7f\u6f80\u6f81\u6f82\u6f83\u6f84\u6f85\u6f86\u6f87\u6f88\u6f89\u6f8a\u6f8b\u6f8c\u6f8d\u6f8e\u6f8f\u6f90\u6f91\u6f92\u6f93\u6f94\u6f95\u6f96\u6f97\u6f98\u6f99\u6f9a\u6f9b\u6f9c\u6f9d\u6f9e\u6f9f\u6fa0\u6fa1\u6fa2\u6fa3\u6fa4\u6fa5\u6fa6\u6fa7\u6fa8\u6fa9\u6faa\u6fab\u6fac\u6fad\u6fae\u6faf\u6fb0\u6fb1\u6fb2\u6fb3\u6fb4\u6fb5\u6fb6\u6fb7\u6fb8\u6fb9\u6fba\u6fbb\u6fbc\u6fbd\u6fbe\u6fbf\u6fc0\u6fc1\u6fc2\u6fc3\u6fc4\u6fc5\u6fc6\u6fc7\u6fc8\u6fc9\u6fca\u6fcb\u6fcc\u6fcd\u6fce\u6fcf\u6fd0\u6fd1\u6fd2\u6fd3\u6fd4\u6fd5\u6fd6\u6fd7\u6fd8\u6fd9\u6fda\u6fdb\u6fdc\u6fdd\u6fde\u6fdf\u6fe0\u6fe1\u6fe2\u6fe3\u6fe4\u6fe5\u6fe6\u6fe7\u6fe8\u6fe9\u6fea\u6feb\u6fec\u6fed\u6fee\u6fef\u6ff0\u6ff1\u6ff2\u6ff3\u6ff4\u6ff5\u6ff6\u6ff7\u6ff8\u6ff9\u6ffa\u6ffb\u6ffc\u6ffd\u6ffe\u6fff\u7000\u7001\u7002\u7003\u7004\u7005\u7006\u7007\u7008\u7009\u700a\u700b\u700c\u700d\u700e\u700f\u7010\u7011\u7012\u7013\u7014\u7015\u7016\u7017\u7018\u7019\u701a\u701b\u701c\u701d\u701e\u701f\u7020\u7021\u7022\u7023\u7024\u7025\u7026\u7027\u7028\u7029\u702a\u702b\u702c\u702d\u702e\u702f\u7030\u7031\u7032\u7033\u7034\u7035\u7036\u7037\u7038\u7039\u703a\u703b\u703c\u703d\u703e\u703f\u7040\u7041\u7042\u7043\u7044\u7045\u7046\u7047\u7048\u7049\u704a\u704b\u704c\u704d\u704e\u704f\u7050\u7051\u7052\u7053\u7054\u7055\u7056\u7057\u7058\u7059\u705a\u705b\u705c\u705d\u705e\u705f\u7060\u7061\u7062\u7063\u7064\u7065\u7066\u7067\u7068\u7069\u706a\u706b\u706c\u706d\u706e\u706f\u7070\u7071\u7072\u7073\u7074\u7075\u7076\u7077\u7078\u7079\u707a\u707b\u707c\u707d\u707e\u707f\u7080\u7081\u7082\u7083\u7084\u7085\u7086\u7087\u7088\u7089\u708a\u708b\u708c\u708d\u708e\u708f\u7090\u7091\u7092\u7093\u7094\u7095\u7096\u7097\u7098\u7099\u709a\u709b\u709c\u709d\u709e\u709f\u70a0\u70a1\u70a2\u70a3\u70a4\u70a5\u70a6\u70a7\u70a8\u70a9\u70aa\u70ab\u70ac\u70ad\u70ae\u70af\u70b0\u70b1\u70b2\u70b3\u70b4\u70b5\u70b6\u70b7\u70b8\u70b9\u70ba\u70bb\u70bc\u70bd\u70be\u70bf\u70c0\u70c1\u70c2\u70c3\u70c4\u70c5\u70c6\u70c7\u70c8\u70c9\u70ca\u70cb\u70cc\u70cd\u70ce\u70cf\u70d0\u70d1\u70d2\u70d3\u70d4\u70d5\u70d6\u70d7\u70d8\u70d9\u70da\u70db\u70dc\u70dd\u70de\u70df\u70e0\u70e1\u70e2\u70e3\u70e4\u70e5\u70e6\u70e7\u70e8\u70e9\u70ea\u70eb\u70ec\u70ed\u70ee\u70ef\u70f0\u70f1\u70f2\u70f3\u70f4\u70f5\u70f6\u70f7\u70f8\u70f9\u70fa\u70fb\u70fc\u70fd\u70fe\u70ff\u7100\u7101\u7102\u7103\u7104\u7105\u7106\u7107\u7108\u7109\u710a\u710b\u710c\u710d\u710e\u710f\u7110\u7111\u7112\u7113\u7114\u7115\u7116\u7117\u7118\u7119\u711a\u711b\u711c\u711d\u711e\u711f\u7120\u7121\u7122\u7123\u7124\u7125\u7126\u7127\u7128\u7129\u712a\u712b\u712c\u712d\u712e\u712f\u7130\u7131\u7132\u7133\u7134\u7135\u7136\u7137\u7138\u7139\u713a\u713b\u713c\u713d\u713e\u713f\u7140\u7141\u7142\u7143\u7144\u7145\u7146\u7147\u7148\u7149\u714a\u714b\u714c\u714d\u714e\u714f\u7150\u7151\u7152\u7153\u7154\u7155\u7156\u7157\u7158\u7159\u715a\u715b\u715c\u715d\u715e\u715f\u7160\u7161\u7162\u7163\u7164\u7165\u7166\u7167\u7168\u7169\u716a\u716b\u716c\u716d\u716e\u716f\u7170\u7171\u7172\u7173\u7174\u7175\u7176\u7177\u7178\u7179\u717a\u717b\u717c\u717d\u717e\u717f\u7180\u7181\u7182\u7183\u7184\u7185\u7186\u7187\u7188\u7189\u718a\u718b\u718c\u718d\u718e\u718f\u7190\u7191\u7192\u7193\u7194\u7195\u7196\u7197\u7198\u7199\u719a\u719b\u719c\u719d\u719e\u719f\u71a0\u71a1\u71a2\u71a3\u71a4\u71a5\u71a6\u71a7\u71a8\u71a9\u71aa\u71ab\u71ac\u71ad\u71ae\u71af\u71b0\u71b1\u71b2\u71b3\u71b4\u71b5\u71b6\u71b7\u71b8\u71b9\u71ba\u71bb\u71bc\u71bd\u71be\u71bf\u71c0\u71c1\u71c2\u71c3\u71c4\u71c5\u71c6\u71c7\u71c8\u71c9\u71ca\u71cb\u71cc\u71cd\u71ce\u71cf\u71d0\u71d1\u71d2\u71d3\u71d4\u71d5\u71d6\u71d7\u71d8\u71d9\u71da\u71db\u71dc\u71dd\u71de\u71df\u71e0\u71e1\u71e2\u71e3\u71e4\u71e5\u71e6\u71e7\u71e8\u71e9\u71ea\u71eb\u71ec\u71ed\u71ee\u71ef\u71f0\u71f1\u71f2\u71f3\u71f4\u71f5\u71f6\u71f7\u71f8\u71f9\u71fa\u71fb\u71fc\u71fd\u71fe\u71ff\u7200\u7201\u7202\u7203\u7204\u7205\u7206\u7207\u7208\u7209\u720a\u720b\u720c\u720d\u720e\u720f\u7210\u7211\u7212\u7213\u7214\u7215\u7216\u7217\u7218\u7219\u721a\u721b\u721c\u721d\u721e\u721f\u7220\u7221\u7222\u7223\u7224\u7225\u7226\u7227\u7228\u7229\u722a\u722b\u722c\u722d\u722e\u722f\u7230\u7231\u7232\u7233\u7234\u7235\u7236\u7237\u7238\u7239\u723a\u723b\u723c\u723d\u723e\u723f\u7240\u7241\u7242\u7243\u7244\u7245\u7246\u7247\u7248\u7249\u724a\u724b\u724c\u724d\u724e\u724f\u7250\u7251\u7252\u7253\u7254\u7255\u7256\u7257\u7258\u7259\u725a\u725b\u725c\u725d\u725e\u725f\u7260\u7261\u7262\u7263\u7264\u7265\u7266\u7267\u7268\u7269\u726a\u726b\u726c\u726d\u726e\u726f\u7270\u7271\u7272\u7273\u7274\u7275\u7276\u7277\u7278\u7279\u727a\u727b\u727c\u727d\u727e\u727f\u7280\u7281\u7282\u7283\u7284\u7285\u7286\u7287\u7288\u7289\u728a\u728b\u728c\u728d\u728e\u728f\u7290\u7291\u7292\u7293\u7294\u7295\u7296\u7297\u7298\u7299\u729a\u729b\u729c\u729d\u729e\u729f\u72a0\u72a1\u72a2\u72a3\u72a4\u72a5\u72a6\u72a7\u72a8\u72a9\u72aa\u72ab\u72ac\u72ad\u72ae\u72af\u72b0\u72b1\u72b2\u72b3\u72b4\u72b5\u72b6\u72b7\u72b8\u72b9\u72ba\u72bb\u72bc\u72bd\u72be\u72bf\u72c0\u72c1\u72c2\u72c3\u72c4\u72c5\u72c6\u72c7\u72c8\u72c9\u72ca\u72cb\u72cc\u72cd\u72ce\u72cf\u72d0\u72d1\u72d2\u72d3\u72d4\u72d5\u72d6\u72d7\u72d8\u72d9\u72da\u72db\u72dc\u72dd\u72de\u72df\u72e0\u72e1\u72e2\u72e3\u72e4\u72e5\u72e6\u72e7\u72e8\u72e9\u72ea\u72eb\u72ec\u72ed\u72ee\u72ef\u72f0\u72f1\u72f2\u72f3\u72f4\u72f5\u72f6\u72f7\u72f8\u72f9\u72fa\u72fb\u72fc\u72fd\u72fe\u72ff\u7300\u7301\u7302\u7303\u7304\u7305\u7306\u7307\u7308\u7309\u730a\u730b\u730c\u730d\u730e\u730f\u7310\u7311\u7312\u7313\u7314\u7315\u7316\u7317\u7318\u7319\u731a\u731b\u731c\u731d\u731e\u731f\u7320\u7321\u7322\u7323\u7324\u7325\u7326\u7327\u7328\u7329\u732a\u732b\u732c\u732d\u732e\u732f\u7330\u7331\u7332\u7333\u7334\u7335\u7336\u7337\u7338\u7339\u733a\u733b\u733c\u733d\u733e\u733f\u7340\u7341\u7342\u7343\u7344\u7345\u7346\u7347\u7348\u7349\u734a\u734b\u734c\u734d\u734e\u734f\u7350\u7351\u7352\u7353\u7354\u7355\u7356\u7357\u7358\u7359\u735a\u735b\u735c\u735d\u735e\u735f\u7360\u7361\u7362\u7363\u7364\u7365\u7366\u7367\u7368\u7369\u736a\u736b\u736c\u736d\u736e\u736f\u7370\u7371\u7372\u7373\u7374\u7375\u7376\u7377\u7378\u7379\u737a\u737b\u737c\u737d\u737e\u737f\u7380\u7381\u7382\u7383\u7384\u7385\u7386\u7387\u7388\u7389\u738a\u738b\u738c\u738d\u738e\u738f\u7390\u7391\u7392\u7393\u7394\u7395\u7396\u7397\u7398\u7399\u739a\u739b\u739c\u739d\u739e\u739f\u73a0\u73a1\u73a2\u73a3\u73a4\u73a5\u73a6\u73a7\u73a8\u73a9\u73aa\u73ab\u73ac\u73ad\u73ae\u73af\u73b0\u73b1\u73b2\u73b3\u73b4\u73b5\u73b6\u73b7\u73b8\u73b9\u73ba\u73bb\u73bc\u73bd\u73be\u73bf\u73c0\u73c1\u73c2\u73c3\u73c4\u73c5\u73c6\u73c7\u73c8\u73c9\u73ca\u73cb\u73cc\u73cd\u73ce\u73cf\u73d0\u73d1\u73d2\u73d3\u73d4\u73d5\u73d6\u73d7\u73d8\u73d9\u73da\u73db\u73dc\u73dd\u73de\u73df\u73e0\u73e1\u73e2\u73e3\u73e4\u73e5\u73e6\u73e7\u73e8\u73e9\u73ea\u73eb\u73ec\u73ed\u73ee\u73ef\u73f0\u73f1\u73f2\u73f3\u73f4\u73f5\u73f6\u73f7\u73f8\u73f9\u73fa\u73fb\u73fc\u73fd\u73fe\u73ff\u7400\u7401\u7402\u7403\u7404\u7405\u7406\u7407\u7408\u7409\u740a\u740b\u740c\u740d\u740e\u740f\u7410\u7411\u7412\u7413\u7414\u7415\u7416\u7417\u7418\u7419\u741a\u741b\u741c\u741d\u741e\u741f\u7420\u7421\u7422\u7423\u7424\u7425\u7426\u7427\u7428\u7429\u742a\u742b\u742c\u742d\u742e\u742f\u7430\u7431\u7432\u7433\u7434\u7435\u7436\u7437\u7438\u7439\u743a\u743b\u743c\u743d\u743e\u743f\u7440\u7441\u7442\u7443\u7444\u7445\u7446\u7447\u7448\u7449\u744a\u744b\u744c\u744d\u744e\u744f\u7450\u7451\u7452\u7453\u7454\u7455\u7456\u7457\u7458\u7459\u745a\u745b\u745c\u745d\u745e\u745f\u7460\u7461\u7462\u7463\u7464\u7465\u7466\u7467\u7468\u7469\u746a\u746b\u746c\u746d\u746e\u746f\u7470\u7471\u7472\u7473\u7474\u7475\u7476\u7477\u7478\u7479\u747a\u747b\u747c\u747d\u747e\u747f\u7480\u7481\u7482\u7483\u7484\u7485\u7486\u7487\u7488\u7489\u748a\u748b\u748c\u748d\u748e\u748f\u7490\u7491\u7492\u7493\u7494\u7495\u7496\u7497\u7498\u7499\u749a\u749b\u749c\u749d\u749e\u749f\u74a0\u74a1\u74a2\u74a3\u74a4\u74a5\u74a6\u74a7\u74a8\u74a9\u74aa\u74ab\u74ac\u74ad\u74ae\u74af\u74b0\u74b1\u74b2\u74b3\u74b4\u74b5\u74b6\u74b7\u74b8\u74b9\u74ba\u74bb\u74bc\u74bd\u74be\u74bf\u74c0\u74c1\u74c2\u74c3\u74c4\u74c5\u74c6\u74c7\u74c8\u74c9\u74ca\u74cb\u74cc\u74cd\u74ce\u74cf\u74d0\u74d1\u74d2\u74d3\u74d4\u74d5\u74d6\u74d7\u74d8\u74d9\u74da\u74db\u74dc\u74dd\u74de\u74df\u74e0\u74e1\u74e2\u74e3\u74e4\u74e5\u74e6\u74e7\u74e8\u74e9\u74ea\u74eb\u74ec\u74ed\u74ee\u74ef\u74f0\u74f1\u74f2\u74f3\u74f4\u74f5\u74f6\u74f7\u74f8\u74f9\u74fa\u74fb\u74fc\u74fd\u74fe\u74ff\u7500\u7501\u7502\u7503\u7504\u7505\u7506\u7507\u7508\u7509\u750a\u750b\u750c\u750d\u750e\u750f\u7510\u7511\u7512\u7513\u7514\u7515\u7516\u7517\u7518\u7519\u751a\u751b\u751c\u751d\u751e\u751f\u7520\u7521\u7522\u7523\u7524\u7525\u7526\u7527\u7528\u7529\u752a\u752b\u752c\u752d\u752e\u752f\u7530\u7531\u7532\u7533\u7534\u7535\u7536\u7537\u7538\u7539\u753a\u753b\u753c\u753d\u753e\u753f\u7540\u7541\u7542\u7543\u7544\u7545\u7546\u7547\u7548\u7549\u754a\u754b\u754c\u754d\u754e\u754f\u7550\u7551\u7552\u7553\u7554\u7555\u7556\u7557\u7558\u7559\u755a\u755b\u755c\u755d\u755e\u755f\u7560\u7561\u7562\u7563\u7564\u7565\u7566\u7567\u7568\u7569\u756a\u756b\u756c\u756d\u756e\u756f\u7570\u7571\u7572\u7573\u7574\u7575\u7576\u7577\u7578\u7579\u757a\u757b\u757c\u757d\u757e\u757f\u7580\u7581\u7582\u7583\u7584\u7585\u7586\u7587\u7588\u7589\u758a\u758b\u758c\u758d\u758e\u758f\u7590\u7591\u7592\u7593\u7594\u7595\u7596\u7597\u7598\u7599\u759a\u759b\u759c\u759d\u759e\u759f\u75a0\u75a1\u75a2\u75a3\u75a4\u75a5\u75a6\u75a7\u75a8\u75a9\u75aa\u75ab\u75ac\u75ad\u75ae\u75af\u75b0\u75b1\u75b2\u75b3\u75b4\u75b5\u75b6\u75b7\u75b8\u75b9\u75ba\u75bb\u75bc\u75bd\u75be\u75bf\u75c0\u75c1\u75c2\u75c3\u75c4\u75c5\u75c6\u75c7\u75c8\u75c9\u75ca\u75cb\u75cc\u75cd\u75ce\u75cf\u75d0\u75d1\u75d2\u75d3\u75d4\u75d5\u75d6\u75d7\u75d8\u75d9\u75da\u75db\u75dc\u75dd\u75de\u75df\u75e0\u75e1\u75e2\u75e3\u75e4\u75e5\u75e6\u75e7\u75e8\u75e9\u75ea\u75eb\u75ec\u75ed\u75ee\u75ef\u75f0\u75f1\u75f2\u75f3\u75f4\u75f5\u75f6\u75f7\u75f8\u75f9\u75fa\u75fb\u75fc\u75fd\u75fe\u75ff\u7600\u7601\u7602\u7603\u7604\u7605\u7606\u7607\u7608\u7609\u760a\u760b\u760c\u760d\u760e\u760f\u7610\u7611\u7612\u7613\u7614\u7615\u7616\u7617\u7618\u7619\u761a\u761b\u761c\u761d\u761e\u761f\u7620\u7621\u7622\u7623\u7624\u7625\u7626\u7627\u7628\u7629\u762a\u762b\u762c\u762d\u762e\u762f\u7630\u7631\u7632\u7633\u7634\u7635\u7636\u7637\u7638\u7639\u763a\u763b\u763c\u763d\u763e\u763f\u7640\u7641\u7642\u7643\u7644\u7645\u7646\u7647\u7648\u7649\u764a\u764b\u764c\u764d\u764e\u764f\u7650\u7651\u7652\u7653\u7654\u7655\u7656\u7657\u7658\u7659\u765a\u765b\u765c\u765d\u765e\u765f\u7660\u7661\u7662\u7663\u7664\u7665\u7666\u7667\u7668\u7669\u766a\u766b\u766c\u766d\u766e\u766f\u7670\u7671\u7672\u7673\u7674\u7675\u7676\u7677\u7678\u7679\u767a\u767b\u767c\u767d\u767e\u767f\u7680\u7681\u7682\u7683\u7684\u7685\u7686\u7687\u7688\u7689\u768a\u768b\u768c\u768d\u768e\u768f\u7690\u7691\u7692\u7693\u7694\u7695\u7696\u7697\u7698\u7699\u769a\u769b\u769c\u769d\u769e\u769f\u76a0\u76a1\u76a2\u76a3\u76a4\u76a5\u76a6\u76a7\u76a8\u76a9\u76aa\u76ab\u76ac\u76ad\u76ae\u76af\u76b0\u76b1\u76b2\u76b3\u76b4\u76b5\u76b6\u76b7\u76b8\u76b9\u76ba\u76bb\u76bc\u76bd\u76be\u76bf\u76c0\u76c1\u76c2\u76c3\u76c4\u76c5\u76c6\u76c7\u76c8\u76c9\u76ca\u76cb\u76cc\u76cd\u76ce\u76cf\u76d0\u76d1\u76d2\u76d3\u76d4\u76d5\u76d6\u76d7\u76d8\u76d9\u76da\u76db\u76dc\u76dd\u76de\u76df\u76e0\u76e1\u76e2\u76e3\u76e4\u76e5\u76e6\u76e7\u76e8\u76e9\u76ea\u76eb\u76ec\u76ed\u76ee\u76ef\u76f0\u76f1\u76f2\u76f3\u76f4\u76f5\u76f6\u76f7\u76f8\u76f9\u76fa\u76fb\u76fc\u76fd\u76fe\u76ff\u7700\u7701\u7702\u7703\u7704\u7705\u7706\u7707\u7708\u7709\u770a\u770b\u770c\u770d\u770e\u770f\u7710\u7711\u7712\u7713\u7714\u7715\u7716\u7717\u7718\u7719\u771a\u771b\u771c\u771d\u771e\u771f\u7720\u7721\u7722\u7723\u7724\u7725\u7726\u7727\u7728\u7729\u772a\u772b\u772c\u772d\u772e\u772f\u7730\u7731\u7732\u7733\u7734\u7735\u7736\u7737\u7738\u7739\u773a\u773b\u773c\u773d\u773e\u773f\u7740\u7741\u7742\u7743\u7744\u7745\u7746\u7747\u7748\u7749\u774a\u774b\u774c\u774d\u774e\u774f\u7750\u7751\u7752\u7753\u7754\u7755\u7756\u7757\u7758\u7759\u775a\u775b\u775c\u775d\u775e\u775f\u7760\u7761\u7762\u7763\u7764\u7765\u7766\u7767\u7768\u7769\u776a\u776b\u776c\u776d\u776e\u776f\u7770\u7771\u7772\u7773\u7774\u7775\u7776\u7777\u7778\u7779\u777a\u777b\u777c\u777d\u777e\u777f\u7780\u7781\u7782\u7783\u7784\u7785\u7786\u7787\u7788\u7789\u778a\u778b\u778c\u778d\u778e\u778f\u7790\u7791\u7792\u7793\u7794\u7795\u7796\u7797\u7798\u7799\u779a\u779b\u779c\u779d\u779e\u779f\u77a0\u77a1\u77a2\u77a3\u77a4\u77a5\u77a6\u77a7\u77a8\u77a9\u77aa\u77ab\u77ac\u77ad\u77ae\u77af\u77b0\u77b1\u77b2\u77b3\u77b4\u77b5\u77b6\u77b7\u77b8\u77b9\u77ba\u77bb\u77bc\u77bd\u77be\u77bf\u77c0\u77c1\u77c2\u77c3\u77c4\u77c5\u77c6\u77c7\u77c8\u77c9\u77ca\u77cb\u77cc\u77cd\u77ce\u77cf\u77d0\u77d1\u77d2\u77d3\u77d4\u77d5\u77d6\u77d7\u77d8\u77d9\u77da\u77db\u77dc\u77dd\u77de\u77df\u77e0\u77e1\u77e2\u77e3\u77e4\u77e5\u77e6\u77e7\u77e8\u77e9\u77ea\u77eb\u77ec\u77ed\u77ee\u77ef\u77f0\u77f1\u77f2\u77f3\u77f4\u77f5\u77f6\u77f7\u77f8\u77f9\u77fa\u77fb\u77fc\u77fd\u77fe\u77ff\u7800\u7801\u7802\u7803\u7804\u7805\u7806\u7807\u7808\u7809\u780a\u780b\u780c\u780d\u780e\u780f\u7810\u7811\u7812\u7813\u7814\u7815\u7816\u7817\u7818\u7819\u781a\u781b\u781c\u781d\u781e\u781f\u7820\u7821\u7822\u7823\u7824\u7825\u7826\u7827\u7828\u7829\u782a\u782b\u782c\u782d\u782e\u782f\u7830\u7831\u7832\u7833\u7834\u7835\u7836\u7837\u7838\u7839\u783a\u783b\u783c\u783d\u783e\u783f\u7840\u7841\u7842\u7843\u7844\u7845\u7846\u7847\u7848\u7849\u784a\u784b\u784c\u784d\u784e\u784f\u7850\u7851\u7852\u7853\u7854\u7855\u7856\u7857\u7858\u7859\u785a\u785b\u785c\u785d\u785e\u785f\u7860\u7861\u7862\u7863\u7864\u7865\u7866\u7867\u7868\u7869\u786a\u786b\u786c\u786d\u786e\u786f\u7870\u7871\u7872\u7873\u7874\u7875\u7876\u7877\u7878\u7879\u787a\u787b\u787c\u787d\u787e\u787f\u7880\u7881\u7882\u7883\u7884\u7885\u7886\u7887\u7888\u7889\u788a\u788b\u788c\u788d\u788e\u788f\u7890\u7891\u7892\u7893\u7894\u7895\u7896\u7897\u7898\u7899\u789a\u789b\u789c\u789d\u789e\u789f\u78a0\u78a1\u78a2\u78a3\u78a4\u78a5\u78a6\u78a7\u78a8\u78a9\u78aa\u78ab\u78ac\u78ad\u78ae\u78af\u78b0\u78b1\u78b2\u78b3\u78b4\u78b5\u78b6\u78b7\u78b8\u78b9\u78ba\u78bb\u78bc\u78bd\u78be\u78bf\u78c0\u78c1\u78c2\u78c3\u78c4\u78c5\u78c6\u78c7\u78c8\u78c9\u78ca\u78cb\u78cc\u78cd\u78ce\u78cf\u78d0\u78d1\u78d2\u78d3\u78d4\u78d5\u78d6\u78d7\u78d8\u78d9\u78da\u78db\u78dc\u78dd\u78de\u78df\u78e0\u78e1\u78e2\u78e3\u78e4\u78e5\u78e6\u78e7\u78e8\u78e9\u78ea\u78eb\u78ec\u78ed\u78ee\u78ef\u78f0\u78f1\u78f2\u78f3\u78f4\u78f5\u78f6\u78f7\u78f8\u78f9\u78fa\u78fb\u78fc\u78fd\u78fe\u78ff\u7900\u7901\u7902\u7903\u7904\u7905\u7906\u7907\u7908\u7909\u790a\u790b\u790c\u790d\u790e\u790f\u7910\u7911\u7912\u7913\u7914\u7915\u7916\u7917\u7918\u7919\u791a\u791b\u791c\u791d\u791e\u791f\u7920\u7921\u7922\u7923\u7924\u7925\u7926\u7927\u7928\u7929\u792a\u792b\u792c\u792d\u792e\u792f\u7930\u7931\u7932\u7933\u7934\u7935\u7936\u7937\u7938\u7939\u793a\u793b\u793c\u793d\u793e\u793f\u7940\u7941\u7942\u7943\u7944\u7945\u7946\u7947\u7948\u7949\u794a\u794b\u794c\u794d\u794e\u794f\u7950\u7951\u7952\u7953\u7954\u7955\u7956\u7957\u7958\u7959\u795a\u795b\u795c\u795d\u795e\u795f\u7960\u7961\u7962\u7963\u7964\u7965\u7966\u7967\u7968\u7969\u796a\u796b\u796c\u796d\u796e\u796f\u7970\u7971\u7972\u7973\u7974\u7975\u7976\u7977\u7978\u7979\u797a\u797b\u797c\u797d\u797e\u797f\u7980\u7981\u7982\u7983\u7984\u7985\u7986\u7987\u7988\u7989\u798a\u798b\u798c\u798d\u798e\u798f\u7990\u7991\u7992\u7993\u7994\u7995\u7996\u7997\u7998\u7999\u799a\u799b\u799c\u799d\u799e\u799f\u79a0\u79a1\u79a2\u79a3\u79a4\u79a5\u79a6\u79a7\u79a8\u79a9\u79aa\u79ab\u79ac\u79ad\u79ae\u79af\u79b0\u79b1\u79b2\u79b3\u79b4\u79b5\u79b6\u79b7\u79b8\u79b9\u79ba\u79bb\u79bc\u79bd\u79be\u79bf\u79c0\u79c1\u79c2\u79c3\u79c4\u79c5\u79c6\u79c7\u79c8\u79c9\u79ca\u79cb\u79cc\u79cd\u79ce\u79cf\u79d0\u79d1\u79d2\u79d3\u79d4\u79d5\u79d6\u79d7\u79d8\u79d9\u79da\u79db\u79dc\u79dd\u79de\u79df\u79e0\u79e1\u79e2\u79e3\u79e4\u79e5\u79e6\u79e7\u79e8\u79e9\u79ea\u79eb\u79ec\u79ed\u79ee\u79ef\u79f0\u79f1\u79f2\u79f3\u79f4\u79f5\u79f6\u79f7\u79f8\u79f9\u79fa\u79fb\u79fc\u79fd\u79fe\u79ff\u7a00\u7a01\u7a02\u7a03\u7a04\u7a05\u7a06\u7a07\u7a08\u7a09\u7a0a\u7a0b\u7a0c\u7a0d\u7a0e\u7a0f\u7a10\u7a11\u7a12\u7a13\u7a14\u7a15\u7a16\u7a17\u7a18\u7a19\u7a1a\u7a1b\u7a1c\u7a1d\u7a1e\u7a1f\u7a20\u7a21\u7a22\u7a23\u7a24\u7a25\u7a26\u7a27\u7a28\u7a29\u7a2a\u7a2b\u7a2c\u7a2d\u7a2e\u7a2f\u7a30\u7a31\u7a32\u7a33\u7a34\u7a35\u7a36\u7a37\u7a38\u7a39\u7a3a\u7a3b\u7a3c\u7a3d\u7a3e\u7a3f\u7a40\u7a41\u7a42\u7a43\u7a44\u7a45\u7a46\u7a47\u7a48\u7a49\u7a4a\u7a4b\u7a4c\u7a4d\u7a4e\u7a4f\u7a50\u7a51\u7a52\u7a53\u7a54\u7a55\u7a56\u7a57\u7a58\u7a59\u7a5a\u7a5b\u7a5c\u7a5d\u7a5e\u7a5f\u7a60\u7a61\u7a62\u7a63\u7a64\u7a65\u7a66\u7a67\u7a68\u7a69\u7a6a\u7a6b\u7a6c\u7a6d\u7a6e\u7a6f\u7a70\u7a71\u7a72\u7a73\u7a74\u7a75\u7a76\u7a77\u7a78\u7a79\u7a7a\u7a7b\u7a7c\u7a7d\u7a7e\u7a7f\u7a80\u7a81\u7a82\u7a83\u7a84\u7a85\u7a86\u7a87\u7a88\u7a89\u7a8a\u7a8b\u7a8c\u7a8d\u7a8e\u7a8f\u7a90\u7a91\u7a92\u7a93\u7a94\u7a95\u7a96\u7a97\u7a98\u7a99\u7a9a\u7a9b\u7a9c\u7a9d\u7a9e\u7a9f\u7aa0\u7aa1\u7aa2\u7aa3\u7aa4\u7aa5\u7aa6\u7aa7\u7aa8\u7aa9\u7aaa\u7aab\u7aac\u7aad\u7aae\u7aaf\u7ab0\u7ab1\u7ab2\u7ab3\u7ab4\u7ab5\u7ab6\u7ab7\u7ab8\u7ab9\u7aba\u7abb\u7abc\u7abd\u7abe\u7abf\u7ac0\u7ac1\u7ac2\u7ac3\u7ac4\u7ac5\u7ac6\u7ac7\u7ac8\u7ac9\u7aca\u7acb\u7acc\u7acd\u7ace\u7acf\u7ad0\u7ad1\u7ad2\u7ad3\u7ad4\u7ad5\u7ad6\u7ad7\u7ad8\u7ad9\u7ada\u7adb\u7adc\u7add\u7ade\u7adf\u7ae0\u7ae1\u7ae2\u7ae3\u7ae4\u7ae5\u7ae6\u7ae7\u7ae8\u7ae9\u7aea\u7aeb\u7aec\u7aed\u7aee\u7aef\u7af0\u7af1\u7af2\u7af3\u7af4\u7af5\u7af6\u7af7\u7af8\u7af9\u7afa\u7afb\u7afc\u7afd\u7afe\u7aff\u7b00\u7b01\u7b02\u7b03\u7b04\u7b05\u7b06\u7b07\u7b08\u7b09\u7b0a\u7b0b\u7b0c\u7b0d\u7b0e\u7b0f\u7b10\u7b11\u7b12\u7b13\u7b14\u7b15\u7b16\u7b17\u7b18\u7b19\u7b1a\u7b1b\u7b1c\u7b1d\u7b1e\u7b1f\u7b20\u7b21\u7b22\u7b23\u7b24\u7b25\u7b26\u7b27\u7b28\u7b29\u7b2a\u7b2b\u7b2c\u7b2d\u7b2e\u7b2f\u7b30\u7b31\u7b32\u7b33\u7b34\u7b35\u7b36\u7b37\u7b38\u7b39\u7b3a\u7b3b\u7b3c\u7b3d\u7b3e\u7b3f\u7b40\u7b41\u7b42\u7b43\u7b44\u7b45\u7b46\u7b47\u7b48\u7b49\u7b4a\u7b4b\u7b4c\u7b4d\u7b4e\u7b4f\u7b50\u7b51\u7b52\u7b53\u7b54\u7b55\u7b56\u7b57\u7b58\u7b59\u7b5a\u7b5b\u7b5c\u7b5d\u7b5e\u7b5f\u7b60\u7b61\u7b62\u7b63\u7b64\u7b65\u7b66\u7b67\u7b68\u7b69\u7b6a\u7b6b\u7b6c\u7b6d\u7b6e\u7b6f\u7b70\u7b71\u7b72\u7b73\u7b74\u7b75\u7b76\u7b77\u7b78\u7b79\u7b7a\u7b7b\u7b7c\u7b7d\u7b7e\u7b7f\u7b80\u7b81\u7b82\u7b83\u7b84\u7b85\u7b86\u7b87\u7b88\u7b89\u7b8a\u7b8b\u7b8c\u7b8d\u7b8e\u7b8f\u7b90\u7b91\u7b92\u7b93\u7b94\u7b95\u7b96\u7b97\u7b98\u7b99\u7b9a\u7b9b\u7b9c\u7b9d\u7b9e\u7b9f\u7ba0\u7ba1\u7ba2\u7ba3\u7ba4\u7ba5\u7ba6\u7ba7\u7ba8\u7ba9\u7baa\u7bab\u7bac\u7bad\u7bae\u7baf\u7bb0\u7bb1\u7bb2\u7bb3\u7bb4\u7bb5\u7bb6\u7bb7\u7bb8\u7bb9\u7bba\u7bbb\u7bbc\u7bbd\u7bbe\u7bbf\u7bc0\u7bc1\u7bc2\u7bc3\u7bc4\u7bc5\u7bc6\u7bc7\u7bc8\u7bc9\u7bca\u7bcb\u7bcc\u7bcd\u7bce\u7bcf\u7bd0\u7bd1\u7bd2\u7bd3\u7bd4\u7bd5\u7bd6\u7bd7\u7bd8\u7bd9\u7bda\u7bdb\u7bdc\u7bdd\u7bde\u7bdf\u7be0\u7be1\u7be2\u7be3\u7be4\u7be5\u7be6\u7be7\u7be8\u7be9\u7bea\u7beb\u7bec\u7bed\u7bee\u7bef\u7bf0\u7bf1\u7bf2\u7bf3\u7bf4\u7bf5\u7bf6\u7bf7\u7bf8\u7bf9\u7bfa\u7bfb\u7bfc\u7bfd\u7bfe\u7bff\u7c00\u7c01\u7c02\u7c03\u7c04\u7c05\u7c06\u7c07\u7c08\u7c09\u7c0a\u7c0b\u7c0c\u7c0d\u7c0e\u7c0f\u7c10\u7c11\u7c12\u7c13\u7c14\u7c15\u7c16\u7c17\u7c18\u7c19\u7c1a\u7c1b\u7c1c\u7c1d\u7c1e\u7c1f\u7c20\u7c21\u7c22\u7c23\u7c24\u7c25\u7c26\u7c27\u7c28\u7c29\u7c2a\u7c2b\u7c2c\u7c2d\u7c2e\u7c2f\u7c30\u7c31\u7c32\u7c33\u7c34\u7c35\u7c36\u7c37\u7c38\u7c39\u7c3a\u7c3b\u7c3c\u7c3d\u7c3e\u7c3f\u7c40\u7c41\u7c42\u7c43\u7c44\u7c45\u7c46\u7c47\u7c48\u7c49\u7c4a\u7c4b\u7c4c\u7c4d\u7c4e\u7c4f\u7c50\u7c51\u7c52\u7c53\u7c54\u7c55\u7c56\u7c57\u7c58\u7c59\u7c5a\u7c5b\u7c5c\u7c5d\u7c5e\u7c5f\u7c60\u7c61\u7c62\u7c63\u7c64\u7c65\u7c66\u7c67\u7c68\u7c69\u7c6a\u7c6b\u7c6c\u7c6d\u7c6e\u7c6f\u7c70\u7c71\u7c72\u7c73\u7c74\u7c75\u7c76\u7c77\u7c78\u7c79\u7c7a\u7c7b\u7c7c\u7c7d\u7c7e\u7c7f\u7c80\u7c81\u7c82\u7c83\u7c84\u7c85\u7c86\u7c87\u7c88\u7c89\u7c8a\u7c8b\u7c8c\u7c8d\u7c8e\u7c8f\u7c90\u7c91\u7c92\u7c93\u7c94\u7c95\u7c96\u7c97\u7c98\u7c99\u7c9a\u7c9b\u7c9c\u7c9d\u7c9e\u7c9f\u7ca0\u7ca1\u7ca2\u7ca3\u7ca4\u7ca5\u7ca6\u7ca7\u7ca8\u7ca9\u7caa\u7cab\u7cac\u7cad\u7cae\u7caf\u7cb0\u7cb1\u7cb2\u7cb3\u7cb4\u7cb5\u7cb6\u7cb7\u7cb8\u7cb9\u7cba\u7cbb\u7cbc\u7cbd\u7cbe\u7cbf\u7cc0\u7cc1\u7cc2\u7cc3\u7cc4\u7cc5\u7cc6\u7cc7\u7cc8\u7cc9\u7cca\u7ccb\u7ccc\u7ccd\u7cce\u7ccf\u7cd0\u7cd1\u7cd2\u7cd3\u7cd4\u7cd5\u7cd6\u7cd7\u7cd8\u7cd9\u7cda\u7cdb\u7cdc\u7cdd\u7cde\u7cdf\u7ce0\u7ce1\u7ce2\u7ce3\u7ce4\u7ce5\u7ce6\u7ce7\u7ce8\u7ce9\u7cea\u7ceb\u7cec\u7ced\u7cee\u7cef\u7cf0\u7cf1\u7cf2\u7cf3\u7cf4\u7cf5\u7cf6\u7cf7\u7cf8\u7cf9\u7cfa\u7cfb\u7cfc\u7cfd\u7cfe\u7cff\u7d00\u7d01\u7d02\u7d03\u7d04\u7d05\u7d06\u7d07\u7d08\u7d09\u7d0a\u7d0b\u7d0c\u7d0d\u7d0e\u7d0f\u7d10\u7d11\u7d12\u7d13\u7d14\u7d15\u7d16\u7d17\u7d18\u7d19\u7d1a\u7d1b\u7d1c\u7d1d\u7d1e\u7d1f\u7d20\u7d21\u7d22\u7d23\u7d24\u7d25\u7d26\u7d27\u7d28\u7d29\u7d2a\u7d2b\u7d2c\u7d2d\u7d2e\u7d2f\u7d30\u7d31\u7d32\u7d33\u7d34\u7d35\u7d36\u7d37\u7d38\u7d39\u7d3a\u7d3b\u7d3c\u7d3d\u7d3e\u7d3f\u7d40\u7d41\u7d42\u7d43\u7d44\u7d45\u7d46\u7d47\u7d48\u7d49\u7d4a\u7d4b\u7d4c\u7d4d\u7d4e\u7d4f\u7d50\u7d51\u7d52\u7d53\u7d54\u7d55\u7d56\u7d57\u7d58\u7d59\u7d5a\u7d5b\u7d5c\u7d5d\u7d5e\u7d5f\u7d60\u7d61\u7d62\u7d63\u7d64\u7d65\u7d66\u7d67\u7d68\u7d69\u7d6a\u7d6b\u7d6c\u7d6d\u7d6e\u7d6f\u7d70\u7d71\u7d72\u7d73\u7d74\u7d75\u7d76\u7d77\u7d78\u7d79\u7d7a\u7d7b\u7d7c\u7d7d\u7d7e\u7d7f\u7d80\u7d81\u7d82\u7d83\u7d84\u7d85\u7d86\u7d87\u7d88\u7d89\u7d8a\u7d8b\u7d8c\u7d8d\u7d8e\u7d8f\u7d90\u7d91\u7d92\u7d93\u7d94\u7d95\u7d96\u7d97\u7d98\u7d99\u7d9a\u7d9b\u7d9c\u7d9d\u7d9e\u7d9f\u7da0\u7da1\u7da2\u7da3\u7da4\u7da5\u7da6\u7da7\u7da8\u7da9\u7daa\u7dab\u7dac\u7dad\u7dae\u7daf\u7db0\u7db1\u7db2\u7db3\u7db4\u7db5\u7db6\u7db7\u7db8\u7db9\u7dba\u7dbb\u7dbc\u7dbd\u7dbe\u7dbf\u7dc0\u7dc1\u7dc2\u7dc3\u7dc4\u7dc5\u7dc6\u7dc7\u7dc8\u7dc9\u7dca\u7dcb\u7dcc\u7dcd\u7dce\u7dcf\u7dd0\u7dd1\u7dd2\u7dd3\u7dd4\u7dd5\u7dd6\u7dd7\u7dd8\u7dd9\u7dda\u7ddb\u7ddc\u7ddd\u7dde\u7ddf\u7de0\u7de1\u7de2\u7de3\u7de4\u7de5\u7de6\u7de7\u7de8\u7de9\u7dea\u7deb\u7dec\u7ded\u7dee\u7def\u7df0\u7df1\u7df2\u7df3\u7df4\u7df5\u7df6\u7df7\u7df8\u7df9\u7dfa\u7dfb\u7dfc\u7dfd\u7dfe\u7dff\u7e00\u7e01\u7e02\u7e03\u7e04\u7e05\u7e06\u7e07\u7e08\u7e09\u7e0a\u7e0b\u7e0c\u7e0d\u7e0e\u7e0f\u7e10\u7e11\u7e12\u7e13\u7e14\u7e15\u7e16\u7e17\u7e18\u7e19\u7e1a\u7e1b\u7e1c\u7e1d\u7e1e\u7e1f\u7e20\u7e21\u7e22\u7e23\u7e24\u7e25\u7e26\u7e27\u7e28\u7e29\u7e2a\u7e2b\u7e2c\u7e2d\u7e2e\u7e2f\u7e30\u7e31\u7e32\u7e33\u7e34\u7e35\u7e36\u7e37\u7e38\u7e39\u7e3a\u7e3b\u7e3c\u7e3d\u7e3e\u7e3f\u7e40\u7e41\u7e42\u7e43\u7e44\u7e45\u7e46\u7e47\u7e48\u7e49\u7e4a\u7e4b\u7e4c\u7e4d\u7e4e\u7e4f\u7e50\u7e51\u7e52\u7e53\u7e54\u7e55\u7e56\u7e57\u7e58\u7e59\u7e5a\u7e5b\u7e5c\u7e5d\u7e5e\u7e5f\u7e60\u7e61\u7e62\u7e63\u7e64\u7e65\u7e66\u7e67\u7e68\u7e69\u7e6a\u7e6b\u7e6c\u7e6d\u7e6e\u7e6f\u7e70\u7e71\u7e72\u7e73\u7e74\u7e75\u7e76\u7e77\u7e78\u7e79\u7e7a\u7e7b\u7e7c\u7e7d\u7e7e\u7e7f\u7e80\u7e81\u7e82\u7e83\u7e84\u7e85\u7e86\u7e87\u7e88\u7e89\u7e8a\u7e8b\u7e8c\u7e8d\u7e8e\u7e8f\u7e90\u7e91\u7e92\u7e93\u7e94\u7e95\u7e96\u7e97\u7e98\u7e99\u7e9a\u7e9b\u7e9c\u7e9d\u7e9e\u7e9f\u7ea0\u7ea1\u7ea2\u7ea3\u7ea4\u7ea5\u7ea6\u7ea7\u7ea8\u7ea9\u7eaa\u7eab\u7eac\u7ead\u7eae\u7eaf\u7eb0\u7eb1\u7eb2\u7eb3\u7eb4\u7eb5\u7eb6\u7eb7\u7eb8\u7eb9\u7eba\u7ebb\u7ebc\u7ebd\u7ebe\u7ebf\u7ec0\u7ec1\u7ec2\u7ec3\u7ec4\u7ec5\u7ec6\u7ec7\u7ec8\u7ec9\u7eca\u7ecb\u7ecc\u7ecd\u7ece\u7ecf\u7ed0\u7ed1\u7ed2\u7ed3\u7ed4\u7ed5\u7ed6\u7ed7\u7ed8\u7ed9\u7eda\u7edb\u7edc\u7edd\u7ede\u7edf\u7ee0\u7ee1\u7ee2\u7ee3\u7ee4\u7ee5\u7ee6\u7ee7\u7ee8\u7ee9\u7eea\u7eeb\u7eec\u7eed\u7eee\u7eef\u7ef0\u7ef1\u7ef2\u7ef3\u7ef4\u7ef5\u7ef6\u7ef7\u7ef8\u7ef9\u7efa\u7efb\u7efc\u7efd\u7efe\u7eff\u7f00\u7f01\u7f02\u7f03\u7f04\u7f05\u7f06\u7f07\u7f08\u7f09\u7f0a\u7f0b\u7f0c\u7f0d\u7f0e\u7f0f\u7f10\u7f11\u7f12\u7f13\u7f14\u7f15\u7f16\u7f17\u7f18\u7f19\u7f1a\u7f1b\u7f1c\u7f1d\u7f1e\u7f1f\u7f20\u7f21\u7f22\u7f23\u7f24\u7f25\u7f26\u7f27\u7f28\u7f29\u7f2a\u7f2b\u7f2c\u7f2d\u7f2e\u7f2f\u7f30\u7f31\u7f32\u7f33\u7f34\u7f35\u7f36\u7f37\u7f38\u7f39\u7f3a\u7f3b\u7f3c\u7f3d\u7f3e\u7f3f\u7f40\u7f41\u7f42\u7f43\u7f44\u7f45\u7f46\u7f47\u7f48\u7f49\u7f4a\u7f4b\u7f4c\u7f4d\u7f4e\u7f4f\u7f50\u7f51\u7f52\u7f53\u7f54\u7f55\u7f56\u7f57\u7f58\u7f59\u7f5a\u7f5b\u7f5c\u7f5d\u7f5e\u7f5f\u7f60\u7f61\u7f62\u7f63\u7f64\u7f65\u7f66\u7f67\u7f68\u7f69\u7f6a\u7f6b\u7f6c\u7f6d\u7f6e\u7f6f\u7f70\u7f71\u7f72\u7f73\u7f74\u7f75\u7f76\u7f77\u7f78\u7f79\u7f7a\u7f7b\u7f7c\u7f7d\u7f7e\u7f7f\u7f80\u7f81\u7f82\u7f83\u7f84\u7f85\u7f86\u7f87\u7f88\u7f89\u7f8a\u7f8b\u7f8c\u7f8d\u7f8e\u7f8f\u7f90\u7f91\u7f92\u7f93\u7f94\u7f95\u7f96\u7f97\u7f98\u7f99\u7f9a\u7f9b\u7f9c\u7f9d\u7f9e\u7f9f\u7fa0\u7fa1\u7fa2\u7fa3\u7fa4\u7fa5\u7fa6\u7fa7\u7fa8\u7fa9\u7faa\u7fab\u7fac\u7fad\u7fae\u7faf\u7fb0\u7fb1\u7fb2\u7fb3\u7fb4\u7fb5\u7fb6\u7fb7\u7fb8\u7fb9\u7fba\u7fbb\u7fbc\u7fbd\u7fbe\u7fbf\u7fc0\u7fc1\u7fc2\u7fc3\u7fc4\u7fc5\u7fc6\u7fc7\u7fc8\u7fc9\u7fca\u7fcb\u7fcc\u7fcd\u7fce\u7fcf\u7fd0\u7fd1\u7fd2\u7fd3\u7fd4\u7fd5\u7fd6\u7fd7\u7fd8\u7fd9\u7fda\u7fdb\u7fdc\u7fdd\u7fde\u7fdf\u7fe0\u7fe1\u7fe2\u7fe3\u7fe4\u7fe5\u7fe6\u7fe7\u7fe8\u7fe9\u7fea\u7feb\u7fec\u7fed\u7fee\u7fef\u7ff0\u7ff1\u7ff2\u7ff3\u7ff4\u7ff5\u7ff6\u7ff7\u7ff8\u7ff9\u7ffa\u7ffb\u7ffc\u7ffd\u7ffe\u7fff\u8000\u8001\u8002\u8003\u8004\u8005\u8006\u8007\u8008\u8009\u800a\u800b\u800c\u800d\u800e\u800f\u8010\u8011\u8012\u8013\u8014\u8015\u8016\u8017\u8018\u8019\u801a\u801b\u801c\u801d\u801e\u801f\u8020\u8021\u8022\u8023\u8024\u8025\u8026\u8027\u8028\u8029\u802a\u802b\u802c\u802d\u802e\u802f\u8030\u8031\u8032\u8033\u8034\u8035\u8036\u8037\u8038\u8039\u803a\u803b\u803c\u803d\u803e\u803f\u8040\u8041\u8042\u8043\u8044\u8045\u8046\u8047\u8048\u8049\u804a\u804b\u804c\u804d\u804e\u804f\u8050\u8051\u8052\u8053\u8054\u8055\u8056\u8057\u8058\u8059\u805a\u805b\u805c\u805d\u805e\u805f\u8060\u8061\u8062\u8063\u8064\u8065\u8066\u8067\u8068\u8069\u806a\u806b\u806c\u806d\u806e\u806f\u8070\u8071\u8072\u8073\u8074\u8075\u8076\u8077\u8078\u8079\u807a\u807b\u807c\u807d\u807e\u807f\u8080\u8081\u8082\u8083\u8084\u8085\u8086\u8087\u8088\u8089\u808a\u808b\u808c\u808d\u808e\u808f\u8090\u8091\u8092\u8093\u8094\u8095\u8096\u8097\u8098\u8099\u809a\u809b\u809c\u809d\u809e\u809f\u80a0\u80a1\u80a2\u80a3\u80a4\u80a5\u80a6\u80a7\u80a8\u80a9\u80aa\u80ab\u80ac\u80ad\u80ae\u80af\u80b0\u80b1\u80b2\u80b3\u80b4\u80b5\u80b6\u80b7\u80b8\u80b9\u80ba\u80bb\u80bc\u80bd\u80be\u80bf\u80c0\u80c1\u80c2\u80c3\u80c4\u80c5\u80c6\u80c7\u80c8\u80c9\u80ca\u80cb\u80cc\u80cd\u80ce\u80cf\u80d0\u80d1\u80d2\u80d3\u80d4\u80d5\u80d6\u80d7\u80d8\u80d9\u80da\u80db\u80dc\u80dd\u80de\u80df\u80e0\u80e1\u80e2\u80e3\u80e4\u80e5\u80e6\u80e7\u80e8\u80e9\u80ea\u80eb\u80ec\u80ed\u80ee\u80ef\u80f0\u80f1\u80f2\u80f3\u80f4\u80f5\u80f6\u80f7\u80f8\u80f9\u80fa\u80fb\u80fc\u80fd\u80fe\u80ff\u8100\u8101\u8102\u8103\u8104\u8105\u8106\u8107\u8108\u8109\u810a\u810b\u810c\u810d\u810e\u810f\u8110\u8111\u8112\u8113\u8114\u8115\u8116\u8117\u8118\u8119\u811a\u811b\u811c\u811d\u811e\u811f\u8120\u8121\u8122\u8123\u8124\u8125\u8126\u8127\u8128\u8129\u812a\u812b\u812c\u812d\u812e\u812f\u8130\u8131\u8132\u8133\u8134\u8135\u8136\u8137\u8138\u8139\u813a\u813b\u813c\u813d\u813e\u813f\u8140\u8141\u8142\u8143\u8144\u8145\u8146\u8147\u8148\u8149\u814a\u814b\u814c\u814d\u814e\u814f\u8150\u8151\u8152\u8153\u8154\u8155\u8156\u8157\u8158\u8159\u815a\u815b\u815c\u815d\u815e\u815f\u8160\u8161\u8162\u8163\u8164\u8165\u8166\u8167\u8168\u8169\u816a\u816b\u816c\u816d\u816e\u816f\u8170\u8171\u8172\u8173\u8174\u8175\u8176\u8177\u8178\u8179\u817a\u817b\u817c\u817d\u817e\u817f\u8180\u8181\u8182\u8183\u8184\u8185\u8186\u8187\u8188\u8189\u818a\u818b\u818c\u818d\u818e\u818f\u8190\u8191\u8192\u8193\u8194\u8195\u8196\u8197\u8198\u8199\u819a\u819b\u819c\u819d\u819e\u819f\u81a0\u81a1\u81a2\u81a3\u81a4\u81a5\u81a6\u81a7\u81a8\u81a9\u81aa\u81ab\u81ac\u81ad\u81ae\u81af\u81b0\u81b1\u81b2\u81b3\u81b4\u81b5\u81b6\u81b7\u81b8\u81b9\u81ba\u81bb\u81bc\u81bd\u81be\u81bf\u81c0\u81c1\u81c2\u81c3\u81c4\u81c5\u81c6\u81c7\u81c8\u81c9\u81ca\u81cb\u81cc\u81cd\u81ce\u81cf\u81d0\u81d1\u81d2\u81d3\u81d4\u81d5\u81d6\u81d7\u81d8\u81d9\u81da\u81db\u81dc\u81dd\u81de\u81df\u81e0\u81e1\u81e2\u81e3\u81e4\u81e5\u81e6\u81e7\u81e8\u81e9\u81ea\u81eb\u81ec\u81ed\u81ee\u81ef\u81f0\u81f1\u81f2\u81f3\u81f4\u81f5\u81f6\u81f7\u81f8\u81f9\u81fa\u81fb\u81fc\u81fd\u81fe\u81ff\u8200\u8201\u8202\u8203\u8204\u8205\u8206\u8207\u8208\u8209\u820a\u820b\u820c\u820d\u820e\u820f\u8210\u8211\u8212\u8213\u8214\u8215\u8216\u8217\u8218\u8219\u821a\u821b\u821c\u821d\u821e\u821f\u8220\u8221\u8222\u8223\u8224\u8225\u8226\u8227\u8228\u8229\u822a\u822b\u822c\u822d\u822e\u822f\u8230\u8231\u8232\u8233\u8234\u8235\u8236\u8237\u8238\u8239\u823a\u823b\u823c\u823d\u823e\u823f\u8240\u8241\u8242\u8243\u8244\u8245\u8246\u8247\u8248\u8249\u824a\u824b\u824c\u824d\u824e\u824f\u8250\u8251\u8252\u8253\u8254\u8255\u8256\u8257\u8258\u8259\u825a\u825b\u825c\u825d\u825e\u825f\u8260\u8261\u8262\u8263\u8264\u8265\u8266\u8267\u8268\u8269\u826a\u826b\u826c\u826d\u826e\u826f\u8270\u8271\u8272\u8273\u8274\u8275\u8276\u8277\u8278\u8279\u827a\u827b\u827c\u827d\u827e\u827f\u8280\u8281\u8282\u8283\u8284\u8285\u8286\u8287\u8288\u8289\u828a\u828b\u828c\u828d\u828e\u828f\u8290\u8291\u8292\u8293\u8294\u8295\u8296\u8297\u8298\u8299\u829a\u829b\u829c\u829d\u829e\u829f\u82a0\u82a1\u82a2\u82a3\u82a4\u82a5\u82a6\u82a7\u82a8\u82a9\u82aa\u82ab\u82ac\u82ad\u82ae\u82af\u82b0\u82b1\u82b2\u82b3\u82b4\u82b5\u82b6\u82b7\u82b8\u82b9\u82ba\u82bb\u82bc\u82bd\u82be\u82bf\u82c0\u82c1\u82c2\u82c3\u82c4\u82c5\u82c6\u82c7\u82c8\u82c9\u82ca\u82cb\u82cc\u82cd\u82ce\u82cf\u82d0\u82d1\u82d2\u82d3\u82d4\u82d5\u82d6\u82d7\u82d8\u82d9\u82da\u82db\u82dc\u82dd\u82de\u82df\u82e0\u82e1\u82e2\u82e3\u82e4\u82e5\u82e6\u82e7\u82e8\u82e9\u82ea\u82eb\u82ec\u82ed\u82ee\u82ef\u82f0\u82f1\u82f2\u82f3\u82f4\u82f5\u82f6\u82f7\u82f8\u82f9\u82fa\u82fb\u82fc\u82fd\u82fe\u82ff\u8300\u8301\u8302\u8303\u8304\u8305\u8306\u8307\u8308\u8309\u830a\u830b\u830c\u830d\u830e\u830f\u8310\u8311\u8312\u8313\u8314\u8315\u8316\u8317\u8318\u8319\u831a\u831b\u831c\u831d\u831e\u831f\u8320\u8321\u8322\u8323\u8324\u8325\u8326\u8327\u8328\u8329\u832a\u832b\u832c\u832d\u832e\u832f\u8330\u8331\u8332\u8333\u8334\u8335\u8336\u8337\u8338\u8339\u833a\u833b\u833c\u833d\u833e\u833f\u8340\u8341\u8342\u8343\u8344\u8345\u8346\u8347\u8348\u8349\u834a\u834b\u834c\u834d\u834e\u834f\u8350\u8351\u8352\u8353\u8354\u8355\u8356\u8357\u8358\u8359\u835a\u835b\u835c\u835d\u835e\u835f\u8360\u8361\u8362\u8363\u8364\u8365\u8366\u8367\u8368\u8369\u836a\u836b\u836c\u836d\u836e\u836f\u8370\u8371\u8372\u8373\u8374\u8375\u8376\u8377\u8378\u8379\u837a\u837b\u837c\u837d\u837e\u837f\u8380\u8381\u8382\u8383\u8384\u8385\u8386\u8387\u8388\u8389\u838a\u838b\u838c\u838d\u838e\u838f\u8390\u8391\u8392\u8393\u8394\u8395\u8396\u8397\u8398\u8399\u839a\u839b\u839c\u839d\u839e\u839f\u83a0\u83a1\u83a2\u83a3\u83a4\u83a5\u83a6\u83a7\u83a8\u83a9\u83aa\u83ab\u83ac\u83ad\u83ae\u83af\u83b0\u83b1\u83b2\u83b3\u83b4\u83b5\u83b6\u83b7\u83b8\u83b9\u83ba\u83bb\u83bc\u83bd\u83be\u83bf\u83c0\u83c1\u83c2\u83c3\u83c4\u83c5\u83c6\u83c7\u83c8\u83c9\u83ca\u83cb\u83cc\u83cd\u83ce\u83cf\u83d0\u83d1\u83d2\u83d3\u83d4\u83d5\u83d6\u83d7\u83d8\u83d9\u83da\u83db\u83dc\u83dd\u83de\u83df\u83e0\u83e1\u83e2\u83e3\u83e4\u83e5\u83e6\u83e7\u83e8\u83e9\u83ea\u83eb\u83ec\u83ed\u83ee\u83ef\u83f0\u83f1\u83f2\u83f3\u83f4\u83f5\u83f6\u83f7\u83f8\u83f9\u83fa\u83fb\u83fc\u83fd\u83fe\u83ff\u8400\u8401\u8402\u8403\u8404\u8405\u8406\u8407\u8408\u8409\u840a\u840b\u840c\u840d\u840e\u840f\u8410\u8411\u8412\u8413\u8414\u8415\u8416\u8417\u8418\u8419\u841a\u841b\u841c\u841d\u841e\u841f\u8420\u8421\u8422\u8423\u8424\u8425\u8426\u8427\u8428\u8429\u842a\u842b\u842c\u842d\u842e\u842f\u8430\u8431\u8432\u8433\u8434\u8435\u8436\u8437\u8438\u8439\u843a\u843b\u843c\u843d\u843e\u843f\u8440\u8441\u8442\u8443\u8444\u8445\u8446\u8447\u8448\u8449\u844a\u844b\u844c\u844d\u844e\u844f\u8450\u8451\u8452\u8453\u8454\u8455\u8456\u8457\u8458\u8459\u845a\u845b\u845c\u845d\u845e\u845f\u8460\u8461\u8462\u8463\u8464\u8465\u8466\u8467\u8468\u8469\u846a\u846b\u846c\u846d\u846e\u846f\u8470\u8471\u8472\u8473\u8474\u8475\u8476\u8477\u8478\u8479\u847a\u847b\u847c\u847d\u847e\u847f\u8480\u8481\u8482\u8483\u8484\u8485\u8486\u8487\u8488\u8489\u848a\u848b\u848c\u848d\u848e\u848f\u8490\u8491\u8492\u8493\u8494\u8495\u8496\u8497\u8498\u8499\u849a\u849b\u849c\u849d\u849e\u849f\u84a0\u84a1\u84a2\u84a3\u84a4\u84a5\u84a6\u84a7\u84a8\u84a9\u84aa\u84ab\u84ac\u84ad\u84ae\u84af\u84b0\u84b1\u84b2\u84b3\u84b4\u84b5\u84b6\u84b7\u84b8\u84b9\u84ba\u84bb\u84bc\u84bd\u84be\u84bf\u84c0\u84c1\u84c2\u84c3\u84c4\u84c5\u84c6\u84c7\u84c8\u84c9\u84ca\u84cb\u84cc\u84cd\u84ce\u84cf\u84d0\u84d1\u84d2\u84d3\u84d4\u84d5\u84d6\u84d7\u84d8\u84d9\u84da\u84db\u84dc\u84dd\u84de\u84df\u84e0\u84e1\u84e2\u84e3\u84e4\u84e5\u84e6\u84e7\u84e8\u84e9\u84ea\u84eb\u84ec\u84ed\u84ee\u84ef\u84f0\u84f1\u84f2\u84f3\u84f4\u84f5\u84f6\u84f7\u84f8\u84f9\u84fa\u84fb\u84fc\u84fd\u84fe\u84ff\u8500\u8501\u8502\u8503\u8504\u8505\u8506\u8507\u8508\u8509\u850a\u850b\u850c\u850d\u850e\u850f\u8510\u8511\u8512\u8513\u8514\u8515\u8516\u8517\u8518\u8519\u851a\u851b\u851c\u851d\u851e\u851f\u8520\u8521\u8522\u8523\u8524\u8525\u8526\u8527\u8528\u8529\u852a\u852b\u852c\u852d\u852e\u852f\u8530\u8531\u8532\u8533\u8534\u8535\u8536\u8537\u8538\u8539\u853a\u853b\u853c\u853d\u853e\u853f\u8540\u8541\u8542\u8543\u8544\u8545\u8546\u8547\u8548\u8549\u854a\u854b\u854c\u854d\u854e\u854f\u8550\u8551\u8552\u8553\u8554\u8555\u8556\u8557\u8558\u8559\u855a\u855b\u855c\u855d\u855e\u855f\u8560\u8561\u8562\u8563\u8564\u8565\u8566\u8567\u8568\u8569\u856a\u856b\u856c\u856d\u856e\u856f\u8570\u8571\u8572\u8573\u8574\u8575\u8576\u8577\u8578\u8579\u857a\u857b\u857c\u857d\u857e\u857f\u8580\u8581\u8582\u8583\u8584\u8585\u8586\u8587\u8588\u8589\u858a\u858b\u858c\u858d\u858e\u858f\u8590\u8591\u8592\u8593\u8594\u8595\u8596\u8597\u8598\u8599\u859a\u859b\u859c\u859d\u859e\u859f\u85a0\u85a1\u85a2\u85a3\u85a4\u85a5\u85a6\u85a7\u85a8\u85a9\u85aa\u85ab\u85ac\u85ad\u85ae\u85af\u85b0\u85b1\u85b2\u85b3\u85b4\u85b5\u85b6\u85b7\u85b8\u85b9\u85ba\u85bb\u85bc\u85bd\u85be\u85bf\u85c0\u85c1\u85c2\u85c3\u85c4\u85c5\u85c6\u85c7\u85c8\u85c9\u85ca\u85cb\u85cc\u85cd\u85ce\u85cf\u85d0\u85d1\u85d2\u85d3\u85d4\u85d5\u85d6\u85d7\u85d8\u85d9\u85da\u85db\u85dc\u85dd\u85de\u85df\u85e0\u85e1\u85e2\u85e3\u85e4\u85e5\u85e6\u85e7\u85e8\u85e9\u85ea\u85eb\u85ec\u85ed\u85ee\u85ef\u85f0\u85f1\u85f2\u85f3\u85f4\u85f5\u85f6\u85f7\u85f8\u85f9\u85fa\u85fb\u85fc\u85fd\u85fe\u85ff\u8600\u8601\u8602\u8603\u8604\u8605\u8606\u8607\u8608\u8609\u860a\u860b\u860c\u860d\u860e\u860f\u8610\u8611\u8612\u8613\u8614\u8615\u8616\u8617\u8618\u8619\u861a\u861b\u861c\u861d\u861e\u861f\u8620\u8621\u8622\u8623\u8624\u8625\u8626\u8627\u8628\u8629\u862a\u862b\u862c\u862d\u862e\u862f\u8630\u8631\u8632\u8633\u8634\u8635\u8636\u8637\u8638\u8639\u863a\u863b\u863c\u863d\u863e\u863f\u8640\u8641\u8642\u8643\u8644\u8645\u8646\u8647\u8648\u8649\u864a\u864b\u864c\u864d\u864e\u864f\u8650\u8651\u8652\u8653\u8654\u8655\u8656\u8657\u8658\u8659\u865a\u865b\u865c\u865d\u865e\u865f\u8660\u8661\u8662\u8663\u8664\u8665\u8666\u8667\u8668\u8669\u866a\u866b\u866c\u866d\u866e\u866f\u8670\u8671\u8672\u8673\u8674\u8675\u8676\u8677\u8678\u8679\u867a\u867b\u867c\u867d\u867e\u867f\u8680\u8681\u8682\u8683\u8684\u8685\u8686\u8687\u8688\u8689\u868a\u868b\u868c\u868d\u868e\u868f\u8690\u8691\u8692\u8693\u8694\u8695\u8696\u8697\u8698\u8699\u869a\u869b\u869c\u869d\u869e\u869f\u86a0\u86a1\u86a2\u86a3\u86a4\u86a5\u86a6\u86a7\u86a8\u86a9\u86aa\u86ab\u86ac\u86ad\u86ae\u86af\u86b0\u86b1\u86b2\u86b3\u86b4\u86b5\u86b6\u86b7\u86b8\u86b9\u86ba\u86bb\u86bc\u86bd\u86be\u86bf\u86c0\u86c1\u86c2\u86c3\u86c4\u86c5\u86c6\u86c7\u86c8\u86c9\u86ca\u86cb\u86cc\u86cd\u86ce\u86cf\u86d0\u86d1\u86d2\u86d3\u86d4\u86d5\u86d6\u86d7\u86d8\u86d9\u86da\u86db\u86dc\u86dd\u86de\u86df\u86e0\u86e1\u86e2\u86e3\u86e4\u86e5\u86e6\u86e7\u86e8\u86e9\u86ea\u86eb\u86ec\u86ed\u86ee\u86ef\u86f0\u86f1\u86f2\u86f3\u86f4\u86f5\u86f6\u86f7\u86f8\u86f9\u86fa\u86fb\u86fc\u86fd\u86fe\u86ff\u8700\u8701\u8702\u8703\u8704\u8705\u8706\u8707\u8708\u8709\u870a\u870b\u870c\u870d\u870e\u870f\u8710\u8711\u8712\u8713\u8714\u8715\u8716\u8717\u8718\u8719\u871a\u871b\u871c\u871d\u871e\u871f\u8720\u8721\u8722\u8723\u8724\u8725\u8726\u8727\u8728\u8729\u872a\u872b\u872c\u872d\u872e\u872f\u8730\u8731\u8732\u8733\u8734\u8735\u8736\u8737\u8738\u8739\u873a\u873b\u873c\u873d\u873e\u873f\u8740\u8741\u8742\u8743\u8744\u8745\u8746\u8747\u8748\u8749\u874a\u874b\u874c\u874d\u874e\u874f\u8750\u8751\u8752\u8753\u8754\u8755\u8756\u8757\u8758\u8759\u875a\u875b\u875c\u875d\u875e\u875f\u8760\u8761\u8762\u8763\u8764\u8765\u8766\u8767\u8768\u8769\u876a\u876b\u876c\u876d\u876e\u876f\u8770\u8771\u8772\u8773\u8774\u8775\u8776\u8777\u8778\u8779\u877a\u877b\u877c\u877d\u877e\u877f\u8780\u8781\u8782\u8783\u8784\u8785\u8786\u8787\u8788\u8789\u878a\u878b\u878c\u878d\u878e\u878f\u8790\u8791\u8792\u8793\u8794\u8795\u8796\u8797\u8798\u8799\u879a\u879b\u879c\u879d\u879e\u879f\u87a0\u87a1\u87a2\u87a3\u87a4\u87a5\u87a6\u87a7\u87a8\u87a9\u87aa\u87ab\u87ac\u87ad\u87ae\u87af\u87b0\u87b1\u87b2\u87b3\u87b4\u87b5\u87b6\u87b7\u87b8\u87b9\u87ba\u87bb\u87bc\u87bd\u87be\u87bf\u87c0\u87c1\u87c2\u87c3\u87c4\u87c5\u87c6\u87c7\u87c8\u87c9\u87ca\u87cb\u87cc\u87cd\u87ce\u87cf\u87d0\u87d1\u87d2\u87d3\u87d4\u87d5\u87d6\u87d7\u87d8\u87d9\u87da\u87db\u87dc\u87dd\u87de\u87df\u87e0\u87e1\u87e2\u87e3\u87e4\u87e5\u87e6\u87e7\u87e8\u87e9\u87ea\u87eb\u87ec\u87ed\u87ee\u87ef\u87f0\u87f1\u87f2\u87f3\u87f4\u87f5\u87f6\u87f7\u87f8\u87f9\u87fa\u87fb\u87fc\u87fd\u87fe\u87ff\u8800\u8801\u8802\u8803\u8804\u8805\u8806\u8807\u8808\u8809\u880a\u880b\u880c\u880d\u880e\u880f\u8810\u8811\u8812\u8813\u8814\u8815\u8816\u8817\u8818\u8819\u881a\u881b\u881c\u881d\u881e\u881f\u8820\u8821\u8822\u8823\u8824\u8825\u8826\u8827\u8828\u8829\u882a\u882b\u882c\u882d\u882e\u882f\u8830\u8831\u8832\u8833\u8834\u8835\u8836\u8837\u8838\u8839\u883a\u883b\u883c\u883d\u883e\u883f\u8840\u8841\u8842\u8843\u8844\u8845\u8846\u8847\u8848\u8849\u884a\u884b\u884c\u884d\u884e\u884f\u8850\u8851\u8852\u8853\u8854\u8855\u8856\u8857\u8858\u8859\u885a\u885b\u885c\u885d\u885e\u885f\u8860\u8861\u8862\u8863\u8864\u8865\u8866\u8867\u8868\u8869\u886a\u886b\u886c\u886d\u886e\u886f\u8870\u8871\u8872\u8873\u8874\u8875\u8876\u8877\u8878\u8879\u887a\u887b\u887c\u887d\u887e\u887f\u8880\u8881\u8882\u8883\u8884\u8885\u8886\u8887\u8888\u8889\u888a\u888b\u888c\u888d\u888e\u888f\u8890\u8891\u8892\u8893\u8894\u8895\u8896\u8897\u8898\u8899\u889a\u889b\u889c\u889d\u889e\u889f\u88a0\u88a1\u88a2\u88a3\u88a4\u88a5\u88a6\u88a7\u88a8\u88a9\u88aa\u88ab\u88ac\u88ad\u88ae\u88af\u88b0\u88b1\u88b2\u88b3\u88b4\u88b5\u88b6\u88b7\u88b8\u88b9\u88ba\u88bb\u88bc\u88bd\u88be\u88bf\u88c0\u88c1\u88c2\u88c3\u88c4\u88c5\u88c6\u88c7\u88c8\u88c9\u88ca\u88cb\u88cc\u88cd\u88ce\u88cf\u88d0\u88d1\u88d2\u88d3\u88d4\u88d5\u88d6\u88d7\u88d8\u88d9\u88da\u88db\u88dc\u88dd\u88de\u88df\u88e0\u88e1\u88e2\u88e3\u88e4\u88e5\u88e6\u88e7\u88e8\u88e9\u88ea\u88eb\u88ec\u88ed\u88ee\u88ef\u88f0\u88f1\u88f2\u88f3\u88f4\u88f5\u88f6\u88f7\u88f8\u88f9\u88fa\u88fb\u88fc\u88fd\u88fe\u88ff\u8900\u8901\u8902\u8903\u8904\u8905\u8906\u8907\u8908\u8909\u890a\u890b\u890c\u890d\u890e\u890f\u8910\u8911\u8912\u8913\u8914\u8915\u8916\u8917\u8918\u8919\u891a\u891b\u891c\u891d\u891e\u891f\u8920\u8921\u8922\u8923\u8924\u8925\u8926\u8927\u8928\u8929\u892a\u892b\u892c\u892d\u892e\u892f\u8930\u8931\u8932\u8933\u8934\u8935\u8936\u8937\u8938\u8939\u893a\u893b\u893c\u893d\u893e\u893f\u8940\u8941\u8942\u8943\u8944\u8945\u8946\u8947\u8948\u8949\u894a\u894b\u894c\u894d\u894e\u894f\u8950\u8951\u8952\u8953\u8954\u8955\u8956\u8957\u8958\u8959\u895a\u895b\u895c\u895d\u895e\u895f\u8960\u8961\u8962\u8963\u8964\u8965\u8966\u8967\u8968\u8969\u896a\u896b\u896c\u896d\u896e\u896f\u8970\u8971\u8972\u8973\u8974\u8975\u8976\u8977\u8978\u8979\u897a\u897b\u897c\u897d\u897e\u897f\u8980\u8981\u8982\u8983\u8984\u8985\u8986\u8987\u8988\u8989\u898a\u898b\u898c\u898d\u898e\u898f\u8990\u8991\u8992\u8993\u8994\u8995\u8996\u8997\u8998\u8999\u899a\u899b\u899c\u899d\u899e\u899f\u89a0\u89a1\u89a2\u89a3\u89a4\u89a5\u89a6\u89a7\u89a8\u89a9\u89aa\u89ab\u89ac\u89ad\u89ae\u89af\u89b0\u89b1\u89b2\u89b3\u89b4\u89b5\u89b6\u89b7\u89b8\u89b9\u89ba\u89bb\u89bc\u89bd\u89be\u89bf\u89c0\u89c1\u89c2\u89c3\u89c4\u89c5\u89c6\u89c7\u89c8\u89c9\u89ca\u89cb\u89cc\u89cd\u89ce\u89cf\u89d0\u89d1\u89d2\u89d3\u89d4\u89d5\u89d6\u89d7\u89d8\u89d9\u89da\u89db\u89dc\u89dd\u89de\u89df\u89e0\u89e1\u89e2\u89e3\u89e4\u89e5\u89e6\u89e7\u89e8\u89e9\u89ea\u89eb\u89ec\u89ed\u89ee\u89ef\u89f0\u89f1\u89f2\u89f3\u89f4\u89f5\u89f6\u89f7\u89f8\u89f9\u89fa\u89fb\u89fc\u89fd\u89fe\u89ff\u8a00\u8a01\u8a02\u8a03\u8a04\u8a05\u8a06\u8a07\u8a08\u8a09\u8a0a\u8a0b\u8a0c\u8a0d\u8a0e\u8a0f\u8a10\u8a11\u8a12\u8a13\u8a14\u8a15\u8a16\u8a17\u8a18\u8a19\u8a1a\u8a1b\u8a1c\u8a1d\u8a1e\u8a1f\u8a20\u8a21\u8a22\u8a23\u8a24\u8a25\u8a26\u8a27\u8a28\u8a29\u8a2a\u8a2b\u8a2c\u8a2d\u8a2e\u8a2f\u8a30\u8a31\u8a32\u8a33\u8a34\u8a35\u8a36\u8a37\u8a38\u8a39\u8a3a\u8a3b\u8a3c\u8a3d\u8a3e\u8a3f\u8a40\u8a41\u8a42\u8a43\u8a44\u8a45\u8a46\u8a47\u8a48\u8a49\u8a4a\u8a4b\u8a4c\u8a4d\u8a4e\u8a4f\u8a50\u8a51\u8a52\u8a53\u8a54\u8a55\u8a56\u8a57\u8a58\u8a59\u8a5a\u8a5b\u8a5c\u8a5d\u8a5e\u8a5f\u8a60\u8a61\u8a62\u8a63\u8a64\u8a65\u8a66\u8a67\u8a68\u8a69\u8a6a\u8a6b\u8a6c\u8a6d\u8a6e\u8a6f\u8a70\u8a71\u8a72\u8a73\u8a74\u8a75\u8a76\u8a77\u8a78\u8a79\u8a7a\u8a7b\u8a7c\u8a7d\u8a7e\u8a7f\u8a80\u8a81\u8a82\u8a83\u8a84\u8a85\u8a86\u8a87\u8a88\u8a89\u8a8a\u8a8b\u8a8c\u8a8d\u8a8e\u8a8f\u8a90\u8a91\u8a92\u8a93\u8a94\u8a95\u8a96\u8a97\u8a98\u8a99\u8a9a\u8a9b\u8a9c\u8a9d\u8a9e\u8a9f\u8aa0\u8aa1\u8aa2\u8aa3\u8aa4\u8aa5\u8aa6\u8aa7\u8aa8\u8aa9\u8aaa\u8aab\u8aac\u8aad\u8aae\u8aaf\u8ab0\u8ab1\u8ab2\u8ab3\u8ab4\u8ab5\u8ab6\u8ab7\u8ab8\u8ab9\u8aba\u8abb\u8abc\u8abd\u8abe\u8abf\u8ac0\u8ac1\u8ac2\u8ac3\u8ac4\u8ac5\u8ac6\u8ac7\u8ac8\u8ac9\u8aca\u8acb\u8acc\u8acd\u8ace\u8acf\u8ad0\u8ad1\u8ad2\u8ad3\u8ad4\u8ad5\u8ad6\u8ad7\u8ad8\u8ad9\u8ada\u8adb\u8adc\u8add\u8ade\u8adf\u8ae0\u8ae1\u8ae2\u8ae3\u8ae4\u8ae5\u8ae6\u8ae7\u8ae8\u8ae9\u8aea\u8aeb\u8aec\u8aed\u8aee\u8aef\u8af0\u8af1\u8af2\u8af3\u8af4\u8af5\u8af6\u8af7\u8af8\u8af9\u8afa\u8afb\u8afc\u8afd\u8afe\u8aff\u8b00\u8b01\u8b02\u8b03\u8b04\u8b05\u8b06\u8b07\u8b08\u8b09\u8b0a\u8b0b\u8b0c\u8b0d\u8b0e\u8b0f\u8b10\u8b11\u8b12\u8b13\u8b14\u8b15\u8b16\u8b17\u8b18\u8b19\u8b1a\u8b1b\u8b1c\u8b1d\u8b1e\u8b1f\u8b20\u8b21\u8b22\u8b23\u8b24\u8b25\u8b26\u8b27\u8b28\u8b29\u8b2a\u8b2b\u8b2c\u8b2d\u8b2e\u8b2f\u8b30\u8b31\u8b32\u8b33\u8b34\u8b35\u8b36\u8b37\u8b38\u8b39\u8b3a\u8b3b\u8b3c\u8b3d\u8b3e\u8b3f\u8b40\u8b41\u8b42\u8b43\u8b44\u8b45\u8b46\u8b47\u8b48\u8b49\u8b4a\u8b4b\u8b4c\u8b4d\u8b4e\u8b4f\u8b50\u8b51\u8b52\u8b53\u8b54\u8b55\u8b56\u8b57\u8b58\u8b59\u8b5a\u8b5b\u8b5c\u8b5d\u8b5e\u8b5f\u8b60\u8b61\u8b62\u8b63\u8b64\u8b65\u8b66\u8b67\u8b68\u8b69\u8b6a\u8b6b\u8b6c\u8b6d\u8b6e\u8b6f\u8b70\u8b71\u8b72\u8b73\u8b74\u8b75\u8b76\u8b77\u8b78\u8b79\u8b7a\u8b7b\u8b7c\u8b7d\u8b7e\u8b7f\u8b80\u8b81\u8b82\u8b83\u8b84\u8b85\u8b86\u8b87\u8b88\u8b89\u8b8a\u8b8b\u8b8c\u8b8d\u8b8e\u8b8f\u8b90\u8b91\u8b92\u8b93\u8b94\u8b95\u8b96\u8b97\u8b98\u8b99\u8b9a\u8b9b\u8b9c\u8b9d\u8b9e\u8b9f\u8ba0\u8ba1\u8ba2\u8ba3\u8ba4\u8ba5\u8ba6\u8ba7\u8ba8\u8ba9\u8baa\u8bab\u8bac\u8bad\u8bae\u8baf\u8bb0\u8bb1\u8bb2\u8bb3\u8bb4\u8bb5\u8bb6\u8bb7\u8bb8\u8bb9\u8bba\u8bbb\u8bbc\u8bbd\u8bbe\u8bbf\u8bc0\u8bc1\u8bc2\u8bc3\u8bc4\u8bc5\u8bc6\u8bc7\u8bc8\u8bc9\u8bca\u8bcb\u8bcc\u8bcd\u8bce\u8bcf\u8bd0\u8bd1\u8bd2\u8bd3\u8bd4\u8bd5\u8bd6\u8bd7\u8bd8\u8bd9\u8bda\u8bdb\u8bdc\u8bdd\u8bde\u8bdf\u8be0\u8be1\u8be2\u8be3\u8be4\u8be5\u8be6\u8be7\u8be8\u8be9\u8bea\u8beb\u8bec\u8bed\u8bee\u8bef\u8bf0\u8bf1\u8bf2\u8bf3\u8bf4\u8bf5\u8bf6\u8bf7\u8bf8\u8bf9\u8bfa\u8bfb\u8bfc\u8bfd\u8bfe\u8bff\u8c00\u8c01\u8c02\u8c03\u8c04\u8c05\u8c06\u8c07\u8c08\u8c09\u8c0a\u8c0b\u8c0c\u8c0d\u8c0e\u8c0f\u8c10\u8c11\u8c12\u8c13\u8c14\u8c15\u8c16\u8c17\u8c18\u8c19\u8c1a\u8c1b\u8c1c\u8c1d\u8c1e\u8c1f\u8c20\u8c21\u8c22\u8c23\u8c24\u8c25\u8c26\u8c27\u8c28\u8c29\u8c2a\u8c2b\u8c2c\u8c2d\u8c2e\u8c2f\u8c30\u8c31\u8c32\u8c33\u8c34\u8c35\u8c36\u8c37\u8c38\u8c39\u8c3a\u8c3b\u8c3c\u8c3d\u8c3e\u8c3f\u8c40\u8c41\u8c42\u8c43\u8c44\u8c45\u8c46\u8c47\u8c48\u8c49\u8c4a\u8c4b\u8c4c\u8c4d\u8c4e\u8c4f\u8c50\u8c51\u8c52\u8c53\u8c54\u8c55\u8c56\u8c57\u8c58\u8c59\u8c5a\u8c5b\u8c5c\u8c5d\u8c5e\u8c5f\u8c60\u8c61\u8c62\u8c63\u8c64\u8c65\u8c66\u8c67\u8c68\u8c69\u8c6a\u8c6b\u8c6c\u8c6d\u8c6e\u8c6f\u8c70\u8c71\u8c72\u8c73\u8c74\u8c75\u8c76\u8c77\u8c78\u8c79\u8c7a\u8c7b\u8c7c\u8c7d\u8c7e\u8c7f\u8c80\u8c81\u8c82\u8c83\u8c84\u8c85\u8c86\u8c87\u8c88\u8c89\u8c8a\u8c8b\u8c8c\u8c8d\u8c8e\u8c8f\u8c90\u8c91\u8c92\u8c93\u8c94\u8c95\u8c96\u8c97\u8c98\u8c99\u8c9a\u8c9b\u8c9c\u8c9d\u8c9e\u8c9f\u8ca0\u8ca1\u8ca2\u8ca3\u8ca4\u8ca5\u8ca6\u8ca7\u8ca8\u8ca9\u8caa\u8cab\u8cac\u8cad\u8cae\u8caf\u8cb0\u8cb1\u8cb2\u8cb3\u8cb4\u8cb5\u8cb6\u8cb7\u8cb8\u8cb9\u8cba\u8cbb\u8cbc\u8cbd\u8cbe\u8cbf\u8cc0\u8cc1\u8cc2\u8cc3\u8cc4\u8cc5\u8cc6\u8cc7\u8cc8\u8cc9\u8cca\u8ccb\u8ccc\u8ccd\u8cce\u8ccf\u8cd0\u8cd1\u8cd2\u8cd3\u8cd4\u8cd5\u8cd6\u8cd7\u8cd8\u8cd9\u8cda\u8cdb\u8cdc\u8cdd\u8cde\u8cdf\u8ce0\u8ce1\u8ce2\u8ce3\u8ce4\u8ce5\u8ce6\u8ce7\u8ce8\u8ce9\u8cea\u8ceb\u8cec\u8ced\u8cee\u8cef\u8cf0\u8cf1\u8cf2\u8cf3\u8cf4\u8cf5\u8cf6\u8cf7\u8cf8\u8cf9\u8cfa\u8cfb\u8cfc\u8cfd\u8cfe\u8cff\u8d00\u8d01\u8d02\u8d03\u8d04\u8d05\u8d06\u8d07\u8d08\u8d09\u8d0a\u8d0b\u8d0c\u8d0d\u8d0e\u8d0f\u8d10\u8d11\u8d12\u8d13\u8d14\u8d15\u8d16\u8d17\u8d18\u8d19\u8d1a\u8d1b\u8d1c\u8d1d\u8d1e\u8d1f\u8d20\u8d21\u8d22\u8d23\u8d24\u8d25\u8d26\u8d27\u8d28\u8d29\u8d2a\u8d2b\u8d2c\u8d2d\u8d2e\u8d2f\u8d30\u8d31\u8d32\u8d33\u8d34\u8d35\u8d36\u8d37\u8d38\u8d39\u8d3a\u8d3b\u8d3c\u8d3d\u8d3e\u8d3f\u8d40\u8d41\u8d42\u8d43\u8d44\u8d45\u8d46\u8d47\u8d48\u8d49\u8d4a\u8d4b\u8d4c\u8d4d\u8d4e\u8d4f\u8d50\u8d51\u8d52\u8d53\u8d54\u8d55\u8d56\u8d57\u8d58\u8d59\u8d5a\u8d5b\u8d5c\u8d5d\u8d5e\u8d5f\u8d60\u8d61\u8d62\u8d63\u8d64\u8d65\u8d66\u8d67\u8d68\u8d69\u8d6a\u8d6b\u8d6c\u8d6d\u8d6e\u8d6f\u8d70\u8d71\u8d72\u8d73\u8d74\u8d75\u8d76\u8d77\u8d78\u8d79\u8d7a\u8d7b\u8d7c\u8d7d\u8d7e\u8d7f\u8d80\u8d81\u8d82\u8d83\u8d84\u8d85\u8d86\u8d87\u8d88\u8d89\u8d8a\u8d8b\u8d8c\u8d8d\u8d8e\u8d8f\u8d90\u8d91\u8d92\u8d93\u8d94\u8d95\u8d96\u8d97\u8d98\u8d99\u8d9a\u8d9b\u8d9c\u8d9d\u8d9e\u8d9f\u8da0\u8da1\u8da2\u8da3\u8da4\u8da5\u8da6\u8da7\u8da8\u8da9\u8daa\u8dab\u8dac\u8dad\u8dae\u8daf\u8db0\u8db1\u8db2\u8db3\u8db4\u8db5\u8db6\u8db7\u8db8\u8db9\u8dba\u8dbb\u8dbc\u8dbd\u8dbe\u8dbf\u8dc0\u8dc1\u8dc2\u8dc3\u8dc4\u8dc5\u8dc6\u8dc7\u8dc8\u8dc9\u8dca\u8dcb\u8dcc\u8dcd\u8dce\u8dcf\u8dd0\u8dd1\u8dd2\u8dd3\u8dd4\u8dd5\u8dd6\u8dd7\u8dd8\u8dd9\u8dda\u8ddb\u8ddc\u8ddd\u8dde\u8ddf\u8de0\u8de1\u8de2\u8de3\u8de4\u8de5\u8de6\u8de7\u8de8\u8de9\u8dea\u8deb\u8dec\u8ded\u8dee\u8def\u8df0\u8df1\u8df2\u8df3\u8df4\u8df5\u8df6\u8df7\u8df8\u8df9\u8dfa\u8dfb\u8dfc\u8dfd\u8dfe\u8dff\u8e00\u8e01\u8e02\u8e03\u8e04\u8e05\u8e06\u8e07\u8e08\u8e09\u8e0a\u8e0b\u8e0c\u8e0d\u8e0e\u8e0f\u8e10\u8e11\u8e12\u8e13\u8e14\u8e15\u8e16\u8e17\u8e18\u8e19\u8e1a\u8e1b\u8e1c\u8e1d\u8e1e\u8e1f\u8e20\u8e21\u8e22\u8e23\u8e24\u8e25\u8e26\u8e27\u8e28\u8e29\u8e2a\u8e2b\u8e2c\u8e2d\u8e2e\u8e2f\u8e30\u8e31\u8e32\u8e33\u8e34\u8e35\u8e36\u8e37\u8e38\u8e39\u8e3a\u8e3b\u8e3c\u8e3d\u8e3e\u8e3f\u8e40\u8e41\u8e42\u8e43\u8e44\u8e45\u8e46\u8e47\u8e48\u8e49\u8e4a\u8e4b\u8e4c\u8e4d\u8e4e\u8e4f\u8e50\u8e51\u8e52\u8e53\u8e54\u8e55\u8e56\u8e57\u8e58\u8e59\u8e5a\u8e5b\u8e5c\u8e5d\u8e5e\u8e5f\u8e60\u8e61\u8e62\u8e63\u8e64\u8e65\u8e66\u8e67\u8e68\u8e69\u8e6a\u8e6b\u8e6c\u8e6d\u8e6e\u8e6f\u8e70\u8e71\u8e72\u8e73\u8e74\u8e75\u8e76\u8e77\u8e78\u8e79\u8e7a\u8e7b\u8e7c\u8e7d\u8e7e\u8e7f\u8e80\u8e81\u8e82\u8e83\u8e84\u8e85\u8e86\u8e87\u8e88\u8e89\u8e8a\u8e8b\u8e8c\u8e8d\u8e8e\u8e8f\u8e90\u8e91\u8e92\u8e93\u8e94\u8e95\u8e96\u8e97\u8e98\u8e99\u8e9a\u8e9b\u8e9c\u8e9d\u8e9e\u8e9f\u8ea0\u8ea1\u8ea2\u8ea3\u8ea4\u8ea5\u8ea6\u8ea7\u8ea8\u8ea9\u8eaa\u8eab\u8eac\u8ead\u8eae\u8eaf\u8eb0\u8eb1\u8eb2\u8eb3\u8eb4\u8eb5\u8eb6\u8eb7\u8eb8\u8eb9\u8eba\u8ebb\u8ebc\u8ebd\u8ebe\u8ebf\u8ec0\u8ec1\u8ec2\u8ec3\u8ec4\u8ec5\u8ec6\u8ec7\u8ec8\u8ec9\u8eca\u8ecb\u8ecc\u8ecd\u8ece\u8ecf\u8ed0\u8ed1\u8ed2\u8ed3\u8ed4\u8ed5\u8ed6\u8ed7\u8ed8\u8ed9\u8eda\u8edb\u8edc\u8edd\u8ede\u8edf\u8ee0\u8ee1\u8ee2\u8ee3\u8ee4\u8ee5\u8ee6\u8ee7\u8ee8\u8ee9\u8eea\u8eeb\u8eec\u8eed\u8eee\u8eef\u8ef0\u8ef1\u8ef2\u8ef3\u8ef4\u8ef5\u8ef6\u8ef7\u8ef8\u8ef9\u8efa\u8efb\u8efc\u8efd\u8efe\u8eff\u8f00\u8f01\u8f02\u8f03\u8f04\u8f05\u8f06\u8f07\u8f08\u8f09\u8f0a\u8f0b\u8f0c\u8f0d\u8f0e\u8f0f\u8f10\u8f11\u8f12\u8f13\u8f14\u8f15\u8f16\u8f17\u8f18\u8f19\u8f1a\u8f1b\u8f1c\u8f1d\u8f1e\u8f1f\u8f20\u8f21\u8f22\u8f23\u8f24\u8f25\u8f26\u8f27\u8f28\u8f29\u8f2a\u8f2b\u8f2c\u8f2d\u8f2e\u8f2f\u8f30\u8f31\u8f32\u8f33\u8f34\u8f35\u8f36\u8f37\u8f38\u8f39\u8f3a\u8f3b\u8f3c\u8f3d\u8f3e\u8f3f\u8f40\u8f41\u8f42\u8f43\u8f44\u8f45\u8f46\u8f47\u8f48\u8f49\u8f4a\u8f4b\u8f4c\u8f4d\u8f4e\u8f4f\u8f50\u8f51\u8f52\u8f53\u8f54\u8f55\u8f56\u8f57\u8f58\u8f59\u8f5a\u8f5b\u8f5c\u8f5d\u8f5e\u8f5f\u8f60\u8f61\u8f62\u8f63\u8f64\u8f65\u8f66\u8f67\u8f68\u8f69\u8f6a\u8f6b\u8f6c\u8f6d\u8f6e\u8f6f\u8f70\u8f71\u8f72\u8f73\u8f74\u8f75\u8f76\u8f77\u8f78\u8f79\u8f7a\u8f7b\u8f7c\u8f7d\u8f7e\u8f7f\u8f80\u8f81\u8f82\u8f83\u8f84\u8f85\u8f86\u8f87\u8f88\u8f89\u8f8a\u8f8b\u8f8c\u8f8d\u8f8e\u8f8f\u8f90\u8f91\u8f92\u8f93\u8f94\u8f95\u8f96\u8f97\u8f98\u8f99\u8f9a\u8f9b\u8f9c\u8f9d\u8f9e\u8f9f\u8fa0\u8fa1\u8fa2\u8fa3\u8fa4\u8fa5\u8fa6\u8fa7\u8fa8\u8fa9\u8faa\u8fab\u8fac\u8fad\u8fae\u8faf\u8fb0\u8fb1\u8fb2\u8fb3\u8fb4\u8fb5\u8fb6\u8fb7\u8fb8\u8fb9\u8fba\u8fbb\u8fbc\u8fbd\u8fbe\u8fbf\u8fc0\u8fc1\u8fc2\u8fc3\u8fc4\u8fc5\u8fc6\u8fc7\u8fc8\u8fc9\u8fca\u8fcb\u8fcc\u8fcd\u8fce\u8fcf\u8fd0\u8fd1\u8fd2\u8fd3\u8fd4\u8fd5\u8fd6\u8fd7\u8fd8\u8fd9\u8fda\u8fdb\u8fdc\u8fdd\u8fde\u8fdf\u8fe0\u8fe1\u8fe2\u8fe3\u8fe4\u8fe5\u8fe6\u8fe7\u8fe8\u8fe9\u8fea\u8feb\u8fec\u8fed\u8fee\u8fef\u8ff0\u8ff1\u8ff2\u8ff3\u8ff4\u8ff5\u8ff6\u8ff7\u8ff8\u8ff9\u8ffa\u8ffb\u8ffc\u8ffd\u8ffe\u8fff\u9000\u9001\u9002\u9003\u9004\u9005\u9006\u9007\u9008\u9009\u900a\u900b\u900c\u900d\u900e\u900f\u9010\u9011\u9012\u9013\u9014\u9015\u9016\u9017\u9018\u9019\u901a\u901b\u901c\u901d\u901e\u901f\u9020\u9021\u9022\u9023\u9024\u9025\u9026\u9027\u9028\u9029\u902a\u902b\u902c\u902d\u902e\u902f\u9030\u9031\u9032\u9033\u9034\u9035\u9036\u9037\u9038\u9039\u903a\u903b\u903c\u903d\u903e\u903f\u9040\u9041\u9042\u9043\u9044\u9045\u9046\u9047\u9048\u9049\u904a\u904b\u904c\u904d\u904e\u904f\u9050\u9051\u9052\u9053\u9054\u9055\u9056\u9057\u9058\u9059\u905a\u905b\u905c\u905d\u905e\u905f\u9060\u9061\u9062\u9063\u9064\u9065\u9066\u9067\u9068\u9069\u906a\u906b\u906c\u906d\u906e\u906f\u9070\u9071\u9072\u9073\u9074\u9075\u9076\u9077\u9078\u9079\u907a\u907b\u907c\u907d\u907e\u907f\u9080\u9081\u9082\u9083\u9084\u9085\u9086\u9087\u9088\u9089\u908a\u908b\u908c\u908d\u908e\u908f\u9090\u9091\u9092\u9093\u9094\u9095\u9096\u9097\u9098\u9099\u909a\u909b\u909c\u909d\u909e\u909f\u90a0\u90a1\u90a2\u90a3\u90a4\u90a5\u90a6\u90a7\u90a8\u90a9\u90aa\u90ab\u90ac\u90ad\u90ae\u90af\u90b0\u90b1\u90b2\u90b3\u90b4\u90b5\u90b6\u90b7\u90b8\u90b9\u90ba\u90bb\u90bc\u90bd\u90be\u90bf\u90c0\u90c1\u90c2\u90c3\u90c4\u90c5\u90c6\u90c7\u90c8\u90c9\u90ca\u90cb\u90cc\u90cd\u90ce\u90cf\u90d0\u90d1\u90d2\u90d3\u90d4\u90d5\u90d6\u90d7\u90d8\u90d9\u90da\u90db\u90dc\u90dd\u90de\u90df\u90e0\u90e1\u90e2\u90e3\u90e4\u90e5\u90e6\u90e7\u90e8\u90e9\u90ea\u90eb\u90ec\u90ed\u90ee\u90ef\u90f0\u90f1\u90f2\u90f3\u90f4\u90f5\u90f6\u90f7\u90f8\u90f9\u90fa\u90fb\u90fc\u90fd\u90fe\u90ff\u9100\u9101\u9102\u9103\u9104\u9105\u9106\u9107\u9108\u9109\u910a\u910b\u910c\u910d\u910e\u910f\u9110\u9111\u9112\u9113\u9114\u9115\u9116\u9117\u9118\u9119\u911a\u911b\u911c\u911d\u911e\u911f\u9120\u9121\u9122\u9123\u9124\u9125\u9126\u9127\u9128\u9129\u912a\u912b\u912c\u912d\u912e\u912f\u9130\u9131\u9132\u9133\u9134\u9135\u9136\u9137\u9138\u9139\u913a\u913b\u913c\u913d\u913e\u913f\u9140\u9141\u9142\u9143\u9144\u9145\u9146\u9147\u9148\u9149\u914a\u914b\u914c\u914d\u914e\u914f\u9150\u9151\u9152\u9153\u9154\u9155\u9156\u9157\u9158\u9159\u915a\u915b\u915c\u915d\u915e\u915f\u9160\u9161\u9162\u9163\u9164\u9165\u9166\u9167\u9168\u9169\u916a\u916b\u916c\u916d\u916e\u916f\u9170\u9171\u9172\u9173\u9174\u9175\u9176\u9177\u9178\u9179\u917a\u917b\u917c\u917d\u917e\u917f\u9180\u9181\u9182\u9183\u9184\u9185\u9186\u9187\u9188\u9189\u918a\u918b\u918c\u918d\u918e\u918f\u9190\u9191\u9192\u9193\u9194\u9195\u9196\u9197\u9198\u9199\u919a\u919b\u919c\u919d\u919e\u919f\u91a0\u91a1\u91a2\u91a3\u91a4\u91a5\u91a6\u91a7\u91a8\u91a9\u91aa\u91ab\u91ac\u91ad\u91ae\u91af\u91b0\u91b1\u91b2\u91b3\u91b4\u91b5\u91b6\u91b7\u91b8\u91b9\u91ba\u91bb\u91bc\u91bd\u91be\u91bf\u91c0\u91c1\u91c2\u91c3\u91c4\u91c5\u91c6\u91c7\u91c8\u91c9\u91ca\u91cb\u91cc\u91cd\u91ce\u91cf\u91d0\u91d1\u91d2\u91d3\u91d4\u91d5\u91d6\u91d7\u91d8\u91d9\u91da\u91db\u91dc\u91dd\u91de\u91df\u91e0\u91e1\u91e2\u91e3\u91e4\u91e5\u91e6\u91e7\u91e8\u91e9\u91ea\u91eb\u91ec\u91ed\u91ee\u91ef\u91f0\u91f1\u91f2\u91f3\u91f4\u91f5\u91f6\u91f7\u91f8\u91f9\u91fa\u91fb\u91fc\u91fd\u91fe\u91ff\u9200\u9201\u9202\u9203\u9204\u9205\u9206\u9207\u9208\u9209\u920a\u920b\u920c\u920d\u920e\u920f\u9210\u9211\u9212\u9213\u9214\u9215\u9216\u9217\u9218\u9219\u921a\u921b\u921c\u921d\u921e\u921f\u9220\u9221\u9222\u9223\u9224\u9225\u9226\u9227\u9228\u9229\u922a\u922b\u922c\u922d\u922e\u922f\u9230\u9231\u9232\u9233\u9234\u9235\u9236\u9237\u9238\u9239\u923a\u923b\u923c\u923d\u923e\u923f\u9240\u9241\u9242\u9243\u9244\u9245\u9246\u9247\u9248\u9249\u924a\u924b\u924c\u924d\u924e\u924f\u9250\u9251\u9252\u9253\u9254\u9255\u9256\u9257\u9258\u9259\u925a\u925b\u925c\u925d\u925e\u925f\u9260\u9261\u9262\u9263\u9264\u9265\u9266\u9267\u9268\u9269\u926a\u926b\u926c\u926d\u926e\u926f\u9270\u9271\u9272\u9273\u9274\u9275\u9276\u9277\u9278\u9279\u927a\u927b\u927c\u927d\u927e\u927f\u9280\u9281\u9282\u9283\u9284\u9285\u9286\u9287\u9288\u9289\u928a\u928b\u928c\u928d\u928e\u928f\u9290\u9291\u9292\u9293\u9294\u9295\u9296\u9297\u9298\u9299\u929a\u929b\u929c\u929d\u929e\u929f\u92a0\u92a1\u92a2\u92a3\u92a4\u92a5\u92a6\u92a7\u92a8\u92a9\u92aa\u92ab\u92ac\u92ad\u92ae\u92af\u92b0\u92b1\u92b2\u92b3\u92b4\u92b5\u92b6\u92b7\u92b8\u92b9\u92ba\u92bb\u92bc\u92bd\u92be\u92bf\u92c0\u92c1\u92c2\u92c3\u92c4\u92c5\u92c6\u92c7\u92c8\u92c9\u92ca\u92cb\u92cc\u92cd\u92ce\u92cf\u92d0\u92d1\u92d2\u92d3\u92d4\u92d5\u92d6\u92d7\u92d8\u92d9\u92da\u92db\u92dc\u92dd\u92de\u92df\u92e0\u92e1\u92e2\u92e3\u92e4\u92e5\u92e6\u92e7\u92e8\u92e9\u92ea\u92eb\u92ec\u92ed\u92ee\u92ef\u92f0\u92f1\u92f2\u92f3\u92f4\u92f5\u92f6\u92f7\u92f8\u92f9\u92fa\u92fb\u92fc\u92fd\u92fe\u92ff\u9300\u9301\u9302\u9303\u9304\u9305\u9306\u9307\u9308\u9309\u930a\u930b\u930c\u930d\u930e\u930f\u9310\u9311\u9312\u9313\u9314\u9315\u9316\u9317\u9318\u9319\u931a\u931b\u931c\u931d\u931e\u931f\u9320\u9321\u9322\u9323\u9324\u9325\u9326\u9327\u9328\u9329\u932a\u932b\u932c\u932d\u932e\u932f\u9330\u9331\u9332\u9333\u9334\u9335\u9336\u9337\u9338\u9339\u933a\u933b\u933c\u933d\u933e\u933f\u9340\u9341\u9342\u9343\u9344\u9345\u9346\u9347\u9348\u9349\u934a\u934b\u934c\u934d\u934e\u934f\u9350\u9351\u9352\u9353\u9354\u9355\u9356\u9357\u9358\u9359\u935a\u935b\u935c\u935d\u935e\u935f\u9360\u9361\u9362\u9363\u9364\u9365\u9366\u9367\u9368\u9369\u936a\u936b\u936c\u936d\u936e\u936f\u9370\u9371\u9372\u9373\u9374\u9375\u9376\u9377\u9378\u9379\u937a\u937b\u937c\u937d\u937e\u937f\u9380\u9381\u9382\u9383\u9384\u9385\u9386\u9387\u9388\u9389\u938a\u938b\u938c\u938d\u938e\u938f\u9390\u9391\u9392\u9393\u9394\u9395\u9396\u9397\u9398\u9399\u939a\u939b\u939c\u939d\u939e\u939f\u93a0\u93a1\u93a2\u93a3\u93a4\u93a5\u93a6\u93a7\u93a8\u93a9\u93aa\u93ab\u93ac\u93ad\u93ae\u93af\u93b0\u93b1\u93b2\u93b3\u93b4\u93b5\u93b6\u93b7\u93b8\u93b9\u93ba\u93bb\u93bc\u93bd\u93be\u93bf\u93c0\u93c1\u93c2\u93c3\u93c4\u93c5\u93c6\u93c7\u93c8\u93c9\u93ca\u93cb\u93cc\u93cd\u93ce\u93cf\u93d0\u93d1\u93d2\u93d3\u93d4\u93d5\u93d6\u93d7\u93d8\u93d9\u93da\u93db\u93dc\u93dd\u93de\u93df\u93e0\u93e1\u93e2\u93e3\u93e4\u93e5\u93e6\u93e7\u93e8\u93e9\u93ea\u93eb\u93ec\u93ed\u93ee\u93ef\u93f0\u93f1\u93f2\u93f3\u93f4\u93f5\u93f6\u93f7\u93f8\u93f9\u93fa\u93fb\u93fc\u93fd\u93fe\u93ff\u9400\u9401\u9402\u9403\u9404\u9405\u9406\u9407\u9408\u9409\u940a\u940b\u940c\u940d\u940e\u940f\u9410\u9411\u9412\u9413\u9414\u9415\u9416\u9417\u9418\u9419\u941a\u941b\u941c\u941d\u941e\u941f\u9420\u9421\u9422\u9423\u9424\u9425\u9426\u9427\u9428\u9429\u942a\u942b\u942c\u942d\u942e\u942f\u9430\u9431\u9432\u9433\u9434\u9435\u9436\u9437\u9438\u9439\u943a\u943b\u943c\u943d\u943e\u943f\u9440\u9441\u9442\u9443\u9444\u9445\u9446\u9447\u9448\u9449\u944a\u944b\u944c\u944d\u944e\u944f\u9450\u9451\u9452\u9453\u9454\u9455\u9456\u9457\u9458\u9459\u945a\u945b\u945c\u945d\u945e\u945f\u9460\u9461\u9462\u9463\u9464\u9465\u9466\u9467\u9468\u9469\u946a\u946b\u946c\u946d\u946e\u946f\u9470\u9471\u9472\u9473\u9474\u9475\u9476\u9477\u9478\u9479\u947a\u947b\u947c\u947d\u947e\u947f\u9480\u9481\u9482\u9483\u9484\u9485\u9486\u9487\u9488\u9489\u948a\u948b\u948c\u948d\u948e\u948f\u9490\u9491\u9492\u9493\u9494\u9495\u9496\u9497\u9498\u9499\u949a\u949b\u949c\u949d\u949e\u949f\u94a0\u94a1\u94a2\u94a3\u94a4\u94a5\u94a6\u94a7\u94a8\u94a9\u94aa\u94ab\u94ac\u94ad\u94ae\u94af\u94b0\u94b1\u94b2\u94b3\u94b4\u94b5\u94b6\u94b7\u94b8\u94b9\u94ba\u94bb\u94bc\u94bd\u94be\u94bf\u94c0\u94c1\u94c2\u94c3\u94c4\u94c5\u94c6\u94c7\u94c8\u94c9\u94ca\u94cb\u94cc\u94cd\u94ce\u94cf\u94d0\u94d1\u94d2\u94d3\u94d4\u94d5\u94d6\u94d7\u94d8\u94d9\u94da\u94db\u94dc\u94dd\u94de\u94df\u94e0\u94e1\u94e2\u94e3\u94e4\u94e5\u94e6\u94e7\u94e8\u94e9\u94ea\u94eb\u94ec\u94ed\u94ee\u94ef\u94f0\u94f1\u94f2\u94f3\u94f4\u94f5\u94f6\u94f7\u94f8\u94f9\u94fa\u94fb\u94fc\u94fd\u94fe\u94ff\u9500\u9501\u9502\u9503\u9504\u9505\u9506\u9507\u9508\u9509\u950a\u950b\u950c\u950d\u950e\u950f\u9510\u9511\u9512\u9513\u9514\u9515\u9516\u9517\u9518\u9519\u951a\u951b\u951c\u951d\u951e\u951f\u9520\u9521\u9522\u9523\u9524\u9525\u9526\u9527\u9528\u9529\u952a\u952b\u952c\u952d\u952e\u952f\u9530\u9531\u9532\u9533\u9534\u9535\u9536\u9537\u9538\u9539\u953a\u953b\u953c\u953d\u953e\u953f\u9540\u9541\u9542\u9543\u9544\u9545\u9546\u9547\u9548\u9549\u954a\u954b\u954c\u954d\u954e\u954f\u9550\u9551\u9552\u9553\u9554\u9555\u9556\u9557\u9558\u9559\u955a\u955b\u955c\u955d\u955e\u955f\u9560\u9561\u9562\u9563\u9564\u9565\u9566\u9567\u9568\u9569\u956a\u956b\u956c\u956d\u956e\u956f\u9570\u9571\u9572\u9573\u9574\u9575\u9576\u9577\u9578\u9579\u957a\u957b\u957c\u957d\u957e\u957f\u9580\u9581\u9582\u9583\u9584\u9585\u9586\u9587\u9588\u9589\u958a\u958b\u958c\u958d\u958e\u958f\u9590\u9591\u9592\u9593\u9594\u9595\u9596\u9597\u9598\u9599\u959a\u959b\u959c\u959d\u959e\u959f\u95a0\u95a1\u95a2\u95a3\u95a4\u95a5\u95a6\u95a7\u95a8\u95a9\u95aa\u95ab\u95ac\u95ad\u95ae\u95af\u95b0\u95b1\u95b2\u95b3\u95b4\u95b5\u95b6\u95b7\u95b8\u95b9\u95ba\u95bb\u95bc\u95bd\u95be\u95bf\u95c0\u95c1\u95c2\u95c3\u95c4\u95c5\u95c6\u95c7\u95c8\u95c9\u95ca\u95cb\u95cc\u95cd\u95ce\u95cf\u95d0\u95d1\u95d2\u95d3\u95d4\u95d5\u95d6\u95d7\u95d8\u95d9\u95da\u95db\u95dc\u95dd\u95de\u95df\u95e0\u95e1\u95e2\u95e3\u95e4\u95e5\u95e6\u95e7\u95e8\u95e9\u95ea\u95eb\u95ec\u95ed\u95ee\u95ef\u95f0\u95f1\u95f2\u95f3\u95f4\u95f5\u95f6\u95f7\u95f8\u95f9\u95fa\u95fb\u95fc\u95fd\u95fe\u95ff\u9600\u9601\u9602\u9603\u9604\u9605\u9606\u9607\u9608\u9609\u960a\u960b\u960c\u960d\u960e\u960f\u9610\u9611\u9612\u9613\u9614\u9615\u9616\u9617\u9618\u9619\u961a\u961b\u961c\u961d\u961e\u961f\u9620\u9621\u9622\u9623\u9624\u9625\u9626\u9627\u9628\u9629\u962a\u962b\u962c\u962d\u962e\u962f\u9630\u9631\u9632\u9633\u9634\u9635\u9636\u9637\u9638\u9639\u963a\u963b\u963c\u963d\u963e\u963f\u9640\u9641\u9642\u9643\u9644\u9645\u9646\u9647\u9648\u9649\u964a\u964b\u964c\u964d\u964e\u964f\u9650\u9651\u9652\u9653\u9654\u9655\u9656\u9657\u9658\u9659\u965a\u965b\u965c\u965d\u965e\u965f\u9660\u9661\u9662\u9663\u9664\u9665\u9666\u9667\u9668\u9669\u966a\u966b\u966c\u966d\u966e\u966f\u9670\u9671\u9672\u9673\u9674\u9675\u9676\u9677\u9678\u9679\u967a\u967b\u967c\u967d\u967e\u967f\u9680\u9681\u9682\u9683\u9684\u9685\u9686\u9687\u9688\u9689\u968a\u968b\u968c\u968d\u968e\u968f\u9690\u9691\u9692\u9693\u9694\u9695\u9696\u9697\u9698\u9699\u969a\u969b\u969c\u969d\u969e\u969f\u96a0\u96a1\u96a2\u96a3\u96a4\u96a5\u96a6\u96a7\u96a8\u96a9\u96aa\u96ab\u96ac\u96ad\u96ae\u96af\u96b0\u96b1\u96b2\u96b3\u96b4\u96b5\u96b6\u96b7\u96b8\u96b9\u96ba\u96bb\u96bc\u96bd\u96be\u96bf\u96c0\u96c1\u96c2\u96c3\u96c4\u96c5\u96c6\u96c7\u96c8\u96c9\u96ca\u96cb\u96cc\u96cd\u96ce\u96cf\u96d0\u96d1\u96d2\u96d3\u96d4\u96d5\u96d6\u96d7\u96d8\u96d9\u96da\u96db\u96dc\u96dd\u96de\u96df\u96e0\u96e1\u96e2\u96e3\u96e4\u96e5\u96e6\u96e7\u96e8\u96e9\u96ea\u96eb\u96ec\u96ed\u96ee\u96ef\u96f0\u96f1\u96f2\u96f3\u96f4\u96f5\u96f6\u96f7\u96f8\u96f9\u96fa\u96fb\u96fc\u96fd\u96fe\u96ff\u9700\u9701\u9702\u9703\u9704\u9705\u9706\u9707\u9708\u9709\u970a\u970b\u970c\u970d\u970e\u970f\u9710\u9711\u9712\u9713\u9714\u9715\u9716\u9717\u9718\u9719\u971a\u971b\u971c\u971d\u971e\u971f\u9720\u9721\u9722\u9723\u9724\u9725\u9726\u9727\u9728\u9729\u972a\u972b\u972c\u972d\u972e\u972f\u9730\u9731\u9732\u9733\u9734\u9735\u9736\u9737\u9738\u9739\u973a\u973b\u973c\u973d\u973e\u973f\u9740\u9741\u9742\u9743\u9744\u9745\u9746\u9747\u9748\u9749\u974a\u974b\u974c\u974d\u974e\u974f\u9750\u9751\u9752\u9753\u9754\u9755\u9756\u9757\u9758\u9759\u975a\u975b\u975c\u975d\u975e\u975f\u9760\u9761\u9762\u9763\u9764\u9765\u9766\u9767\u9768\u9769\u976a\u976b\u976c\u976d\u976e\u976f\u9770\u9771\u9772\u9773\u9774\u9775\u9776\u9777\u9778\u9779\u977a\u977b\u977c\u977d\u977e\u977f\u9780\u9781\u9782\u9783\u9784\u9785\u9786\u9787\u9788\u9789\u978a\u978b\u978c\u978d\u978e\u978f\u9790\u9791\u9792\u9793\u9794\u9795\u9796\u9797\u9798\u9799\u979a\u979b\u979c\u979d\u979e\u979f\u97a0\u97a1\u97a2\u97a3\u97a4\u97a5\u97a6\u97a7\u97a8\u97a9\u97aa\u97ab\u97ac\u97ad\u97ae\u97af\u97b0\u97b1\u97b2\u97b3\u97b4\u97b5\u97b6\u97b7\u97b8\u97b9\u97ba\u97bb\u97bc\u97bd\u97be\u97bf\u97c0\u97c1\u97c2\u97c3\u97c4\u97c5\u97c6\u97c7\u97c8\u97c9\u97ca\u97cb\u97cc\u97cd\u97ce\u97cf\u97d0\u97d1\u97d2\u97d3\u97d4\u97d5\u97d6\u97d7\u97d8\u97d9\u97da\u97db\u97dc\u97dd\u97de\u97df\u97e0\u97e1\u97e2\u97e3\u97e4\u97e5\u97e6\u97e7\u97e8\u97e9\u97ea\u97eb\u97ec\u97ed\u97ee\u97ef\u97f0\u97f1\u97f2\u97f3\u97f4\u97f5\u97f6\u97f7\u97f8\u97f9\u97fa\u97fb\u97fc\u97fd\u97fe\u97ff\u9800\u9801\u9802\u9803\u9804\u9805\u9806\u9807\u9808\u9809\u980a\u980b\u980c\u980d\u980e\u980f\u9810\u9811\u9812\u9813\u9814\u9815\u9816\u9817\u9818\u9819\u981a\u981b\u981c\u981d\u981e\u981f\u9820\u9821\u9822\u9823\u9824\u9825\u9826\u9827\u9828\u9829\u982a\u982b\u982c\u982d\u982e\u982f\u9830\u9831\u9832\u9833\u9834\u9835\u9836\u9837\u9838\u9839\u983a\u983b\u983c\u983d\u983e\u983f\u9840\u9841\u9842\u9843\u9844\u9845\u9846\u9847\u9848\u9849\u984a\u984b\u984c\u984d\u984e\u984f\u9850\u9851\u9852\u9853\u9854\u9855\u9856\u9857\u9858\u9859\u985a\u985b\u985c\u985d\u985e\u985f\u9860\u9861\u9862\u9863\u9864\u9865\u9866\u9867\u9868\u9869\u986a\u986b\u986c\u986d\u986e\u986f\u9870\u9871\u9872\u9873\u9874\u9875\u9876\u9877\u9878\u9879\u987a\u987b\u987c\u987d\u987e\u987f\u9880\u9881\u9882\u9883\u9884\u9885\u9886\u9887\u9888\u9889\u988a\u988b\u988c\u988d\u988e\u988f\u9890\u9891\u9892\u9893\u9894\u9895\u9896\u9897\u9898\u9899\u989a\u989b\u989c\u989d\u989e\u989f\u98a0\u98a1\u98a2\u98a3\u98a4\u98a5\u98a6\u98a7\u98a8\u98a9\u98aa\u98ab\u98ac\u98ad\u98ae\u98af\u98b0\u98b1\u98b2\u98b3\u98b4\u98b5\u98b6\u98b7\u98b8\u98b9\u98ba\u98bb\u98bc\u98bd\u98be\u98bf\u98c0\u98c1\u98c2\u98c3\u98c4\u98c5\u98c6\u98c7\u98c8\u98c9\u98ca\u98cb\u98cc\u98cd\u98ce\u98cf\u98d0\u98d1\u98d2\u98d3\u98d4\u98d5\u98d6\u98d7\u98d8\u98d9\u98da\u98db\u98dc\u98dd\u98de\u98df\u98e0\u98e1\u98e2\u98e3\u98e4\u98e5\u98e6\u98e7\u98e8\u98e9\u98ea\u98eb\u98ec\u98ed\u98ee\u98ef\u98f0\u98f1\u98f2\u98f3\u98f4\u98f5\u98f6\u98f7\u98f8\u98f9\u98fa\u98fb\u98fc\u98fd\u98fe\u98ff\u9900\u9901\u9902\u9903\u9904\u9905\u9906\u9907\u9908\u9909\u990a\u990b\u990c\u990d\u990e\u990f\u9910\u9911\u9912\u9913\u9914\u9915\u9916\u9917\u9918\u9919\u991a\u991b\u991c\u991d\u991e\u991f\u9920\u9921\u9922\u9923\u9924\u9925\u9926\u9927\u9928\u9929\u992a\u992b\u992c\u992d\u992e\u992f\u9930\u9931\u9932\u9933\u9934\u9935\u9936\u9937\u9938\u9939\u993a\u993b\u993c\u993d\u993e\u993f\u9940\u9941\u9942\u9943\u9944\u9945\u9946\u9947\u9948\u9949\u994a\u994b\u994c\u994d\u994e\u994f\u9950\u9951\u9952\u9953\u9954\u9955\u9956\u9957\u9958\u9959\u995a\u995b\u995c\u995d\u995e\u995f\u9960\u9961\u9962\u9963\u9964\u9965\u9966\u9967\u9968\u9969\u996a\u996b\u996c\u996d\u996e\u996f\u9970\u9971\u9972\u9973\u9974\u9975\u9976\u9977\u9978\u9979\u997a\u997b\u997c\u997d\u997e\u997f\u9980\u9981\u9982\u9983\u9984\u9985\u9986\u9987\u9988\u9989\u998a\u998b\u998c\u998d\u998e\u998f\u9990\u9991\u9992\u9993\u9994\u9995\u9996\u9997\u9998\u9999\u999a\u999b\u999c\u999d\u999e\u999f\u99a0\u99a1\u99a2\u99a3\u99a4\u99a5\u99a6\u99a7\u99a8\u99a9\u99aa\u99ab\u99ac\u99ad\u99ae\u99af\u99b0\u99b1\u99b2\u99b3\u99b4\u99b5\u99b6\u99b7\u99b8\u99b9\u99ba\u99bb\u99bc\u99bd\u99be\u99bf\u99c0\u99c1\u99c2\u99c3\u99c4\u99c5\u99c6\u99c7\u99c8\u99c9\u99ca\u99cb\u99cc\u99cd\u99ce\u99cf\u99d0\u99d1\u99d2\u99d3\u99d4\u99d5\u99d6\u99d7\u99d8\u99d9\u99da\u99db\u99dc\u99dd\u99de\u99df\u99e0\u99e1\u99e2\u99e3\u99e4\u99e5\u99e6\u99e7\u99e8\u99e9\u99ea\u99eb\u99ec\u99ed\u99ee\u99ef\u99f0\u99f1\u99f2\u99f3\u99f4\u99f5\u99f6\u99f7\u99f8\u99f9\u99fa\u99fb\u99fc\u99fd\u99fe\u99ff\u9a00\u9a01\u9a02\u9a03\u9a04\u9a05\u9a06\u9a07\u9a08\u9a09\u9a0a\u9a0b\u9a0c\u9a0d\u9a0e\u9a0f\u9a10\u9a11\u9a12\u9a13\u9a14\u9a15\u9a16\u9a17\u9a18\u9a19\u9a1a\u9a1b\u9a1c\u9a1d\u9a1e\u9a1f\u9a20\u9a21\u9a22\u9a23\u9a24\u9a25\u9a26\u9a27\u9a28\u9a29\u9a2a\u9a2b\u9a2c\u9a2d\u9a2e\u9a2f\u9a30\u9a31\u9a32\u9a33\u9a34\u9a35\u9a36\u9a37\u9a38\u9a39\u9a3a\u9a3b\u9a3c\u9a3d\u9a3e\u9a3f\u9a40\u9a41\u9a42\u9a43\u9a44\u9a45\u9a46\u9a47\u9a48\u9a49\u9a4a\u9a4b\u9a4c\u9a4d\u9a4e\u9a4f\u9a50\u9a51\u9a52\u9a53\u9a54\u9a55\u9a56\u9a57\u9a58\u9a59\u9a5a\u9a5b\u9a5c\u9a5d\u9a5e\u9a5f\u9a60\u9a61\u9a62\u9a63\u9a64\u9a65\u9a66\u9a67\u9a68\u9a69\u9a6a\u9a6b\u9a6c\u9a6d\u9a6e\u9a6f\u9a70\u9a71\u9a72\u9a73\u9a74\u9a75\u9a76\u9a77\u9a78\u9a79\u9a7a\u9a7b\u9a7c\u9a7d\u9a7e\u9a7f\u9a80\u9a81\u9a82\u9a83\u9a84\u9a85\u9a86\u9a87\u9a88\u9a89\u9a8a\u9a8b\u9a8c\u9a8d\u9a8e\u9a8f\u9a90\u9a91\u9a92\u9a93\u9a94\u9a95\u9a96\u9a97\u9a98\u9a99\u9a9a\u9a9b\u9a9c\u9a9d\u9a9e\u9a9f\u9aa0\u9aa1\u9aa2\u9aa3\u9aa4\u9aa5\u9aa6\u9aa7\u9aa8\u9aa9\u9aaa\u9aab\u9aac\u9aad\u9aae\u9aaf\u9ab0\u9ab1\u9ab2\u9ab3\u9ab4\u9ab5\u9ab6\u9ab7\u9ab8\u9ab9\u9aba\u9abb\u9abc\u9abd\u9abe\u9abf\u9ac0\u9ac1\u9ac2\u9ac3\u9ac4\u9ac5\u9ac6\u9ac7\u9ac8\u9ac9\u9aca\u9acb\u9acc\u9acd\u9ace\u9acf\u9ad0\u9ad1\u9ad2\u9ad3\u9ad4\u9ad5\u9ad6\u9ad7\u9ad8\u9ad9\u9ada\u9adb\u9adc\u9add\u9ade\u9adf\u9ae0\u9ae1\u9ae2\u9ae3\u9ae4\u9ae5\u9ae6\u9ae7\u9ae8\u9ae9\u9aea\u9aeb\u9aec\u9aed\u9aee\u9aef\u9af0\u9af1\u9af2\u9af3\u9af4\u9af5\u9af6\u9af7\u9af8\u9af9\u9afa\u9afb\u9afc\u9afd\u9afe\u9aff\u9b00\u9b01\u9b02\u9b03\u9b04\u9b05\u9b06\u9b07\u9b08\u9b09\u9b0a\u9b0b\u9b0c\u9b0d\u9b0e\u9b0f\u9b10\u9b11\u9b12\u9b13\u9b14\u9b15\u9b16\u9b17\u9b18\u9b19\u9b1a\u9b1b\u9b1c\u9b1d\u9b1e\u9b1f\u9b20\u9b21\u9b22\u9b23\u9b24\u9b25\u9b26\u9b27\u9b28\u9b29\u9b2a\u9b2b\u9b2c\u9b2d\u9b2e\u9b2f\u9b30\u9b31\u9b32\u9b33\u9b34\u9b35\u9b36\u9b37\u9b38\u9b39\u9b3a\u9b3b\u9b3c\u9b3d\u9b3e\u9b3f\u9b40\u9b41\u9b42\u9b43\u9b44\u9b45\u9b46\u9b47\u9b48\u9b49\u9b4a\u9b4b\u9b4c\u9b4d\u9b4e\u9b4f\u9b50\u9b51\u9b52\u9b53\u9b54\u9b55\u9b56\u9b57\u9b58\u9b59\u9b5a\u9b5b\u9b5c\u9b5d\u9b5e\u9b5f\u9b60\u9b61\u9b62\u9b63\u9b64\u9b65\u9b66\u9b67\u9b68\u9b69\u9b6a\u9b6b\u9b6c\u9b6d\u9b6e\u9b6f\u9b70\u9b71\u9b72\u9b73\u9b74\u9b75\u9b76\u9b77\u9b78\u9b79\u9b7a\u9b7b\u9b7c\u9b7d\u9b7e\u9b7f\u9b80\u9b81\u9b82\u9b83\u9b84\u9b85\u9b86\u9b87\u9b88\u9b89\u9b8a\u9b8b\u9b8c\u9b8d\u9b8e\u9b8f\u9b90\u9b91\u9b92\u9b93\u9b94\u9b95\u9b96\u9b97\u9b98\u9b99\u9b9a\u9b9b\u9b9c\u9b9d\u9b9e\u9b9f\u9ba0\u9ba1\u9ba2\u9ba3\u9ba4\u9ba5\u9ba6\u9ba7\u9ba8\u9ba9\u9baa\u9bab\u9bac\u9bad\u9bae\u9baf\u9bb0\u9bb1\u9bb2\u9bb3\u9bb4\u9bb5\u9bb6\u9bb7\u9bb8\u9bb9\u9bba\u9bbb\u9bbc\u9bbd\u9bbe\u9bbf\u9bc0\u9bc1\u9bc2\u9bc3\u9bc4\u9bc5\u9bc6\u9bc7\u9bc8\u9bc9\u9bca\u9bcb\u9bcc\u9bcd\u9bce\u9bcf\u9bd0\u9bd1\u9bd2\u9bd3\u9bd4\u9bd5\u9bd6\u9bd7\u9bd8\u9bd9\u9bda\u9bdb\u9bdc\u9bdd\u9bde\u9bdf\u9be0\u9be1\u9be2\u9be3\u9be4\u9be5\u9be6\u9be7\u9be8\u9be9\u9bea\u9beb\u9bec\u9bed\u9bee\u9bef\u9bf0\u9bf1\u9bf2\u9bf3\u9bf4\u9bf5\u9bf6\u9bf7\u9bf8\u9bf9\u9bfa\u9bfb\u9bfc\u9bfd\u9bfe\u9bff\u9c00\u9c01\u9c02\u9c03\u9c04\u9c05\u9c06\u9c07\u9c08\u9c09\u9c0a\u9c0b\u9c0c\u9c0d\u9c0e\u9c0f\u9c10\u9c11\u9c12\u9c13\u9c14\u9c15\u9c16\u9c17\u9c18\u9c19\u9c1a\u9c1b\u9c1c\u9c1d\u9c1e\u9c1f\u9c20\u9c21\u9c22\u9c23\u9c24\u9c25\u9c26\u9c27\u9c28\u9c29\u9c2a\u9c2b\u9c2c\u9c2d\u9c2e\u9c2f\u9c30\u9c31\u9c32\u9c33\u9c34\u9c35\u9c36\u9c37\u9c38\u9c39\u9c3a\u9c3b\u9c3c\u9c3d\u9c3e\u9c3f\u9c40\u9c41\u9c42\u9c43\u9c44\u9c45\u9c46\u9c47\u9c48\u9c49\u9c4a\u9c4b\u9c4c\u9c4d\u9c4e\u9c4f\u9c50\u9c51\u9c52\u9c53\u9c54\u9c55\u9c56\u9c57\u9c58\u9c59\u9c5a\u9c5b\u9c5c\u9c5d\u9c5e\u9c5f\u9c60\u9c61\u9c62\u9c63\u9c64\u9c65\u9c66\u9c67\u9c68\u9c69\u9c6a\u9c6b\u9c6c\u9c6d\u9c6e\u9c6f\u9c70\u9c71\u9c72\u9c73\u9c74\u9c75\u9c76\u9c77\u9c78\u9c79\u9c7a\u9c7b\u9c7c\u9c7d\u9c7e\u9c7f\u9c80\u9c81\u9c82\u9c83\u9c84\u9c85\u9c86\u9c87\u9c88\u9c89\u9c8a\u9c8b\u9c8c\u9c8d\u9c8e\u9c8f\u9c90\u9c91\u9c92\u9c93\u9c94\u9c95\u9c96\u9c97\u9c98\u9c99\u9c9a\u9c9b\u9c9c\u9c9d\u9c9e\u9c9f\u9ca0\u9ca1\u9ca2\u9ca3\u9ca4\u9ca5\u9ca6\u9ca7\u9ca8\u9ca9\u9caa\u9cab\u9cac\u9cad\u9cae\u9caf\u9cb0\u9cb1\u9cb2\u9cb3\u9cb4\u9cb5\u9cb6\u9cb7\u9cb8\u9cb9\u9cba\u9cbb\u9cbc\u9cbd\u9cbe\u9cbf\u9cc0\u9cc1\u9cc2\u9cc3\u9cc4\u9cc5\u9cc6\u9cc7\u9cc8\u9cc9\u9cca\u9ccb\u9ccc\u9ccd\u9cce\u9ccf\u9cd0\u9cd1\u9cd2\u9cd3\u9cd4\u9cd5\u9cd6\u9cd7\u9cd8\u9cd9\u9cda\u9cdb\u9cdc\u9cdd\u9cde\u9cdf\u9ce0\u9ce1\u9ce2\u9ce3\u9ce4\u9ce5\u9ce6\u9ce7\u9ce8\u9ce9\u9cea\u9ceb\u9cec\u9ced\u9cee\u9cef\u9cf0\u9cf1\u9cf2\u9cf3\u9cf4\u9cf5\u9cf6\u9cf7\u9cf8\u9cf9\u9cfa\u9cfb\u9cfc\u9cfd\u9cfe\u9cff\u9d00\u9d01\u9d02\u9d03\u9d04\u9d05\u9d06\u9d07\u9d08\u9d09\u9d0a\u9d0b\u9d0c\u9d0d\u9d0e\u9d0f\u9d10\u9d11\u9d12\u9d13\u9d14\u9d15\u9d16\u9d17\u9d18\u9d19\u9d1a\u9d1b\u9d1c\u9d1d\u9d1e\u9d1f\u9d20\u9d21\u9d22\u9d23\u9d24\u9d25\u9d26\u9d27\u9d28\u9d29\u9d2a\u9d2b\u9d2c\u9d2d\u9d2e\u9d2f\u9d30\u9d31\u9d32\u9d33\u9d34\u9d35\u9d36\u9d37\u9d38\u9d39\u9d3a\u9d3b\u9d3c\u9d3d\u9d3e\u9d3f\u9d40\u9d41\u9d42\u9d43\u9d44\u9d45\u9d46\u9d47\u9d48\u9d49\u9d4a\u9d4b\u9d4c\u9d4d\u9d4e\u9d4f\u9d50\u9d51\u9d52\u9d53\u9d54\u9d55\u9d56\u9d57\u9d58\u9d59\u9d5a\u9d5b\u9d5c\u9d5d\u9d5e\u9d5f\u9d60\u9d61\u9d62\u9d63\u9d64\u9d65\u9d66\u9d67\u9d68\u9d69\u9d6a\u9d6b\u9d6c\u9d6d\u9d6e\u9d6f\u9d70\u9d71\u9d72\u9d73\u9d74\u9d75\u9d76\u9d77\u9d78\u9d79\u9d7a\u9d7b\u9d7c\u9d7d\u9d7e\u9d7f\u9d80\u9d81\u9d82\u9d83\u9d84\u9d85\u9d86\u9d87\u9d88\u9d89\u9d8a\u9d8b\u9d8c\u9d8d\u9d8e\u9d8f\u9d90\u9d91\u9d92\u9d93\u9d94\u9d95\u9d96\u9d97\u9d98\u9d99\u9d9a\u9d9b\u9d9c\u9d9d\u9d9e\u9d9f\u9da0\u9da1\u9da2\u9da3\u9da4\u9da5\u9da6\u9da7\u9da8\u9da9\u9daa\u9dab\u9dac\u9dad\u9dae\u9daf\u9db0\u9db1\u9db2\u9db3\u9db4\u9db5\u9db6\u9db7\u9db8\u9db9\u9dba\u9dbb\u9dbc\u9dbd\u9dbe\u9dbf\u9dc0\u9dc1\u9dc2\u9dc3\u9dc4\u9dc5\u9dc6\u9dc7\u9dc8\u9dc9\u9dca\u9dcb\u9dcc\u9dcd\u9dce\u9dcf\u9dd0\u9dd1\u9dd2\u9dd3\u9dd4\u9dd5\u9dd6\u9dd7\u9dd8\u9dd9\u9dda\u9ddb\u9ddc\u9ddd\u9dde\u9ddf\u9de0\u9de1\u9de2\u9de3\u9de4\u9de5\u9de6\u9de7\u9de8\u9de9\u9dea\u9deb\u9dec\u9ded\u9dee\u9def\u9df0\u9df1\u9df2\u9df3\u9df4\u9df5\u9df6\u9df7\u9df8\u9df9\u9dfa\u9dfb\u9dfc\u9dfd\u9dfe\u9dff\u9e00\u9e01\u9e02\u9e03\u9e04\u9e05\u9e06\u9e07\u9e08\u9e09\u9e0a\u9e0b\u9e0c\u9e0d\u9e0e\u9e0f\u9e10\u9e11\u9e12\u9e13\u9e14\u9e15\u9e16\u9e17\u9e18\u9e19\u9e1a\u9e1b\u9e1c\u9e1d\u9e1e\u9e1f\u9e20\u9e21\u9e22\u9e23\u9e24\u9e25\u9e26\u9e27\u9e28\u9e29\u9e2a\u9e2b\u9e2c\u9e2d\u9e2e\u9e2f\u9e30\u9e31\u9e32\u9e33\u9e34\u9e35\u9e36\u9e37\u9e38\u9e39\u9e3a\u9e3b\u9e3c\u9e3d\u9e3e\u9e3f\u9e40\u9e41\u9e42\u9e43\u9e44\u9e45\u9e46\u9e47\u9e48\u9e49\u9e4a\u9e4b\u9e4c\u9e4d\u9e4e\u9e4f\u9e50\u9e51\u9e52\u9e53\u9e54\u9e55\u9e56\u9e57\u9e58\u9e59\u9e5a\u9e5b\u9e5c\u9e5d\u9e5e\u9e5f\u9e60\u9e61\u9e62\u9e63\u9e64\u9e65\u9e66\u9e67\u9e68\u9e69\u9e6a\u9e6b\u9e6c\u9e6d\u9e6e\u9e6f\u9e70\u9e71\u9e72\u9e73\u9e74\u9e75\u9e76\u9e77\u9e78\u9e79\u9e7a\u9e7b\u9e7c\u9e7d\u9e7e\u9e7f\u9e80\u9e81\u9e82\u9e83\u9e84\u9e85\u9e86\u9e87\u9e88\u9e89\u9e8a\u9e8b\u9e8c\u9e8d\u9e8e\u9e8f\u9e90\u9e91\u9e92\u9e93\u9e94\u9e95\u9e96\u9e97\u9e98\u9e99\u9e9a\u9e9b\u9e9c\u9e9d\u9e9e\u9e9f\u9ea0\u9ea1\u9ea2\u9ea3\u9ea4\u9ea5\u9ea6\u9ea7\u9ea8\u9ea9\u9eaa\u9eab\u9eac\u9ead\u9eae\u9eaf\u9eb0\u9eb1\u9eb2\u9eb3\u9eb4\u9eb5\u9eb6\u9eb7\u9eb8\u9eb9\u9eba\u9ebb\u9ebc\u9ebd\u9ebe\u9ebf\u9ec0\u9ec1\u9ec2\u9ec3\u9ec4\u9ec5\u9ec6\u9ec7\u9ec8\u9ec9\u9eca\u9ecb\u9ecc\u9ecd\u9ece\u9ecf\u9ed0\u9ed1\u9ed2\u9ed3\u9ed4\u9ed5\u9ed6\u9ed7\u9ed8\u9ed9\u9eda\u9edb\u9edc\u9edd\u9ede\u9edf\u9ee0\u9ee1\u9ee2\u9ee3\u9ee4\u9ee5\u9ee6\u9ee7\u9ee8\u9ee9\u9eea\u9eeb\u9eec\u9eed\u9eee\u9eef\u9ef0\u9ef1\u9ef2\u9ef3\u9ef4\u9ef5\u9ef6\u9ef7\u9ef8\u9ef9\u9efa\u9efb\u9efc\u9efd\u9efe\u9eff\u9f00\u9f01\u9f02\u9f03\u9f04\u9f05\u9f06\u9f07\u9f08\u9f09\u9f0a\u9f0b\u9f0c\u9f0d\u9f0e\u9f0f\u9f10\u9f11\u9f12\u9f13\u9f14\u9f15\u9f16\u9f17\u9f18\u9f19\u9f1a\u9f1b\u9f1c\u9f1d\u9f1e\u9f1f\u9f20\u9f21\u9f22\u9f23\u9f24\u9f25\u9f26\u9f27\u9f28\u9f29\u9f2a\u9f2b\u9f2c\u9f2d\u9f2e\u9f2f\u9f30\u9f31\u9f32\u9f33\u9f34\u9f35\u9f36\u9f37\u9f38\u9f39\u9f3a\u9f3b\u9f3c\u9f3d\u9f3e\u9f3f\u9f40\u9f41\u9f42\u9f43\u9f44\u9f45\u9f46\u9f47\u9f48\u9f49\u9f4a\u9f4b\u9f4c\u9f4d\u9f4e\u9f4f\u9f50\u9f51\u9f52\u9f53\u9f54\u9f55\u9f56\u9f57\u9f58\u9f59\u9f5a\u9f5b\u9f5c\u9f5d\u9f5e\u9f5f\u9f60\u9f61\u9f62\u9f63\u9f64\u9f65\u9f66\u9f67\u9f68\u9f69\u9f6a\u9f6b\u9f6c\u9f6d\u9f6e\u9f6f\u9f70\u9f71\u9f72\u9f73\u9f74\u9f75\u9f76\u9f77\u9f78\u9f79\u9f7a\u9f7b\u9f7c\u9f7d\u9f7e\u9f7f\u9f80\u9f81\u9f82\u9f83\u9f84\u9f85\u9f86\u9f87\u9f88\u9f89\u9f8a\u9f8b\u9f8c\u9f8d\u9f8e\u9f8f\u9f90\u9f91\u9f92\u9f93\u9f94\u9f95\u9f96\u9f97\u9f98\u9f99\u9f9a\u9f9b\u9f9c\u9f9d\u9f9e\u9f9f\u9fa0\u9fa1\u9fa2\u9fa3\u9fa4\u9fa5\u9fa6\u9fa7\u9fa8\u9fa9\u9faa\u9fab\u9fac\u9fad\u9fae\u9faf\u9fb0\u9fb1\u9fb2\u9fb3\u9fb4\u9fb5\u9fb6\u9fb7\u9fb8\u9fb9\u9fba\u9fbb\ua000\ua001\ua002\ua003\ua004\ua005\ua006\ua007\ua008\ua009\ua00a\ua00b\ua00c\ua00d\ua00e\ua00f\ua010\ua011\ua012\ua013\ua014\ua016\ua017\ua018\ua019\ua01a\ua01b\ua01c\ua01d\ua01e\ua01f\ua020\ua021\ua022\ua023\ua024\ua025\ua026\ua027\ua028\ua029\ua02a\ua02b\ua02c\ua02d\ua02e\ua02f\ua030\ua031\ua032\ua033\ua034\ua035\ua036\ua037\ua038\ua039\ua03a\ua03b\ua03c\ua03d\ua03e\ua03f\ua040\ua041\ua042\ua043\ua044\ua045\ua046\ua047\ua048\ua049\ua04a\ua04b\ua04c\ua04d\ua04e\ua04f\ua050\ua051\ua052\ua053\ua054\ua055\ua056\ua057\ua058\ua059\ua05a\ua05b\ua05c\ua05d\ua05e\ua05f\ua060\ua061\ua062\ua063\ua064\ua065\ua066\ua067\ua068\ua069\ua06a\ua06b\ua06c\ua06d\ua06e\ua06f\ua070\ua071\ua072\ua073\ua074\ua075\ua076\ua077\ua078\ua079\ua07a\ua07b\ua07c\ua07d\ua07e\ua07f\ua080\ua081\ua082\ua083\ua084\ua085\ua086\ua087\ua088\ua089\ua08a\ua08b\ua08c\ua08d\ua08e\ua08f\ua090\ua091\ua092\ua093\ua094\ua095\ua096\ua097\ua098\ua099\ua09a\ua09b\ua09c\ua09d\ua09e\ua09f\ua0a0\ua0a1\ua0a2\ua0a3\ua0a4\ua0a5\ua0a6\ua0a7\ua0a8\ua0a9\ua0aa\ua0ab\ua0ac\ua0ad\ua0ae\ua0af\ua0b0\ua0b1\ua0b2\ua0b3\ua0b4\ua0b5\ua0b6\ua0b7\ua0b8\ua0b9\ua0ba\ua0bb\ua0bc\ua0bd\ua0be\ua0bf\ua0c0\ua0c1\ua0c2\ua0c3\ua0c4\ua0c5\ua0c6\ua0c7\ua0c8\ua0c9\ua0ca\ua0cb\ua0cc\ua0cd\ua0ce\ua0cf\ua0d0\ua0d1\ua0d2\ua0d3\ua0d4\ua0d5\ua0d6\ua0d7\ua0d8\ua0d9\ua0da\ua0db\ua0dc\ua0dd\ua0de\ua0df\ua0e0\ua0e1\ua0e2\ua0e3\ua0e4\ua0e5\ua0e6\ua0e7\ua0e8\ua0e9\ua0ea\ua0eb\ua0ec\ua0ed\ua0ee\ua0ef\ua0f0\ua0f1\ua0f2\ua0f3\ua0f4\ua0f5\ua0f6\ua0f7\ua0f8\ua0f9\ua0fa\ua0fb\ua0fc\ua0fd\ua0fe\ua0ff\ua100\ua101\ua102\ua103\ua104\ua105\ua106\ua107\ua108\ua109\ua10a\ua10b\ua10c\ua10d\ua10e\ua10f\ua110\ua111\ua112\ua113\ua114\ua115\ua116\ua117\ua118\ua119\ua11a\ua11b\ua11c\ua11d\ua11e\ua11f\ua120\ua121\ua122\ua123\ua124\ua125\ua126\ua127\ua128\ua129\ua12a\ua12b\ua12c\ua12d\ua12e\ua12f\ua130\ua131\ua132\ua133\ua134\ua135\ua136\ua137\ua138\ua139\ua13a\ua13b\ua13c\ua13d\ua13e\ua13f\ua140\ua141\ua142\ua143\ua144\ua145\ua146\ua147\ua148\ua149\ua14a\ua14b\ua14c\ua14d\ua14e\ua14f\ua150\ua151\ua152\ua153\ua154\ua155\ua156\ua157\ua158\ua159\ua15a\ua15b\ua15c\ua15d\ua15e\ua15f\ua160\ua161\ua162\ua163\ua164\ua165\ua166\ua167\ua168\ua169\ua16a\ua16b\ua16c\ua16d\ua16e\ua16f\ua170\ua171\ua172\ua173\ua174\ua175\ua176\ua177\ua178\ua179\ua17a\ua17b\ua17c\ua17d\ua17e\ua17f\ua180\ua181\ua182\ua183\ua184\ua185\ua186\ua187\ua188\ua189\ua18a\ua18b\ua18c\ua18d\ua18e\ua18f\ua190\ua191\ua192\ua193\ua194\ua195\ua196\ua197\ua198\ua199\ua19a\ua19b\ua19c\ua19d\ua19e\ua19f\ua1a0\ua1a1\ua1a2\ua1a3\ua1a4\ua1a5\ua1a6\ua1a7\ua1a8\ua1a9\ua1aa\ua1ab\ua1ac\ua1ad\ua1ae\ua1af\ua1b0\ua1b1\ua1b2\ua1b3\ua1b4\ua1b5\ua1b6\ua1b7\ua1b8\ua1b9\ua1ba\ua1bb\ua1bc\ua1bd\ua1be\ua1bf\ua1c0\ua1c1\ua1c2\ua1c3\ua1c4\ua1c5\ua1c6\ua1c7\ua1c8\ua1c9\ua1ca\ua1cb\ua1cc\ua1cd\ua1ce\ua1cf\ua1d0\ua1d1\ua1d2\ua1d3\ua1d4\ua1d5\ua1d6\ua1d7\ua1d8\ua1d9\ua1da\ua1db\ua1dc\ua1dd\ua1de\ua1df\ua1e0\ua1e1\ua1e2\ua1e3\ua1e4\ua1e5\ua1e6\ua1e7\ua1e8\ua1e9\ua1ea\ua1eb\ua1ec\ua1ed\ua1ee\ua1ef\ua1f0\ua1f1\ua1f2\ua1f3\ua1f4\ua1f5\ua1f6\ua1f7\ua1f8\ua1f9\ua1fa\ua1fb\ua1fc\ua1fd\ua1fe\ua1ff\ua200\ua201\ua202\ua203\ua204\ua205\ua206\ua207\ua208\ua209\ua20a\ua20b\ua20c\ua20d\ua20e\ua20f\ua210\ua211\ua212\ua213\ua214\ua215\ua216\ua217\ua218\ua219\ua21a\ua21b\ua21c\ua21d\ua21e\ua21f\ua220\ua221\ua222\ua223\ua224\ua225\ua226\ua227\ua228\ua229\ua22a\ua22b\ua22c\ua22d\ua22e\ua22f\ua230\ua231\ua232\ua233\ua234\ua235\ua236\ua237\ua238\ua239\ua23a\ua23b\ua23c\ua23d\ua23e\ua23f\ua240\ua241\ua242\ua243\ua244\ua245\ua246\ua247\ua248\ua249\ua24a\ua24b\ua24c\ua24d\ua24e\ua24f\ua250\ua251\ua252\ua253\ua254\ua255\ua256\ua257\ua258\ua259\ua25a\ua25b\ua25c\ua25d\ua25e\ua25f\ua260\ua261\ua262\ua263\ua264\ua265\ua266\ua267\ua268\ua269\ua26a\ua26b\ua26c\ua26d\ua26e\ua26f\ua270\ua271\ua272\ua273\ua274\ua275\ua276\ua277\ua278\ua279\ua27a\ua27b\ua27c\ua27d\ua27e\ua27f\ua280\ua281\ua282\ua283\ua284\ua285\ua286\ua287\ua288\ua289\ua28a\ua28b\ua28c\ua28d\ua28e\ua28f\ua290\ua291\ua292\ua293\ua294\ua295\ua296\ua297\ua298\ua299\ua29a\ua29b\ua29c\ua29d\ua29e\ua29f\ua2a0\ua2a1\ua2a2\ua2a3\ua2a4\ua2a5\ua2a6\ua2a7\ua2a8\ua2a9\ua2aa\ua2ab\ua2ac\ua2ad\ua2ae\ua2af\ua2b0\ua2b1\ua2b2\ua2b3\ua2b4\ua2b5\ua2b6\ua2b7\ua2b8\ua2b9\ua2ba\ua2bb\ua2bc\ua2bd\ua2be\ua2bf\ua2c0\ua2c1\ua2c2\ua2c3\ua2c4\ua2c5\ua2c6\ua2c7\ua2c8\ua2c9\ua2ca\ua2cb\ua2cc\ua2cd\ua2ce\ua2cf\ua2d0\ua2d1\ua2d2\ua2d3\ua2d4\ua2d5\ua2d6\ua2d7\ua2d8\ua2d9\ua2da\ua2db\ua2dc\ua2dd\ua2de\ua2df\ua2e0\ua2e1\ua2e2\ua2e3\ua2e4\ua2e5\ua2e6\ua2e7\ua2e8\ua2e9\ua2ea\ua2eb\ua2ec\ua2ed\ua2ee\ua2ef\ua2f0\ua2f1\ua2f2\ua2f3\ua2f4\ua2f5\ua2f6\ua2f7\ua2f8\ua2f9\ua2fa\ua2fb\ua2fc\ua2fd\ua2fe\ua2ff\ua300\ua301\ua302\ua303\ua304\ua305\ua306\ua307\ua308\ua309\ua30a\ua30b\ua30c\ua30d\ua30e\ua30f\ua310\ua311\ua312\ua313\ua314\ua315\ua316\ua317\ua318\ua319\ua31a\ua31b\ua31c\ua31d\ua31e\ua31f\ua320\ua321\ua322\ua323\ua324\ua325\ua326\ua327\ua328\ua329\ua32a\ua32b\ua32c\ua32d\ua32e\ua32f\ua330\ua331\ua332\ua333\ua334\ua335\ua336\ua337\ua338\ua339\ua33a\ua33b\ua33c\ua33d\ua33e\ua33f\ua340\ua341\ua342\ua343\ua344\ua345\ua346\ua347\ua348\ua349\ua34a\ua34b\ua34c\ua34d\ua34e\ua34f\ua350\ua351\ua352\ua353\ua354\ua355\ua356\ua357\ua358\ua359\ua35a\ua35b\ua35c\ua35d\ua35e\ua35f\ua360\ua361\ua362\ua363\ua364\ua365\ua366\ua367\ua368\ua369\ua36a\ua36b\ua36c\ua36d\ua36e\ua36f\ua370\ua371\ua372\ua373\ua374\ua375\ua376\ua377\ua378\ua379\ua37a\ua37b\ua37c\ua37d\ua37e\ua37f\ua380\ua381\ua382\ua383\ua384\ua385\ua386\ua387\ua388\ua389\ua38a\ua38b\ua38c\ua38d\ua38e\ua38f\ua390\ua391\ua392\ua393\ua394\ua395\ua396\ua397\ua398\ua399\ua39a\ua39b\ua39c\ua39d\ua39e\ua39f\ua3a0\ua3a1\ua3a2\ua3a3\ua3a4\ua3a5\ua3a6\ua3a7\ua3a8\ua3a9\ua3aa\ua3ab\ua3ac\ua3ad\ua3ae\ua3af\ua3b0\ua3b1\ua3b2\ua3b3\ua3b4\ua3b5\ua3b6\ua3b7\ua3b8\ua3b9\ua3ba\ua3bb\ua3bc\ua3bd\ua3be\ua3bf\ua3c0\ua3c1\ua3c2\ua3c3\ua3c4\ua3c5\ua3c6\ua3c7\ua3c8\ua3c9\ua3ca\ua3cb\ua3cc\ua3cd\ua3ce\ua3cf\ua3d0\ua3d1\ua3d2\ua3d3\ua3d4\ua3d5\ua3d6\ua3d7\ua3d8\ua3d9\ua3da\ua3db\ua3dc\ua3dd\ua3de\ua3df\ua3e0\ua3e1\ua3e2\ua3e3\ua3e4\ua3e5\ua3e6\ua3e7\ua3e8\ua3e9\ua3ea\ua3eb\ua3ec\ua3ed\ua3ee\ua3ef\ua3f0\ua3f1\ua3f2\ua3f3\ua3f4\ua3f5\ua3f6\ua3f7\ua3f8\ua3f9\ua3fa\ua3fb\ua3fc\ua3fd\ua3fe\ua3ff\ua400\ua401\ua402\ua403\ua404\ua405\ua406\ua407\ua408\ua409\ua40a\ua40b\ua40c\ua40d\ua40e\ua40f\ua410\ua411\ua412\ua413\ua414\ua415\ua416\ua417\ua418\ua419\ua41a\ua41b\ua41c\ua41d\ua41e\ua41f\ua420\ua421\ua422\ua423\ua424\ua425\ua426\ua427\ua428\ua429\ua42a\ua42b\ua42c\ua42d\ua42e\ua42f\ua430\ua431\ua432\ua433\ua434\ua435\ua436\ua437\ua438\ua439\ua43a\ua43b\ua43c\ua43d\ua43e\ua43f\ua440\ua441\ua442\ua443\ua444\ua445\ua446\ua447\ua448\ua449\ua44a\ua44b\ua44c\ua44d\ua44e\ua44f\ua450\ua451\ua452\ua453\ua454\ua455\ua456\ua457\ua458\ua459\ua45a\ua45b\ua45c\ua45d\ua45e\ua45f\ua460\ua461\ua462\ua463\ua464\ua465\ua466\ua467\ua468\ua469\ua46a\ua46b\ua46c\ua46d\ua46e\ua46f\ua470\ua471\ua472\ua473\ua474\ua475\ua476\ua477\ua478\ua479\ua47a\ua47b\ua47c\ua47d\ua47e\ua47f\ua480\ua481\ua482\ua483\ua484\ua485\ua486\ua487\ua488\ua489\ua48a\ua48b\ua48c\ua800\ua801\ua803\ua804\ua805\ua807\ua808\ua809\ua80a\ua80c\ua80d\ua80e\ua80f\ua810\ua811\ua812\ua813\ua814\ua815\ua816\ua817\ua818\ua819\ua81a\ua81b\ua81c\ua81d\ua81e\ua81f\ua820\ua821\ua822\uac00\uac01\uac02\uac03\uac04\uac05\uac06\uac07\uac08\uac09\uac0a\uac0b\uac0c\uac0d\uac0e\uac0f\uac10\uac11\uac12\uac13\uac14\uac15\uac16\uac17\uac18\uac19\uac1a\uac1b\uac1c\uac1d\uac1e\uac1f\uac20\uac21\uac22\uac23\uac24\uac25\uac26\uac27\uac28\uac29\uac2a\uac2b\uac2c\uac2d\uac2e\uac2f\uac30\uac31\uac32\uac33\uac34\uac35\uac36\uac37\uac38\uac39\uac3a\uac3b\uac3c\uac3d\uac3e\uac3f\uac40\uac41\uac42\uac43\uac44\uac45\uac46\uac47\uac48\uac49\uac4a\uac4b\uac4c\uac4d\uac4e\uac4f\uac50\uac51\uac52\uac53\uac54\uac55\uac56\uac57\uac58\uac59\uac5a\uac5b\uac5c\uac5d\uac5e\uac5f\uac60\uac61\uac62\uac63\uac64\uac65\uac66\uac67\uac68\uac69\uac6a\uac6b\uac6c\uac6d\uac6e\uac6f\uac70\uac71\uac72\uac73\uac74\uac75\uac76\uac77\uac78\uac79\uac7a\uac7b\uac7c\uac7d\uac7e\uac7f\uac80\uac81\uac82\uac83\uac84\uac85\uac86\uac87\uac88\uac89\uac8a\uac8b\uac8c\uac8d\uac8e\uac8f\uac90\uac91\uac92\uac93\uac94\uac95\uac96\uac97\uac98\uac99\uac9a\uac9b\uac9c\uac9d\uac9e\uac9f\uaca0\uaca1\uaca2\uaca3\uaca4\uaca5\uaca6\uaca7\uaca8\uaca9\uacaa\uacab\uacac\uacad\uacae\uacaf\uacb0\uacb1\uacb2\uacb3\uacb4\uacb5\uacb6\uacb7\uacb8\uacb9\uacba\uacbb\uacbc\uacbd\uacbe\uacbf\uacc0\uacc1\uacc2\uacc3\uacc4\uacc5\uacc6\uacc7\uacc8\uacc9\uacca\uaccb\uaccc\uaccd\uacce\uaccf\uacd0\uacd1\uacd2\uacd3\uacd4\uacd5\uacd6\uacd7\uacd8\uacd9\uacda\uacdb\uacdc\uacdd\uacde\uacdf\uace0\uace1\uace2\uace3\uace4\uace5\uace6\uace7\uace8\uace9\uacea\uaceb\uacec\uaced\uacee\uacef\uacf0\uacf1\uacf2\uacf3\uacf4\uacf5\uacf6\uacf7\uacf8\uacf9\uacfa\uacfb\uacfc\uacfd\uacfe\uacff\uad00\uad01\uad02\uad03\uad04\uad05\uad06\uad07\uad08\uad09\uad0a\uad0b\uad0c\uad0d\uad0e\uad0f\uad10\uad11\uad12\uad13\uad14\uad15\uad16\uad17\uad18\uad19\uad1a\uad1b\uad1c\uad1d\uad1e\uad1f\uad20\uad21\uad22\uad23\uad24\uad25\uad26\uad27\uad28\uad29\uad2a\uad2b\uad2c\uad2d\uad2e\uad2f\uad30\uad31\uad32\uad33\uad34\uad35\uad36\uad37\uad38\uad39\uad3a\uad3b\uad3c\uad3d\uad3e\uad3f\uad40\uad41\uad42\uad43\uad44\uad45\uad46\uad47\uad48\uad49\uad4a\uad4b\uad4c\uad4d\uad4e\uad4f\uad50\uad51\uad52\uad53\uad54\uad55\uad56\uad57\uad58\uad59\uad5a\uad5b\uad5c\uad5d\uad5e\uad5f\uad60\uad61\uad62\uad63\uad64\uad65\uad66\uad67\uad68\uad69\uad6a\uad6b\uad6c\uad6d\uad6e\uad6f\uad70\uad71\uad72\uad73\uad74\uad75\uad76\uad77\uad78\uad79\uad7a\uad7b\uad7c\uad7d\uad7e\uad7f\uad80\uad81\uad82\uad83\uad84\uad85\uad86\uad87\uad88\uad89\uad8a\uad8b\uad8c\uad8d\uad8e\uad8f\uad90\uad91\uad92\uad93\uad94\uad95\uad96\uad97\uad98\uad99\uad9a\uad9b\uad9c\uad9d\uad9e\uad9f\uada0\uada1\uada2\uada3\uada4\uada5\uada6\uada7\uada8\uada9\uadaa\uadab\uadac\uadad\uadae\uadaf\uadb0\uadb1\uadb2\uadb3\uadb4\uadb5\uadb6\uadb7\uadb8\uadb9\uadba\uadbb\uadbc\uadbd\uadbe\uadbf\uadc0\uadc1\uadc2\uadc3\uadc4\uadc5\uadc6\uadc7\uadc8\uadc9\uadca\uadcb\uadcc\uadcd\uadce\uadcf\uadd0\uadd1\uadd2\uadd3\uadd4\uadd5\uadd6\uadd7\uadd8\uadd9\uadda\uaddb\uaddc\uaddd\uadde\uaddf\uade0\uade1\uade2\uade3\uade4\uade5\uade6\uade7\uade8\uade9\uadea\uadeb\uadec\uaded\uadee\uadef\uadf0\uadf1\uadf2\uadf3\uadf4\uadf5\uadf6\uadf7\uadf8\uadf9\uadfa\uadfb\uadfc\uadfd\uadfe\uadff\uae00\uae01\uae02\uae03\uae04\uae05\uae06\uae07\uae08\uae09\uae0a\uae0b\uae0c\uae0d\uae0e\uae0f\uae10\uae11\uae12\uae13\uae14\uae15\uae16\uae17\uae18\uae19\uae1a\uae1b\uae1c\uae1d\uae1e\uae1f\uae20\uae21\uae22\uae23\uae24\uae25\uae26\uae27\uae28\uae29\uae2a\uae2b\uae2c\uae2d\uae2e\uae2f\uae30\uae31\uae32\uae33\uae34\uae35\uae36\uae37\uae38\uae39\uae3a\uae3b\uae3c\uae3d\uae3e\uae3f\uae40\uae41\uae42\uae43\uae44\uae45\uae46\uae47\uae48\uae49\uae4a\uae4b\uae4c\uae4d\uae4e\uae4f\uae50\uae51\uae52\uae53\uae54\uae55\uae56\uae57\uae58\uae59\uae5a\uae5b\uae5c\uae5d\uae5e\uae5f\uae60\uae61\uae62\uae63\uae64\uae65\uae66\uae67\uae68\uae69\uae6a\uae6b\uae6c\uae6d\uae6e\uae6f\uae70\uae71\uae72\uae73\uae74\uae75\uae76\uae77\uae78\uae79\uae7a\uae7b\uae7c\uae7d\uae7e\uae7f\uae80\uae81\uae82\uae83\uae84\uae85\uae86\uae87\uae88\uae89\uae8a\uae8b\uae8c\uae8d\uae8e\uae8f\uae90\uae91\uae92\uae93\uae94\uae95\uae96\uae97\uae98\uae99\uae9a\uae9b\uae9c\uae9d\uae9e\uae9f\uaea0\uaea1\uaea2\uaea3\uaea4\uaea5\uaea6\uaea7\uaea8\uaea9\uaeaa\uaeab\uaeac\uaead\uaeae\uaeaf\uaeb0\uaeb1\uaeb2\uaeb3\uaeb4\uaeb5\uaeb6\uaeb7\uaeb8\uaeb9\uaeba\uaebb\uaebc\uaebd\uaebe\uaebf\uaec0\uaec1\uaec2\uaec3\uaec4\uaec5\uaec6\uaec7\uaec8\uaec9\uaeca\uaecb\uaecc\uaecd\uaece\uaecf\uaed0\uaed1\uaed2\uaed3\uaed4\uaed5\uaed6\uaed7\uaed8\uaed9\uaeda\uaedb\uaedc\uaedd\uaede\uaedf\uaee0\uaee1\uaee2\uaee3\uaee4\uaee5\uaee6\uaee7\uaee8\uaee9\uaeea\uaeeb\uaeec\uaeed\uaeee\uaeef\uaef0\uaef1\uaef2\uaef3\uaef4\uaef5\uaef6\uaef7\uaef8\uaef9\uaefa\uaefb\uaefc\uaefd\uaefe\uaeff\uaf00\uaf01\uaf02\uaf03\uaf04\uaf05\uaf06\uaf07\uaf08\uaf09\uaf0a\uaf0b\uaf0c\uaf0d\uaf0e\uaf0f\uaf10\uaf11\uaf12\uaf13\uaf14\uaf15\uaf16\uaf17\uaf18\uaf19\uaf1a\uaf1b\uaf1c\uaf1d\uaf1e\uaf1f\uaf20\uaf21\uaf22\uaf23\uaf24\uaf25\uaf26\uaf27\uaf28\uaf29\uaf2a\uaf2b\uaf2c\uaf2d\uaf2e\uaf2f\uaf30\uaf31\uaf32\uaf33\uaf34\uaf35\uaf36\uaf37\uaf38\uaf39\uaf3a\uaf3b\uaf3c\uaf3d\uaf3e\uaf3f\uaf40\uaf41\uaf42\uaf43\uaf44\uaf45\uaf46\uaf47\uaf48\uaf49\uaf4a\uaf4b\uaf4c\uaf4d\uaf4e\uaf4f\uaf50\uaf51\uaf52\uaf53\uaf54\uaf55\uaf56\uaf57\uaf58\uaf59\uaf5a\uaf5b\uaf5c\uaf5d\uaf5e\uaf5f\uaf60\uaf61\uaf62\uaf63\uaf64\uaf65\uaf66\uaf67\uaf68\uaf69\uaf6a\uaf6b\uaf6c\uaf6d\uaf6e\uaf6f\uaf70\uaf71\uaf72\uaf73\uaf74\uaf75\uaf76\uaf77\uaf78\uaf79\uaf7a\uaf7b\uaf7c\uaf7d\uaf7e\uaf7f\uaf80\uaf81\uaf82\uaf83\uaf84\uaf85\uaf86\uaf87\uaf88\uaf89\uaf8a\uaf8b\uaf8c\uaf8d\uaf8e\uaf8f\uaf90\uaf91\uaf92\uaf93\uaf94\uaf95\uaf96\uaf97\uaf98\uaf99\uaf9a\uaf9b\uaf9c\uaf9d\uaf9e\uaf9f\uafa0\uafa1\uafa2\uafa3\uafa4\uafa5\uafa6\uafa7\uafa8\uafa9\uafaa\uafab\uafac\uafad\uafae\uafaf\uafb0\uafb1\uafb2\uafb3\uafb4\uafb5\uafb6\uafb7\uafb8\uafb9\uafba\uafbb\uafbc\uafbd\uafbe\uafbf\uafc0\uafc1\uafc2\uafc3\uafc4\uafc5\uafc6\uafc7\uafc8\uafc9\uafca\uafcb\uafcc\uafcd\uafce\uafcf\uafd0\uafd1\uafd2\uafd3\uafd4\uafd5\uafd6\uafd7\uafd8\uafd9\uafda\uafdb\uafdc\uafdd\uafde\uafdf\uafe0\uafe1\uafe2\uafe3\uafe4\uafe5\uafe6\uafe7\uafe8\uafe9\uafea\uafeb\uafec\uafed\uafee\uafef\uaff0\uaff1\uaff2\uaff3\uaff4\uaff5\uaff6\uaff7\uaff8\uaff9\uaffa\uaffb\uaffc\uaffd\uaffe\uafff\ub000\ub001\ub002\ub003\ub004\ub005\ub006\ub007\ub008\ub009\ub00a\ub00b\ub00c\ub00d\ub00e\ub00f\ub010\ub011\ub012\ub013\ub014\ub015\ub016\ub017\ub018\ub019\ub01a\ub01b\ub01c\ub01d\ub01e\ub01f\ub020\ub021\ub022\ub023\ub024\ub025\ub026\ub027\ub028\ub029\ub02a\ub02b\ub02c\ub02d\ub02e\ub02f\ub030\ub031\ub032\ub033\ub034\ub035\ub036\ub037\ub038\ub039\ub03a\ub03b\ub03c\ub03d\ub03e\ub03f\ub040\ub041\ub042\ub043\ub044\ub045\ub046\ub047\ub048\ub049\ub04a\ub04b\ub04c\ub04d\ub04e\ub04f\ub050\ub051\ub052\ub053\ub054\ub055\ub056\ub057\ub058\ub059\ub05a\ub05b\ub05c\ub05d\ub05e\ub05f\ub060\ub061\ub062\ub063\ub064\ub065\ub066\ub067\ub068\ub069\ub06a\ub06b\ub06c\ub06d\ub06e\ub06f\ub070\ub071\ub072\ub073\ub074\ub075\ub076\ub077\ub078\ub079\ub07a\ub07b\ub07c\ub07d\ub07e\ub07f\ub080\ub081\ub082\ub083\ub084\ub085\ub086\ub087\ub088\ub089\ub08a\ub08b\ub08c\ub08d\ub08e\ub08f\ub090\ub091\ub092\ub093\ub094\ub095\ub096\ub097\ub098\ub099\ub09a\ub09b\ub09c\ub09d\ub09e\ub09f\ub0a0\ub0a1\ub0a2\ub0a3\ub0a4\ub0a5\ub0a6\ub0a7\ub0a8\ub0a9\ub0aa\ub0ab\ub0ac\ub0ad\ub0ae\ub0af\ub0b0\ub0b1\ub0b2\ub0b3\ub0b4\ub0b5\ub0b6\ub0b7\ub0b8\ub0b9\ub0ba\ub0bb\ub0bc\ub0bd\ub0be\ub0bf\ub0c0\ub0c1\ub0c2\ub0c3\ub0c4\ub0c5\ub0c6\ub0c7\ub0c8\ub0c9\ub0ca\ub0cb\ub0cc\ub0cd\ub0ce\ub0cf\ub0d0\ub0d1\ub0d2\ub0d3\ub0d4\ub0d5\ub0d6\ub0d7\ub0d8\ub0d9\ub0da\ub0db\ub0dc\ub0dd\ub0de\ub0df\ub0e0\ub0e1\ub0e2\ub0e3\ub0e4\ub0e5\ub0e6\ub0e7\ub0e8\ub0e9\ub0ea\ub0eb\ub0ec\ub0ed\ub0ee\ub0ef\ub0f0\ub0f1\ub0f2\ub0f3\ub0f4\ub0f5\ub0f6\ub0f7\ub0f8\ub0f9\ub0fa\ub0fb\ub0fc\ub0fd\ub0fe\ub0ff\ub100\ub101\ub102\ub103\ub104\ub105\ub106\ub107\ub108\ub109\ub10a\ub10b\ub10c\ub10d\ub10e\ub10f\ub110\ub111\ub112\ub113\ub114\ub115\ub116\ub117\ub118\ub119\ub11a\ub11b\ub11c\ub11d\ub11e\ub11f\ub120\ub121\ub122\ub123\ub124\ub125\ub126\ub127\ub128\ub129\ub12a\ub12b\ub12c\ub12d\ub12e\ub12f\ub130\ub131\ub132\ub133\ub134\ub135\ub136\ub137\ub138\ub139\ub13a\ub13b\ub13c\ub13d\ub13e\ub13f\ub140\ub141\ub142\ub143\ub144\ub145\ub146\ub147\ub148\ub149\ub14a\ub14b\ub14c\ub14d\ub14e\ub14f\ub150\ub151\ub152\ub153\ub154\ub155\ub156\ub157\ub158\ub159\ub15a\ub15b\ub15c\ub15d\ub15e\ub15f\ub160\ub161\ub162\ub163\ub164\ub165\ub166\ub167\ub168\ub169\ub16a\ub16b\ub16c\ub16d\ub16e\ub16f\ub170\ub171\ub172\ub173\ub174\ub175\ub176\ub177\ub178\ub179\ub17a\ub17b\ub17c\ub17d\ub17e\ub17f\ub180\ub181\ub182\ub183\ub184\ub185\ub186\ub187\ub188\ub189\ub18a\ub18b\ub18c\ub18d\ub18e\ub18f\ub190\ub191\ub192\ub193\ub194\ub195\ub196\ub197\ub198\ub199\ub19a\ub19b\ub19c\ub19d\ub19e\ub19f\ub1a0\ub1a1\ub1a2\ub1a3\ub1a4\ub1a5\ub1a6\ub1a7\ub1a8\ub1a9\ub1aa\ub1ab\ub1ac\ub1ad\ub1ae\ub1af\ub1b0\ub1b1\ub1b2\ub1b3\ub1b4\ub1b5\ub1b6\ub1b7\ub1b8\ub1b9\ub1ba\ub1bb\ub1bc\ub1bd\ub1be\ub1bf\ub1c0\ub1c1\ub1c2\ub1c3\ub1c4\ub1c5\ub1c6\ub1c7\ub1c8\ub1c9\ub1ca\ub1cb\ub1cc\ub1cd\ub1ce\ub1cf\ub1d0\ub1d1\ub1d2\ub1d3\ub1d4\ub1d5\ub1d6\ub1d7\ub1d8\ub1d9\ub1da\ub1db\ub1dc\ub1dd\ub1de\ub1df\ub1e0\ub1e1\ub1e2\ub1e3\ub1e4\ub1e5\ub1e6\ub1e7\ub1e8\ub1e9\ub1ea\ub1eb\ub1ec\ub1ed\ub1ee\ub1ef\ub1f0\ub1f1\ub1f2\ub1f3\ub1f4\ub1f5\ub1f6\ub1f7\ub1f8\ub1f9\ub1fa\ub1fb\ub1fc\ub1fd\ub1fe\ub1ff\ub200\ub201\ub202\ub203\ub204\ub205\ub206\ub207\ub208\ub209\ub20a\ub20b\ub20c\ub20d\ub20e\ub20f\ub210\ub211\ub212\ub213\ub214\ub215\ub216\ub217\ub218\ub219\ub21a\ub21b\ub21c\ub21d\ub21e\ub21f\ub220\ub221\ub222\ub223\ub224\ub225\ub226\ub227\ub228\ub229\ub22a\ub22b\ub22c\ub22d\ub22e\ub22f\ub230\ub231\ub232\ub233\ub234\ub235\ub236\ub237\ub238\ub239\ub23a\ub23b\ub23c\ub23d\ub23e\ub23f\ub240\ub241\ub242\ub243\ub244\ub245\ub246\ub247\ub248\ub249\ub24a\ub24b\ub24c\ub24d\ub24e\ub24f\ub250\ub251\ub252\ub253\ub254\ub255\ub256\ub257\ub258\ub259\ub25a\ub25b\ub25c\ub25d\ub25e\ub25f\ub260\ub261\ub262\ub263\ub264\ub265\ub266\ub267\ub268\ub269\ub26a\ub26b\ub26c\ub26d\ub26e\ub26f\ub270\ub271\ub272\ub273\ub274\ub275\ub276\ub277\ub278\ub279\ub27a\ub27b\ub27c\ub27d\ub27e\ub27f\ub280\ub281\ub282\ub283\ub284\ub285\ub286\ub287\ub288\ub289\ub28a\ub28b\ub28c\ub28d\ub28e\ub28f\ub290\ub291\ub292\ub293\ub294\ub295\ub296\ub297\ub298\ub299\ub29a\ub29b\ub29c\ub29d\ub29e\ub29f\ub2a0\ub2a1\ub2a2\ub2a3\ub2a4\ub2a5\ub2a6\ub2a7\ub2a8\ub2a9\ub2aa\ub2ab\ub2ac\ub2ad\ub2ae\ub2af\ub2b0\ub2b1\ub2b2\ub2b3\ub2b4\ub2b5\ub2b6\ub2b7\ub2b8\ub2b9\ub2ba\ub2bb\ub2bc\ub2bd\ub2be\ub2bf\ub2c0\ub2c1\ub2c2\ub2c3\ub2c4\ub2c5\ub2c6\ub2c7\ub2c8\ub2c9\ub2ca\ub2cb\ub2cc\ub2cd\ub2ce\ub2cf\ub2d0\ub2d1\ub2d2\ub2d3\ub2d4\ub2d5\ub2d6\ub2d7\ub2d8\ub2d9\ub2da\ub2db\ub2dc\ub2dd\ub2de\ub2df\ub2e0\ub2e1\ub2e2\ub2e3\ub2e4\ub2e5\ub2e6\ub2e7\ub2e8\ub2e9\ub2ea\ub2eb\ub2ec\ub2ed\ub2ee\ub2ef\ub2f0\ub2f1\ub2f2\ub2f3\ub2f4\ub2f5\ub2f6\ub2f7\ub2f8\ub2f9\ub2fa\ub2fb\ub2fc\ub2fd\ub2fe\ub2ff\ub300\ub301\ub302\ub303\ub304\ub305\ub306\ub307\ub308\ub309\ub30a\ub30b\ub30c\ub30d\ub30e\ub30f\ub310\ub311\ub312\ub313\ub314\ub315\ub316\ub317\ub318\ub319\ub31a\ub31b\ub31c\ub31d\ub31e\ub31f\ub320\ub321\ub322\ub323\ub324\ub325\ub326\ub327\ub328\ub329\ub32a\ub32b\ub32c\ub32d\ub32e\ub32f\ub330\ub331\ub332\ub333\ub334\ub335\ub336\ub337\ub338\ub339\ub33a\ub33b\ub33c\ub33d\ub33e\ub33f\ub340\ub341\ub342\ub343\ub344\ub345\ub346\ub347\ub348\ub349\ub34a\ub34b\ub34c\ub34d\ub34e\ub34f\ub350\ub351\ub352\ub353\ub354\ub355\ub356\ub357\ub358\ub359\ub35a\ub35b\ub35c\ub35d\ub35e\ub35f\ub360\ub361\ub362\ub363\ub364\ub365\ub366\ub367\ub368\ub369\ub36a\ub36b\ub36c\ub36d\ub36e\ub36f\ub370\ub371\ub372\ub373\ub374\ub375\ub376\ub377\ub378\ub379\ub37a\ub37b\ub37c\ub37d\ub37e\ub37f\ub380\ub381\ub382\ub383\ub384\ub385\ub386\ub387\ub388\ub389\ub38a\ub38b\ub38c\ub38d\ub38e\ub38f\ub390\ub391\ub392\ub393\ub394\ub395\ub396\ub397\ub398\ub399\ub39a\ub39b\ub39c\ub39d\ub39e\ub39f\ub3a0\ub3a1\ub3a2\ub3a3\ub3a4\ub3a5\ub3a6\ub3a7\ub3a8\ub3a9\ub3aa\ub3ab\ub3ac\ub3ad\ub3ae\ub3af\ub3b0\ub3b1\ub3b2\ub3b3\ub3b4\ub3b5\ub3b6\ub3b7\ub3b8\ub3b9\ub3ba\ub3bb\ub3bc\ub3bd\ub3be\ub3bf\ub3c0\ub3c1\ub3c2\ub3c3\ub3c4\ub3c5\ub3c6\ub3c7\ub3c8\ub3c9\ub3ca\ub3cb\ub3cc\ub3cd\ub3ce\ub3cf\ub3d0\ub3d1\ub3d2\ub3d3\ub3d4\ub3d5\ub3d6\ub3d7\ub3d8\ub3d9\ub3da\ub3db\ub3dc\ub3dd\ub3de\ub3df\ub3e0\ub3e1\ub3e2\ub3e3\ub3e4\ub3e5\ub3e6\ub3e7\ub3e8\ub3e9\ub3ea\ub3eb\ub3ec\ub3ed\ub3ee\ub3ef\ub3f0\ub3f1\ub3f2\ub3f3\ub3f4\ub3f5\ub3f6\ub3f7\ub3f8\ub3f9\ub3fa\ub3fb\ub3fc\ub3fd\ub3fe\ub3ff\ub400\ub401\ub402\ub403\ub404\ub405\ub406\ub407\ub408\ub409\ub40a\ub40b\ub40c\ub40d\ub40e\ub40f\ub410\ub411\ub412\ub413\ub414\ub415\ub416\ub417\ub418\ub419\ub41a\ub41b\ub41c\ub41d\ub41e\ub41f\ub420\ub421\ub422\ub423\ub424\ub425\ub426\ub427\ub428\ub429\ub42a\ub42b\ub42c\ub42d\ub42e\ub42f\ub430\ub431\ub432\ub433\ub434\ub435\ub436\ub437\ub438\ub439\ub43a\ub43b\ub43c\ub43d\ub43e\ub43f\ub440\ub441\ub442\ub443\ub444\ub445\ub446\ub447\ub448\ub449\ub44a\ub44b\ub44c\ub44d\ub44e\ub44f\ub450\ub451\ub452\ub453\ub454\ub455\ub456\ub457\ub458\ub459\ub45a\ub45b\ub45c\ub45d\ub45e\ub45f\ub460\ub461\ub462\ub463\ub464\ub465\ub466\ub467\ub468\ub469\ub46a\ub46b\ub46c\ub46d\ub46e\ub46f\ub470\ub471\ub472\ub473\ub474\ub475\ub476\ub477\ub478\ub479\ub47a\ub47b\ub47c\ub47d\ub47e\ub47f\ub480\ub481\ub482\ub483\ub484\ub485\ub486\ub487\ub488\ub489\ub48a\ub48b\ub48c\ub48d\ub48e\ub48f\ub490\ub491\ub492\ub493\ub494\ub495\ub496\ub497\ub498\ub499\ub49a\ub49b\ub49c\ub49d\ub49e\ub49f\ub4a0\ub4a1\ub4a2\ub4a3\ub4a4\ub4a5\ub4a6\ub4a7\ub4a8\ub4a9\ub4aa\ub4ab\ub4ac\ub4ad\ub4ae\ub4af\ub4b0\ub4b1\ub4b2\ub4b3\ub4b4\ub4b5\ub4b6\ub4b7\ub4b8\ub4b9\ub4ba\ub4bb\ub4bc\ub4bd\ub4be\ub4bf\ub4c0\ub4c1\ub4c2\ub4c3\ub4c4\ub4c5\ub4c6\ub4c7\ub4c8\ub4c9\ub4ca\ub4cb\ub4cc\ub4cd\ub4ce\ub4cf\ub4d0\ub4d1\ub4d2\ub4d3\ub4d4\ub4d5\ub4d6\ub4d7\ub4d8\ub4d9\ub4da\ub4db\ub4dc\ub4dd\ub4de\ub4df\ub4e0\ub4e1\ub4e2\ub4e3\ub4e4\ub4e5\ub4e6\ub4e7\ub4e8\ub4e9\ub4ea\ub4eb\ub4ec\ub4ed\ub4ee\ub4ef\ub4f0\ub4f1\ub4f2\ub4f3\ub4f4\ub4f5\ub4f6\ub4f7\ub4f8\ub4f9\ub4fa\ub4fb\ub4fc\ub4fd\ub4fe\ub4ff\ub500\ub501\ub502\ub503\ub504\ub505\ub506\ub507\ub508\ub509\ub50a\ub50b\ub50c\ub50d\ub50e\ub50f\ub510\ub511\ub512\ub513\ub514\ub515\ub516\ub517\ub518\ub519\ub51a\ub51b\ub51c\ub51d\ub51e\ub51f\ub520\ub521\ub522\ub523\ub524\ub525\ub526\ub527\ub528\ub529\ub52a\ub52b\ub52c\ub52d\ub52e\ub52f\ub530\ub531\ub532\ub533\ub534\ub535\ub536\ub537\ub538\ub539\ub53a\ub53b\ub53c\ub53d\ub53e\ub53f\ub540\ub541\ub542\ub543\ub544\ub545\ub546\ub547\ub548\ub549\ub54a\ub54b\ub54c\ub54d\ub54e\ub54f\ub550\ub551\ub552\ub553\ub554\ub555\ub556\ub557\ub558\ub559\ub55a\ub55b\ub55c\ub55d\ub55e\ub55f\ub560\ub561\ub562\ub563\ub564\ub565\ub566\ub567\ub568\ub569\ub56a\ub56b\ub56c\ub56d\ub56e\ub56f\ub570\ub571\ub572\ub573\ub574\ub575\ub576\ub577\ub578\ub579\ub57a\ub57b\ub57c\ub57d\ub57e\ub57f\ub580\ub581\ub582\ub583\ub584\ub585\ub586\ub587\ub588\ub589\ub58a\ub58b\ub58c\ub58d\ub58e\ub58f\ub590\ub591\ub592\ub593\ub594\ub595\ub596\ub597\ub598\ub599\ub59a\ub59b\ub59c\ub59d\ub59e\ub59f\ub5a0\ub5a1\ub5a2\ub5a3\ub5a4\ub5a5\ub5a6\ub5a7\ub5a8\ub5a9\ub5aa\ub5ab\ub5ac\ub5ad\ub5ae\ub5af\ub5b0\ub5b1\ub5b2\ub5b3\ub5b4\ub5b5\ub5b6\ub5b7\ub5b8\ub5b9\ub5ba\ub5bb\ub5bc\ub5bd\ub5be\ub5bf\ub5c0\ub5c1\ub5c2\ub5c3\ub5c4\ub5c5\ub5c6\ub5c7\ub5c8\ub5c9\ub5ca\ub5cb\ub5cc\ub5cd\ub5ce\ub5cf\ub5d0\ub5d1\ub5d2\ub5d3\ub5d4\ub5d5\ub5d6\ub5d7\ub5d8\ub5d9\ub5da\ub5db\ub5dc\ub5dd\ub5de\ub5df\ub5e0\ub5e1\ub5e2\ub5e3\ub5e4\ub5e5\ub5e6\ub5e7\ub5e8\ub5e9\ub5ea\ub5eb\ub5ec\ub5ed\ub5ee\ub5ef\ub5f0\ub5f1\ub5f2\ub5f3\ub5f4\ub5f5\ub5f6\ub5f7\ub5f8\ub5f9\ub5fa\ub5fb\ub5fc\ub5fd\ub5fe\ub5ff\ub600\ub601\ub602\ub603\ub604\ub605\ub606\ub607\ub608\ub609\ub60a\ub60b\ub60c\ub60d\ub60e\ub60f\ub610\ub611\ub612\ub613\ub614\ub615\ub616\ub617\ub618\ub619\ub61a\ub61b\ub61c\ub61d\ub61e\ub61f\ub620\ub621\ub622\ub623\ub624\ub625\ub626\ub627\ub628\ub629\ub62a\ub62b\ub62c\ub62d\ub62e\ub62f\ub630\ub631\ub632\ub633\ub634\ub635\ub636\ub637\ub638\ub639\ub63a\ub63b\ub63c\ub63d\ub63e\ub63f\ub640\ub641\ub642\ub643\ub644\ub645\ub646\ub647\ub648\ub649\ub64a\ub64b\ub64c\ub64d\ub64e\ub64f\ub650\ub651\ub652\ub653\ub654\ub655\ub656\ub657\ub658\ub659\ub65a\ub65b\ub65c\ub65d\ub65e\ub65f\ub660\ub661\ub662\ub663\ub664\ub665\ub666\ub667\ub668\ub669\ub66a\ub66b\ub66c\ub66d\ub66e\ub66f\ub670\ub671\ub672\ub673\ub674\ub675\ub676\ub677\ub678\ub679\ub67a\ub67b\ub67c\ub67d\ub67e\ub67f\ub680\ub681\ub682\ub683\ub684\ub685\ub686\ub687\ub688\ub689\ub68a\ub68b\ub68c\ub68d\ub68e\ub68f\ub690\ub691\ub692\ub693\ub694\ub695\ub696\ub697\ub698\ub699\ub69a\ub69b\ub69c\ub69d\ub69e\ub69f\ub6a0\ub6a1\ub6a2\ub6a3\ub6a4\ub6a5\ub6a6\ub6a7\ub6a8\ub6a9\ub6aa\ub6ab\ub6ac\ub6ad\ub6ae\ub6af\ub6b0\ub6b1\ub6b2\ub6b3\ub6b4\ub6b5\ub6b6\ub6b7\ub6b8\ub6b9\ub6ba\ub6bb\ub6bc\ub6bd\ub6be\ub6bf\ub6c0\ub6c1\ub6c2\ub6c3\ub6c4\ub6c5\ub6c6\ub6c7\ub6c8\ub6c9\ub6ca\ub6cb\ub6cc\ub6cd\ub6ce\ub6cf\ub6d0\ub6d1\ub6d2\ub6d3\ub6d4\ub6d5\ub6d6\ub6d7\ub6d8\ub6d9\ub6da\ub6db\ub6dc\ub6dd\ub6de\ub6df\ub6e0\ub6e1\ub6e2\ub6e3\ub6e4\ub6e5\ub6e6\ub6e7\ub6e8\ub6e9\ub6ea\ub6eb\ub6ec\ub6ed\ub6ee\ub6ef\ub6f0\ub6f1\ub6f2\ub6f3\ub6f4\ub6f5\ub6f6\ub6f7\ub6f8\ub6f9\ub6fa\ub6fb\ub6fc\ub6fd\ub6fe\ub6ff\ub700\ub701\ub702\ub703\ub704\ub705\ub706\ub707\ub708\ub709\ub70a\ub70b\ub70c\ub70d\ub70e\ub70f\ub710\ub711\ub712\ub713\ub714\ub715\ub716\ub717\ub718\ub719\ub71a\ub71b\ub71c\ub71d\ub71e\ub71f\ub720\ub721\ub722\ub723\ub724\ub725\ub726\ub727\ub728\ub729\ub72a\ub72b\ub72c\ub72d\ub72e\ub72f\ub730\ub731\ub732\ub733\ub734\ub735\ub736\ub737\ub738\ub739\ub73a\ub73b\ub73c\ub73d\ub73e\ub73f\ub740\ub741\ub742\ub743\ub744\ub745\ub746\ub747\ub748\ub749\ub74a\ub74b\ub74c\ub74d\ub74e\ub74f\ub750\ub751\ub752\ub753\ub754\ub755\ub756\ub757\ub758\ub759\ub75a\ub75b\ub75c\ub75d\ub75e\ub75f\ub760\ub761\ub762\ub763\ub764\ub765\ub766\ub767\ub768\ub769\ub76a\ub76b\ub76c\ub76d\ub76e\ub76f\ub770\ub771\ub772\ub773\ub774\ub775\ub776\ub777\ub778\ub779\ub77a\ub77b\ub77c\ub77d\ub77e\ub77f\ub780\ub781\ub782\ub783\ub784\ub785\ub786\ub787\ub788\ub789\ub78a\ub78b\ub78c\ub78d\ub78e\ub78f\ub790\ub791\ub792\ub793\ub794\ub795\ub796\ub797\ub798\ub799\ub79a\ub79b\ub79c\ub79d\ub79e\ub79f\ub7a0\ub7a1\ub7a2\ub7a3\ub7a4\ub7a5\ub7a6\ub7a7\ub7a8\ub7a9\ub7aa\ub7ab\ub7ac\ub7ad\ub7ae\ub7af\ub7b0\ub7b1\ub7b2\ub7b3\ub7b4\ub7b5\ub7b6\ub7b7\ub7b8\ub7b9\ub7ba\ub7bb\ub7bc\ub7bd\ub7be\ub7bf\ub7c0\ub7c1\ub7c2\ub7c3\ub7c4\ub7c5\ub7c6\ub7c7\ub7c8\ub7c9\ub7ca\ub7cb\ub7cc\ub7cd\ub7ce\ub7cf\ub7d0\ub7d1\ub7d2\ub7d3\ub7d4\ub7d5\ub7d6\ub7d7\ub7d8\ub7d9\ub7da\ub7db\ub7dc\ub7dd\ub7de\ub7df\ub7e0\ub7e1\ub7e2\ub7e3\ub7e4\ub7e5\ub7e6\ub7e7\ub7e8\ub7e9\ub7ea\ub7eb\ub7ec\ub7ed\ub7ee\ub7ef\ub7f0\ub7f1\ub7f2\ub7f3\ub7f4\ub7f5\ub7f6\ub7f7\ub7f8\ub7f9\ub7fa\ub7fb\ub7fc\ub7fd\ub7fe\ub7ff\ub800\ub801\ub802\ub803\ub804\ub805\ub806\ub807\ub808\ub809\ub80a\ub80b\ub80c\ub80d\ub80e\ub80f\ub810\ub811\ub812\ub813\ub814\ub815\ub816\ub817\ub818\ub819\ub81a\ub81b\ub81c\ub81d\ub81e\ub81f\ub820\ub821\ub822\ub823\ub824\ub825\ub826\ub827\ub828\ub829\ub82a\ub82b\ub82c\ub82d\ub82e\ub82f\ub830\ub831\ub832\ub833\ub834\ub835\ub836\ub837\ub838\ub839\ub83a\ub83b\ub83c\ub83d\ub83e\ub83f\ub840\ub841\ub842\ub843\ub844\ub845\ub846\ub847\ub848\ub849\ub84a\ub84b\ub84c\ub84d\ub84e\ub84f\ub850\ub851\ub852\ub853\ub854\ub855\ub856\ub857\ub858\ub859\ub85a\ub85b\ub85c\ub85d\ub85e\ub85f\ub860\ub861\ub862\ub863\ub864\ub865\ub866\ub867\ub868\ub869\ub86a\ub86b\ub86c\ub86d\ub86e\ub86f\ub870\ub871\ub872\ub873\ub874\ub875\ub876\ub877\ub878\ub879\ub87a\ub87b\ub87c\ub87d\ub87e\ub87f\ub880\ub881\ub882\ub883\ub884\ub885\ub886\ub887\ub888\ub889\ub88a\ub88b\ub88c\ub88d\ub88e\ub88f\ub890\ub891\ub892\ub893\ub894\ub895\ub896\ub897\ub898\ub899\ub89a\ub89b\ub89c\ub89d\ub89e\ub89f\ub8a0\ub8a1\ub8a2\ub8a3\ub8a4\ub8a5\ub8a6\ub8a7\ub8a8\ub8a9\ub8aa\ub8ab\ub8ac\ub8ad\ub8ae\ub8af\ub8b0\ub8b1\ub8b2\ub8b3\ub8b4\ub8b5\ub8b6\ub8b7\ub8b8\ub8b9\ub8ba\ub8bb\ub8bc\ub8bd\ub8be\ub8bf\ub8c0\ub8c1\ub8c2\ub8c3\ub8c4\ub8c5\ub8c6\ub8c7\ub8c8\ub8c9\ub8ca\ub8cb\ub8cc\ub8cd\ub8ce\ub8cf\ub8d0\ub8d1\ub8d2\ub8d3\ub8d4\ub8d5\ub8d6\ub8d7\ub8d8\ub8d9\ub8da\ub8db\ub8dc\ub8dd\ub8de\ub8df\ub8e0\ub8e1\ub8e2\ub8e3\ub8e4\ub8e5\ub8e6\ub8e7\ub8e8\ub8e9\ub8ea\ub8eb\ub8ec\ub8ed\ub8ee\ub8ef\ub8f0\ub8f1\ub8f2\ub8f3\ub8f4\ub8f5\ub8f6\ub8f7\ub8f8\ub8f9\ub8fa\ub8fb\ub8fc\ub8fd\ub8fe\ub8ff\ub900\ub901\ub902\ub903\ub904\ub905\ub906\ub907\ub908\ub909\ub90a\ub90b\ub90c\ub90d\ub90e\ub90f\ub910\ub911\ub912\ub913\ub914\ub915\ub916\ub917\ub918\ub919\ub91a\ub91b\ub91c\ub91d\ub91e\ub91f\ub920\ub921\ub922\ub923\ub924\ub925\ub926\ub927\ub928\ub929\ub92a\ub92b\ub92c\ub92d\ub92e\ub92f\ub930\ub931\ub932\ub933\ub934\ub935\ub936\ub937\ub938\ub939\ub93a\ub93b\ub93c\ub93d\ub93e\ub93f\ub940\ub941\ub942\ub943\ub944\ub945\ub946\ub947\ub948\ub949\ub94a\ub94b\ub94c\ub94d\ub94e\ub94f\ub950\ub951\ub952\ub953\ub954\ub955\ub956\ub957\ub958\ub959\ub95a\ub95b\ub95c\ub95d\ub95e\ub95f\ub960\ub961\ub962\ub963\ub964\ub965\ub966\ub967\ub968\ub969\ub96a\ub96b\ub96c\ub96d\ub96e\ub96f\ub970\ub971\ub972\ub973\ub974\ub975\ub976\ub977\ub978\ub979\ub97a\ub97b\ub97c\ub97d\ub97e\ub97f\ub980\ub981\ub982\ub983\ub984\ub985\ub986\ub987\ub988\ub989\ub98a\ub98b\ub98c\ub98d\ub98e\ub98f\ub990\ub991\ub992\ub993\ub994\ub995\ub996\ub997\ub998\ub999\ub99a\ub99b\ub99c\ub99d\ub99e\ub99f\ub9a0\ub9a1\ub9a2\ub9a3\ub9a4\ub9a5\ub9a6\ub9a7\ub9a8\ub9a9\ub9aa\ub9ab\ub9ac\ub9ad\ub9ae\ub9af\ub9b0\ub9b1\ub9b2\ub9b3\ub9b4\ub9b5\ub9b6\ub9b7\ub9b8\ub9b9\ub9ba\ub9bb\ub9bc\ub9bd\ub9be\ub9bf\ub9c0\ub9c1\ub9c2\ub9c3\ub9c4\ub9c5\ub9c6\ub9c7\ub9c8\ub9c9\ub9ca\ub9cb\ub9cc\ub9cd\ub9ce\ub9cf\ub9d0\ub9d1\ub9d2\ub9d3\ub9d4\ub9d5\ub9d6\ub9d7\ub9d8\ub9d9\ub9da\ub9db\ub9dc\ub9dd\ub9de\ub9df\ub9e0\ub9e1\ub9e2\ub9e3\ub9e4\ub9e5\ub9e6\ub9e7\ub9e8\ub9e9\ub9ea\ub9eb\ub9ec\ub9ed\ub9ee\ub9ef\ub9f0\ub9f1\ub9f2\ub9f3\ub9f4\ub9f5\ub9f6\ub9f7\ub9f8\ub9f9\ub9fa\ub9fb\ub9fc\ub9fd\ub9fe\ub9ff\uba00\uba01\uba02\uba03\uba04\uba05\uba06\uba07\uba08\uba09\uba0a\uba0b\uba0c\uba0d\uba0e\uba0f\uba10\uba11\uba12\uba13\uba14\uba15\uba16\uba17\uba18\uba19\uba1a\uba1b\uba1c\uba1d\uba1e\uba1f\uba20\uba21\uba22\uba23\uba24\uba25\uba26\uba27\uba28\uba29\uba2a\uba2b\uba2c\uba2d\uba2e\uba2f\uba30\uba31\uba32\uba33\uba34\uba35\uba36\uba37\uba38\uba39\uba3a\uba3b\uba3c\uba3d\uba3e\uba3f\uba40\uba41\uba42\uba43\uba44\uba45\uba46\uba47\uba48\uba49\uba4a\uba4b\uba4c\uba4d\uba4e\uba4f\uba50\uba51\uba52\uba53\uba54\uba55\uba56\uba57\uba58\uba59\uba5a\uba5b\uba5c\uba5d\uba5e\uba5f\uba60\uba61\uba62\uba63\uba64\uba65\uba66\uba67\uba68\uba69\uba6a\uba6b\uba6c\uba6d\uba6e\uba6f\uba70\uba71\uba72\uba73\uba74\uba75\uba76\uba77\uba78\uba79\uba7a\uba7b\uba7c\uba7d\uba7e\uba7f\uba80\uba81\uba82\uba83\uba84\uba85\uba86\uba87\uba88\uba89\uba8a\uba8b\uba8c\uba8d\uba8e\uba8f\uba90\uba91\uba92\uba93\uba94\uba95\uba96\uba97\uba98\uba99\uba9a\uba9b\uba9c\uba9d\uba9e\uba9f\ubaa0\ubaa1\ubaa2\ubaa3\ubaa4\ubaa5\ubaa6\ubaa7\ubaa8\ubaa9\ubaaa\ubaab\ubaac\ubaad\ubaae\ubaaf\ubab0\ubab1\ubab2\ubab3\ubab4\ubab5\ubab6\ubab7\ubab8\ubab9\ubaba\ubabb\ubabc\ubabd\ubabe\ubabf\ubac0\ubac1\ubac2\ubac3\ubac4\ubac5\ubac6\ubac7\ubac8\ubac9\ubaca\ubacb\ubacc\ubacd\ubace\ubacf\ubad0\ubad1\ubad2\ubad3\ubad4\ubad5\ubad6\ubad7\ubad8\ubad9\ubada\ubadb\ubadc\ubadd\ubade\ubadf\ubae0\ubae1\ubae2\ubae3\ubae4\ubae5\ubae6\ubae7\ubae8\ubae9\ubaea\ubaeb\ubaec\ubaed\ubaee\ubaef\ubaf0\ubaf1\ubaf2\ubaf3\ubaf4\ubaf5\ubaf6\ubaf7\ubaf8\ubaf9\ubafa\ubafb\ubafc\ubafd\ubafe\ubaff\ubb00\ubb01\ubb02\ubb03\ubb04\ubb05\ubb06\ubb07\ubb08\ubb09\ubb0a\ubb0b\ubb0c\ubb0d\ubb0e\ubb0f\ubb10\ubb11\ubb12\ubb13\ubb14\ubb15\ubb16\ubb17\ubb18\ubb19\ubb1a\ubb1b\ubb1c\ubb1d\ubb1e\ubb1f\ubb20\ubb21\ubb22\ubb23\ubb24\ubb25\ubb26\ubb27\ubb28\ubb29\ubb2a\ubb2b\ubb2c\ubb2d\ubb2e\ubb2f\ubb30\ubb31\ubb32\ubb33\ubb34\ubb35\ubb36\ubb37\ubb38\ubb39\ubb3a\ubb3b\ubb3c\ubb3d\ubb3e\ubb3f\ubb40\ubb41\ubb42\ubb43\ubb44\ubb45\ubb46\ubb47\ubb48\ubb49\ubb4a\ubb4b\ubb4c\ubb4d\ubb4e\ubb4f\ubb50\ubb51\ubb52\ubb53\ubb54\ubb55\ubb56\ubb57\ubb58\ubb59\ubb5a\ubb5b\ubb5c\ubb5d\ubb5e\ubb5f\ubb60\ubb61\ubb62\ubb63\ubb64\ubb65\ubb66\ubb67\ubb68\ubb69\ubb6a\ubb6b\ubb6c\ubb6d\ubb6e\ubb6f\ubb70\ubb71\ubb72\ubb73\ubb74\ubb75\ubb76\ubb77\ubb78\ubb79\ubb7a\ubb7b\ubb7c\ubb7d\ubb7e\ubb7f\ubb80\ubb81\ubb82\ubb83\ubb84\ubb85\ubb86\ubb87\ubb88\ubb89\ubb8a\ubb8b\ubb8c\ubb8d\ubb8e\ubb8f\ubb90\ubb91\ubb92\ubb93\ubb94\ubb95\ubb96\ubb97\ubb98\ubb99\ubb9a\ubb9b\ubb9c\ubb9d\ubb9e\ubb9f\ubba0\ubba1\ubba2\ubba3\ubba4\ubba5\ubba6\ubba7\ubba8\ubba9\ubbaa\ubbab\ubbac\ubbad\ubbae\ubbaf\ubbb0\ubbb1\ubbb2\ubbb3\ubbb4\ubbb5\ubbb6\ubbb7\ubbb8\ubbb9\ubbba\ubbbb\ubbbc\ubbbd\ubbbe\ubbbf\ubbc0\ubbc1\ubbc2\ubbc3\ubbc4\ubbc5\ubbc6\ubbc7\ubbc8\ubbc9\ubbca\ubbcb\ubbcc\ubbcd\ubbce\ubbcf\ubbd0\ubbd1\ubbd2\ubbd3\ubbd4\ubbd5\ubbd6\ubbd7\ubbd8\ubbd9\ubbda\ubbdb\ubbdc\ubbdd\ubbde\ubbdf\ubbe0\ubbe1\ubbe2\ubbe3\ubbe4\ubbe5\ubbe6\ubbe7\ubbe8\ubbe9\ubbea\ubbeb\ubbec\ubbed\ubbee\ubbef\ubbf0\ubbf1\ubbf2\ubbf3\ubbf4\ubbf5\ubbf6\ubbf7\ubbf8\ubbf9\ubbfa\ubbfb\ubbfc\ubbfd\ubbfe\ubbff\ubc00\ubc01\ubc02\ubc03\ubc04\ubc05\ubc06\ubc07\ubc08\ubc09\ubc0a\ubc0b\ubc0c\ubc0d\ubc0e\ubc0f\ubc10\ubc11\ubc12\ubc13\ubc14\ubc15\ubc16\ubc17\ubc18\ubc19\ubc1a\ubc1b\ubc1c\ubc1d\ubc1e\ubc1f\ubc20\ubc21\ubc22\ubc23\ubc24\ubc25\ubc26\ubc27\ubc28\ubc29\ubc2a\ubc2b\ubc2c\ubc2d\ubc2e\ubc2f\ubc30\ubc31\ubc32\ubc33\ubc34\ubc35\ubc36\ubc37\ubc38\ubc39\ubc3a\ubc3b\ubc3c\ubc3d\ubc3e\ubc3f\ubc40\ubc41\ubc42\ubc43\ubc44\ubc45\ubc46\ubc47\ubc48\ubc49\ubc4a\ubc4b\ubc4c\ubc4d\ubc4e\ubc4f\ubc50\ubc51\ubc52\ubc53\ubc54\ubc55\ubc56\ubc57\ubc58\ubc59\ubc5a\ubc5b\ubc5c\ubc5d\ubc5e\ubc5f\ubc60\ubc61\ubc62\ubc63\ubc64\ubc65\ubc66\ubc67\ubc68\ubc69\ubc6a\ubc6b\ubc6c\ubc6d\ubc6e\ubc6f\ubc70\ubc71\ubc72\ubc73\ubc74\ubc75\ubc76\ubc77\ubc78\ubc79\ubc7a\ubc7b\ubc7c\ubc7d\ubc7e\ubc7f\ubc80\ubc81\ubc82\ubc83\ubc84\ubc85\ubc86\ubc87\ubc88\ubc89\ubc8a\ubc8b\ubc8c\ubc8d\ubc8e\ubc8f\ubc90\ubc91\ubc92\ubc93\ubc94\ubc95\ubc96\ubc97\ubc98\ubc99\ubc9a\ubc9b\ubc9c\ubc9d\ubc9e\ubc9f\ubca0\ubca1\ubca2\ubca3\ubca4\ubca5\ubca6\ubca7\ubca8\ubca9\ubcaa\ubcab\ubcac\ubcad\ubcae\ubcaf\ubcb0\ubcb1\ubcb2\ubcb3\ubcb4\ubcb5\ubcb6\ubcb7\ubcb8\ubcb9\ubcba\ubcbb\ubcbc\ubcbd\ubcbe\ubcbf\ubcc0\ubcc1\ubcc2\ubcc3\ubcc4\ubcc5\ubcc6\ubcc7\ubcc8\ubcc9\ubcca\ubccb\ubccc\ubccd\ubcce\ubccf\ubcd0\ubcd1\ubcd2\ubcd3\ubcd4\ubcd5\ubcd6\ubcd7\ubcd8\ubcd9\ubcda\ubcdb\ubcdc\ubcdd\ubcde\ubcdf\ubce0\ubce1\ubce2\ubce3\ubce4\ubce5\ubce6\ubce7\ubce8\ubce9\ubcea\ubceb\ubcec\ubced\ubcee\ubcef\ubcf0\ubcf1\ubcf2\ubcf3\ubcf4\ubcf5\ubcf6\ubcf7\ubcf8\ubcf9\ubcfa\ubcfb\ubcfc\ubcfd\ubcfe\ubcff\ubd00\ubd01\ubd02\ubd03\ubd04\ubd05\ubd06\ubd07\ubd08\ubd09\ubd0a\ubd0b\ubd0c\ubd0d\ubd0e\ubd0f\ubd10\ubd11\ubd12\ubd13\ubd14\ubd15\ubd16\ubd17\ubd18\ubd19\ubd1a\ubd1b\ubd1c\ubd1d\ubd1e\ubd1f\ubd20\ubd21\ubd22\ubd23\ubd24\ubd25\ubd26\ubd27\ubd28\ubd29\ubd2a\ubd2b\ubd2c\ubd2d\ubd2e\ubd2f\ubd30\ubd31\ubd32\ubd33\ubd34\ubd35\ubd36\ubd37\ubd38\ubd39\ubd3a\ubd3b\ubd3c\ubd3d\ubd3e\ubd3f\ubd40\ubd41\ubd42\ubd43\ubd44\ubd45\ubd46\ubd47\ubd48\ubd49\ubd4a\ubd4b\ubd4c\ubd4d\ubd4e\ubd4f\ubd50\ubd51\ubd52\ubd53\ubd54\ubd55\ubd56\ubd57\ubd58\ubd59\ubd5a\ubd5b\ubd5c\ubd5d\ubd5e\ubd5f\ubd60\ubd61\ubd62\ubd63\ubd64\ubd65\ubd66\ubd67\ubd68\ubd69\ubd6a\ubd6b\ubd6c\ubd6d\ubd6e\ubd6f\ubd70\ubd71\ubd72\ubd73\ubd74\ubd75\ubd76\ubd77\ubd78\ubd79\ubd7a\ubd7b\ubd7c\ubd7d\ubd7e\ubd7f\ubd80\ubd81\ubd82\ubd83\ubd84\ubd85\ubd86\ubd87\ubd88\ubd89\ubd8a\ubd8b\ubd8c\ubd8d\ubd8e\ubd8f\ubd90\ubd91\ubd92\ubd93\ubd94\ubd95\ubd96\ubd97\ubd98\ubd99\ubd9a\ubd9b\ubd9c\ubd9d\ubd9e\ubd9f\ubda0\ubda1\ubda2\ubda3\ubda4\ubda5\ubda6\ubda7\ubda8\ubda9\ubdaa\ubdab\ubdac\ubdad\ubdae\ubdaf\ubdb0\ubdb1\ubdb2\ubdb3\ubdb4\ubdb5\ubdb6\ubdb7\ubdb8\ubdb9\ubdba\ubdbb\ubdbc\ubdbd\ubdbe\ubdbf\ubdc0\ubdc1\ubdc2\ubdc3\ubdc4\ubdc5\ubdc6\ubdc7\ubdc8\ubdc9\ubdca\ubdcb\ubdcc\ubdcd\ubdce\ubdcf\ubdd0\ubdd1\ubdd2\ubdd3\ubdd4\ubdd5\ubdd6\ubdd7\ubdd8\ubdd9\ubdda\ubddb\ubddc\ubddd\ubdde\ubddf\ubde0\ubde1\ubde2\ubde3\ubde4\ubde5\ubde6\ubde7\ubde8\ubde9\ubdea\ubdeb\ubdec\ubded\ubdee\ubdef\ubdf0\ubdf1\ubdf2\ubdf3\ubdf4\ubdf5\ubdf6\ubdf7\ubdf8\ubdf9\ubdfa\ubdfb\ubdfc\ubdfd\ubdfe\ubdff\ube00\ube01\ube02\ube03\ube04\ube05\ube06\ube07\ube08\ube09\ube0a\ube0b\ube0c\ube0d\ube0e\ube0f\ube10\ube11\ube12\ube13\ube14\ube15\ube16\ube17\ube18\ube19\ube1a\ube1b\ube1c\ube1d\ube1e\ube1f\ube20\ube21\ube22\ube23\ube24\ube25\ube26\ube27\ube28\ube29\ube2a\ube2b\ube2c\ube2d\ube2e\ube2f\ube30\ube31\ube32\ube33\ube34\ube35\ube36\ube37\ube38\ube39\ube3a\ube3b\ube3c\ube3d\ube3e\ube3f\ube40\ube41\ube42\ube43\ube44\ube45\ube46\ube47\ube48\ube49\ube4a\ube4b\ube4c\ube4d\ube4e\ube4f\ube50\ube51\ube52\ube53\ube54\ube55\ube56\ube57\ube58\ube59\ube5a\ube5b\ube5c\ube5d\ube5e\ube5f\ube60\ube61\ube62\ube63\ube64\ube65\ube66\ube67\ube68\ube69\ube6a\ube6b\ube6c\ube6d\ube6e\ube6f\ube70\ube71\ube72\ube73\ube74\ube75\ube76\ube77\ube78\ube79\ube7a\ube7b\ube7c\ube7d\ube7e\ube7f\ube80\ube81\ube82\ube83\ube84\ube85\ube86\ube87\ube88\ube89\ube8a\ube8b\ube8c\ube8d\ube8e\ube8f\ube90\ube91\ube92\ube93\ube94\ube95\ube96\ube97\ube98\ube99\ube9a\ube9b\ube9c\ube9d\ube9e\ube9f\ubea0\ubea1\ubea2\ubea3\ubea4\ubea5\ubea6\ubea7\ubea8\ubea9\ubeaa\ubeab\ubeac\ubead\ubeae\ubeaf\ubeb0\ubeb1\ubeb2\ubeb3\ubeb4\ubeb5\ubeb6\ubeb7\ubeb8\ubeb9\ubeba\ubebb\ubebc\ubebd\ubebe\ubebf\ubec0\ubec1\ubec2\ubec3\ubec4\ubec5\ubec6\ubec7\ubec8\ubec9\ubeca\ubecb\ubecc\ubecd\ubece\ubecf\ubed0\ubed1\ubed2\ubed3\ubed4\ubed5\ubed6\ubed7\ubed8\ubed9\ubeda\ubedb\ubedc\ubedd\ubede\ubedf\ubee0\ubee1\ubee2\ubee3\ubee4\ubee5\ubee6\ubee7\ubee8\ubee9\ubeea\ubeeb\ubeec\ubeed\ubeee\ubeef\ubef0\ubef1\ubef2\ubef3\ubef4\ubef5\ubef6\ubef7\ubef8\ubef9\ubefa\ubefb\ubefc\ubefd\ubefe\ubeff\ubf00\ubf01\ubf02\ubf03\ubf04\ubf05\ubf06\ubf07\ubf08\ubf09\ubf0a\ubf0b\ubf0c\ubf0d\ubf0e\ubf0f\ubf10\ubf11\ubf12\ubf13\ubf14\ubf15\ubf16\ubf17\ubf18\ubf19\ubf1a\ubf1b\ubf1c\ubf1d\ubf1e\ubf1f\ubf20\ubf21\ubf22\ubf23\ubf24\ubf25\ubf26\ubf27\ubf28\ubf29\ubf2a\ubf2b\ubf2c\ubf2d\ubf2e\ubf2f\ubf30\ubf31\ubf32\ubf33\ubf34\ubf35\ubf36\ubf37\ubf38\ubf39\ubf3a\ubf3b\ubf3c\ubf3d\ubf3e\ubf3f\ubf40\ubf41\ubf42\ubf43\ubf44\ubf45\ubf46\ubf47\ubf48\ubf49\ubf4a\ubf4b\ubf4c\ubf4d\ubf4e\ubf4f\ubf50\ubf51\ubf52\ubf53\ubf54\ubf55\ubf56\ubf57\ubf58\ubf59\ubf5a\ubf5b\ubf5c\ubf5d\ubf5e\ubf5f\ubf60\ubf61\ubf62\ubf63\ubf64\ubf65\ubf66\ubf67\ubf68\ubf69\ubf6a\ubf6b\ubf6c\ubf6d\ubf6e\ubf6f\ubf70\ubf71\ubf72\ubf73\ubf74\ubf75\ubf76\ubf77\ubf78\ubf79\ubf7a\ubf7b\ubf7c\ubf7d\ubf7e\ubf7f\ubf80\ubf81\ubf82\ubf83\ubf84\ubf85\ubf86\ubf87\ubf88\ubf89\ubf8a\ubf8b\ubf8c\ubf8d\ubf8e\ubf8f\ubf90\ubf91\ubf92\ubf93\ubf94\ubf95\ubf96\ubf97\ubf98\ubf99\ubf9a\ubf9b\ubf9c\ubf9d\ubf9e\ubf9f\ubfa0\ubfa1\ubfa2\ubfa3\ubfa4\ubfa5\ubfa6\ubfa7\ubfa8\ubfa9\ubfaa\ubfab\ubfac\ubfad\ubfae\ubfaf\ubfb0\ubfb1\ubfb2\ubfb3\ubfb4\ubfb5\ubfb6\ubfb7\ubfb8\ubfb9\ubfba\ubfbb\ubfbc\ubfbd\ubfbe\ubfbf\ubfc0\ubfc1\ubfc2\ubfc3\ubfc4\ubfc5\ubfc6\ubfc7\ubfc8\ubfc9\ubfca\ubfcb\ubfcc\ubfcd\ubfce\ubfcf\ubfd0\ubfd1\ubfd2\ubfd3\ubfd4\ubfd5\ubfd6\ubfd7\ubfd8\ubfd9\ubfda\ubfdb\ubfdc\ubfdd\ubfde\ubfdf\ubfe0\ubfe1\ubfe2\ubfe3\ubfe4\ubfe5\ubfe6\ubfe7\ubfe8\ubfe9\ubfea\ubfeb\ubfec\ubfed\ubfee\ubfef\ubff0\ubff1\ubff2\ubff3\ubff4\ubff5\ubff6\ubff7\ubff8\ubff9\ubffa\ubffb\ubffc\ubffd\ubffe\ubfff\uc000\uc001\uc002\uc003\uc004\uc005\uc006\uc007\uc008\uc009\uc00a\uc00b\uc00c\uc00d\uc00e\uc00f\uc010\uc011\uc012\uc013\uc014\uc015\uc016\uc017\uc018\uc019\uc01a\uc01b\uc01c\uc01d\uc01e\uc01f\uc020\uc021\uc022\uc023\uc024\uc025\uc026\uc027\uc028\uc029\uc02a\uc02b\uc02c\uc02d\uc02e\uc02f\uc030\uc031\uc032\uc033\uc034\uc035\uc036\uc037\uc038\uc039\uc03a\uc03b\uc03c\uc03d\uc03e\uc03f\uc040\uc041\uc042\uc043\uc044\uc045\uc046\uc047\uc048\uc049\uc04a\uc04b\uc04c\uc04d\uc04e\uc04f\uc050\uc051\uc052\uc053\uc054\uc055\uc056\uc057\uc058\uc059\uc05a\uc05b\uc05c\uc05d\uc05e\uc05f\uc060\uc061\uc062\uc063\uc064\uc065\uc066\uc067\uc068\uc069\uc06a\uc06b\uc06c\uc06d\uc06e\uc06f\uc070\uc071\uc072\uc073\uc074\uc075\uc076\uc077\uc078\uc079\uc07a\uc07b\uc07c\uc07d\uc07e\uc07f\uc080\uc081\uc082\uc083\uc084\uc085\uc086\uc087\uc088\uc089\uc08a\uc08b\uc08c\uc08d\uc08e\uc08f\uc090\uc091\uc092\uc093\uc094\uc095\uc096\uc097\uc098\uc099\uc09a\uc09b\uc09c\uc09d\uc09e\uc09f\uc0a0\uc0a1\uc0a2\uc0a3\uc0a4\uc0a5\uc0a6\uc0a7\uc0a8\uc0a9\uc0aa\uc0ab\uc0ac\uc0ad\uc0ae\uc0af\uc0b0\uc0b1\uc0b2\uc0b3\uc0b4\uc0b5\uc0b6\uc0b7\uc0b8\uc0b9\uc0ba\uc0bb\uc0bc\uc0bd\uc0be\uc0bf\uc0c0\uc0c1\uc0c2\uc0c3\uc0c4\uc0c5\uc0c6\uc0c7\uc0c8\uc0c9\uc0ca\uc0cb\uc0cc\uc0cd\uc0ce\uc0cf\uc0d0\uc0d1\uc0d2\uc0d3\uc0d4\uc0d5\uc0d6\uc0d7\uc0d8\uc0d9\uc0da\uc0db\uc0dc\uc0dd\uc0de\uc0df\uc0e0\uc0e1\uc0e2\uc0e3\uc0e4\uc0e5\uc0e6\uc0e7\uc0e8\uc0e9\uc0ea\uc0eb\uc0ec\uc0ed\uc0ee\uc0ef\uc0f0\uc0f1\uc0f2\uc0f3\uc0f4\uc0f5\uc0f6\uc0f7\uc0f8\uc0f9\uc0fa\uc0fb\uc0fc\uc0fd\uc0fe\uc0ff\uc100\uc101\uc102\uc103\uc104\uc105\uc106\uc107\uc108\uc109\uc10a\uc10b\uc10c\uc10d\uc10e\uc10f\uc110\uc111\uc112\uc113\uc114\uc115\uc116\uc117\uc118\uc119\uc11a\uc11b\uc11c\uc11d\uc11e\uc11f\uc120\uc121\uc122\uc123\uc124\uc125\uc126\uc127\uc128\uc129\uc12a\uc12b\uc12c\uc12d\uc12e\uc12f\uc130\uc131\uc132\uc133\uc134\uc135\uc136\uc137\uc138\uc139\uc13a\uc13b\uc13c\uc13d\uc13e\uc13f\uc140\uc141\uc142\uc143\uc144\uc145\uc146\uc147\uc148\uc149\uc14a\uc14b\uc14c\uc14d\uc14e\uc14f\uc150\uc151\uc152\uc153\uc154\uc155\uc156\uc157\uc158\uc159\uc15a\uc15b\uc15c\uc15d\uc15e\uc15f\uc160\uc161\uc162\uc163\uc164\uc165\uc166\uc167\uc168\uc169\uc16a\uc16b\uc16c\uc16d\uc16e\uc16f\uc170\uc171\uc172\uc173\uc174\uc175\uc176\uc177\uc178\uc179\uc17a\uc17b\uc17c\uc17d\uc17e\uc17f\uc180\uc181\uc182\uc183\uc184\uc185\uc186\uc187\uc188\uc189\uc18a\uc18b\uc18c\uc18d\uc18e\uc18f\uc190\uc191\uc192\uc193\uc194\uc195\uc196\uc197\uc198\uc199\uc19a\uc19b\uc19c\uc19d\uc19e\uc19f\uc1a0\uc1a1\uc1a2\uc1a3\uc1a4\uc1a5\uc1a6\uc1a7\uc1a8\uc1a9\uc1aa\uc1ab\uc1ac\uc1ad\uc1ae\uc1af\uc1b0\uc1b1\uc1b2\uc1b3\uc1b4\uc1b5\uc1b6\uc1b7\uc1b8\uc1b9\uc1ba\uc1bb\uc1bc\uc1bd\uc1be\uc1bf\uc1c0\uc1c1\uc1c2\uc1c3\uc1c4\uc1c5\uc1c6\uc1c7\uc1c8\uc1c9\uc1ca\uc1cb\uc1cc\uc1cd\uc1ce\uc1cf\uc1d0\uc1d1\uc1d2\uc1d3\uc1d4\uc1d5\uc1d6\uc1d7\uc1d8\uc1d9\uc1da\uc1db\uc1dc\uc1dd\uc1de\uc1df\uc1e0\uc1e1\uc1e2\uc1e3\uc1e4\uc1e5\uc1e6\uc1e7\uc1e8\uc1e9\uc1ea\uc1eb\uc1ec\uc1ed\uc1ee\uc1ef\uc1f0\uc1f1\uc1f2\uc1f3\uc1f4\uc1f5\uc1f6\uc1f7\uc1f8\uc1f9\uc1fa\uc1fb\uc1fc\uc1fd\uc1fe\uc1ff\uc200\uc201\uc202\uc203\uc204\uc205\uc206\uc207\uc208\uc209\uc20a\uc20b\uc20c\uc20d\uc20e\uc20f\uc210\uc211\uc212\uc213\uc214\uc215\uc216\uc217\uc218\uc219\uc21a\uc21b\uc21c\uc21d\uc21e\uc21f\uc220\uc221\uc222\uc223\uc224\uc225\uc226\uc227\uc228\uc229\uc22a\uc22b\uc22c\uc22d\uc22e\uc22f\uc230\uc231\uc232\uc233\uc234\uc235\uc236\uc237\uc238\uc239\uc23a\uc23b\uc23c\uc23d\uc23e\uc23f\uc240\uc241\uc242\uc243\uc244\uc245\uc246\uc247\uc248\uc249\uc24a\uc24b\uc24c\uc24d\uc24e\uc24f\uc250\uc251\uc252\uc253\uc254\uc255\uc256\uc257\uc258\uc259\uc25a\uc25b\uc25c\uc25d\uc25e\uc25f\uc260\uc261\uc262\uc263\uc264\uc265\uc266\uc267\uc268\uc269\uc26a\uc26b\uc26c\uc26d\uc26e\uc26f\uc270\uc271\uc272\uc273\uc274\uc275\uc276\uc277\uc278\uc279\uc27a\uc27b\uc27c\uc27d\uc27e\uc27f\uc280\uc281\uc282\uc283\uc284\uc285\uc286\uc287\uc288\uc289\uc28a\uc28b\uc28c\uc28d\uc28e\uc28f\uc290\uc291\uc292\uc293\uc294\uc295\uc296\uc297\uc298\uc299\uc29a\uc29b\uc29c\uc29d\uc29e\uc29f\uc2a0\uc2a1\uc2a2\uc2a3\uc2a4\uc2a5\uc2a6\uc2a7\uc2a8\uc2a9\uc2aa\uc2ab\uc2ac\uc2ad\uc2ae\uc2af\uc2b0\uc2b1\uc2b2\uc2b3\uc2b4\uc2b5\uc2b6\uc2b7\uc2b8\uc2b9\uc2ba\uc2bb\uc2bc\uc2bd\uc2be\uc2bf\uc2c0\uc2c1\uc2c2\uc2c3\uc2c4\uc2c5\uc2c6\uc2c7\uc2c8\uc2c9\uc2ca\uc2cb\uc2cc\uc2cd\uc2ce\uc2cf\uc2d0\uc2d1\uc2d2\uc2d3\uc2d4\uc2d5\uc2d6\uc2d7\uc2d8\uc2d9\uc2da\uc2db\uc2dc\uc2dd\uc2de\uc2df\uc2e0\uc2e1\uc2e2\uc2e3\uc2e4\uc2e5\uc2e6\uc2e7\uc2e8\uc2e9\uc2ea\uc2eb\uc2ec\uc2ed\uc2ee\uc2ef\uc2f0\uc2f1\uc2f2\uc2f3\uc2f4\uc2f5\uc2f6\uc2f7\uc2f8\uc2f9\uc2fa\uc2fb\uc2fc\uc2fd\uc2fe\uc2ff\uc300\uc301\uc302\uc303\uc304\uc305\uc306\uc307\uc308\uc309\uc30a\uc30b\uc30c\uc30d\uc30e\uc30f\uc310\uc311\uc312\uc313\uc314\uc315\uc316\uc317\uc318\uc319\uc31a\uc31b\uc31c\uc31d\uc31e\uc31f\uc320\uc321\uc322\uc323\uc324\uc325\uc326\uc327\uc328\uc329\uc32a\uc32b\uc32c\uc32d\uc32e\uc32f\uc330\uc331\uc332\uc333\uc334\uc335\uc336\uc337\uc338\uc339\uc33a\uc33b\uc33c\uc33d\uc33e\uc33f\uc340\uc341\uc342\uc343\uc344\uc345\uc346\uc347\uc348\uc349\uc34a\uc34b\uc34c\uc34d\uc34e\uc34f\uc350\uc351\uc352\uc353\uc354\uc355\uc356\uc357\uc358\uc359\uc35a\uc35b\uc35c\uc35d\uc35e\uc35f\uc360\uc361\uc362\uc363\uc364\uc365\uc366\uc367\uc368\uc369\uc36a\uc36b\uc36c\uc36d\uc36e\uc36f\uc370\uc371\uc372\uc373\uc374\uc375\uc376\uc377\uc378\uc379\uc37a\uc37b\uc37c\uc37d\uc37e\uc37f\uc380\uc381\uc382\uc383\uc384\uc385\uc386\uc387\uc388\uc389\uc38a\uc38b\uc38c\uc38d\uc38e\uc38f\uc390\uc391\uc392\uc393\uc394\uc395\uc396\uc397\uc398\uc399\uc39a\uc39b\uc39c\uc39d\uc39e\uc39f\uc3a0\uc3a1\uc3a2\uc3a3\uc3a4\uc3a5\uc3a6\uc3a7\uc3a8\uc3a9\uc3aa\uc3ab\uc3ac\uc3ad\uc3ae\uc3af\uc3b0\uc3b1\uc3b2\uc3b3\uc3b4\uc3b5\uc3b6\uc3b7\uc3b8\uc3b9\uc3ba\uc3bb\uc3bc\uc3bd\uc3be\uc3bf\uc3c0\uc3c1\uc3c2\uc3c3\uc3c4\uc3c5\uc3c6\uc3c7\uc3c8\uc3c9\uc3ca\uc3cb\uc3cc\uc3cd\uc3ce\uc3cf\uc3d0\uc3d1\uc3d2\uc3d3\uc3d4\uc3d5\uc3d6\uc3d7\uc3d8\uc3d9\uc3da\uc3db\uc3dc\uc3dd\uc3de\uc3df\uc3e0\uc3e1\uc3e2\uc3e3\uc3e4\uc3e5\uc3e6\uc3e7\uc3e8\uc3e9\uc3ea\uc3eb\uc3ec\uc3ed\uc3ee\uc3ef\uc3f0\uc3f1\uc3f2\uc3f3\uc3f4\uc3f5\uc3f6\uc3f7\uc3f8\uc3f9\uc3fa\uc3fb\uc3fc\uc3fd\uc3fe\uc3ff\uc400\uc401\uc402\uc403\uc404\uc405\uc406\uc407\uc408\uc409\uc40a\uc40b\uc40c\uc40d\uc40e\uc40f\uc410\uc411\uc412\uc413\uc414\uc415\uc416\uc417\uc418\uc419\uc41a\uc41b\uc41c\uc41d\uc41e\uc41f\uc420\uc421\uc422\uc423\uc424\uc425\uc426\uc427\uc428\uc429\uc42a\uc42b\uc42c\uc42d\uc42e\uc42f\uc430\uc431\uc432\uc433\uc434\uc435\uc436\uc437\uc438\uc439\uc43a\uc43b\uc43c\uc43d\uc43e\uc43f\uc440\uc441\uc442\uc443\uc444\uc445\uc446\uc447\uc448\uc449\uc44a\uc44b\uc44c\uc44d\uc44e\uc44f\uc450\uc451\uc452\uc453\uc454\uc455\uc456\uc457\uc458\uc459\uc45a\uc45b\uc45c\uc45d\uc45e\uc45f\uc460\uc461\uc462\uc463\uc464\uc465\uc466\uc467\uc468\uc469\uc46a\uc46b\uc46c\uc46d\uc46e\uc46f\uc470\uc471\uc472\uc473\uc474\uc475\uc476\uc477\uc478\uc479\uc47a\uc47b\uc47c\uc47d\uc47e\uc47f\uc480\uc481\uc482\uc483\uc484\uc485\uc486\uc487\uc488\uc489\uc48a\uc48b\uc48c\uc48d\uc48e\uc48f\uc490\uc491\uc492\uc493\uc494\uc495\uc496\uc497\uc498\uc499\uc49a\uc49b\uc49c\uc49d\uc49e\uc49f\uc4a0\uc4a1\uc4a2\uc4a3\uc4a4\uc4a5\uc4a6\uc4a7\uc4a8\uc4a9\uc4aa\uc4ab\uc4ac\uc4ad\uc4ae\uc4af\uc4b0\uc4b1\uc4b2\uc4b3\uc4b4\uc4b5\uc4b6\uc4b7\uc4b8\uc4b9\uc4ba\uc4bb\uc4bc\uc4bd\uc4be\uc4bf\uc4c0\uc4c1\uc4c2\uc4c3\uc4c4\uc4c5\uc4c6\uc4c7\uc4c8\uc4c9\uc4ca\uc4cb\uc4cc\uc4cd\uc4ce\uc4cf\uc4d0\uc4d1\uc4d2\uc4d3\uc4d4\uc4d5\uc4d6\uc4d7\uc4d8\uc4d9\uc4da\uc4db\uc4dc\uc4dd\uc4de\uc4df\uc4e0\uc4e1\uc4e2\uc4e3\uc4e4\uc4e5\uc4e6\uc4e7\uc4e8\uc4e9\uc4ea\uc4eb\uc4ec\uc4ed\uc4ee\uc4ef\uc4f0\uc4f1\uc4f2\uc4f3\uc4f4\uc4f5\uc4f6\uc4f7\uc4f8\uc4f9\uc4fa\uc4fb\uc4fc\uc4fd\uc4fe\uc4ff\uc500\uc501\uc502\uc503\uc504\uc505\uc506\uc507\uc508\uc509\uc50a\uc50b\uc50c\uc50d\uc50e\uc50f\uc510\uc511\uc512\uc513\uc514\uc515\uc516\uc517\uc518\uc519\uc51a\uc51b\uc51c\uc51d\uc51e\uc51f\uc520\uc521\uc522\uc523\uc524\uc525\uc526\uc527\uc528\uc529\uc52a\uc52b\uc52c\uc52d\uc52e\uc52f\uc530\uc531\uc532\uc533\uc534\uc535\uc536\uc537\uc538\uc539\uc53a\uc53b\uc53c\uc53d\uc53e\uc53f\uc540\uc541\uc542\uc543\uc544\uc545\uc546\uc547\uc548\uc549\uc54a\uc54b\uc54c\uc54d\uc54e\uc54f\uc550\uc551\uc552\uc553\uc554\uc555\uc556\uc557\uc558\uc559\uc55a\uc55b\uc55c\uc55d\uc55e\uc55f\uc560\uc561\uc562\uc563\uc564\uc565\uc566\uc567\uc568\uc569\uc56a\uc56b\uc56c\uc56d\uc56e\uc56f\uc570\uc571\uc572\uc573\uc574\uc575\uc576\uc577\uc578\uc579\uc57a\uc57b\uc57c\uc57d\uc57e\uc57f\uc580\uc581\uc582\uc583\uc584\uc585\uc586\uc587\uc588\uc589\uc58a\uc58b\uc58c\uc58d\uc58e\uc58f\uc590\uc591\uc592\uc593\uc594\uc595\uc596\uc597\uc598\uc599\uc59a\uc59b\uc59c\uc59d\uc59e\uc59f\uc5a0\uc5a1\uc5a2\uc5a3\uc5a4\uc5a5\uc5a6\uc5a7\uc5a8\uc5a9\uc5aa\uc5ab\uc5ac\uc5ad\uc5ae\uc5af\uc5b0\uc5b1\uc5b2\uc5b3\uc5b4\uc5b5\uc5b6\uc5b7\uc5b8\uc5b9\uc5ba\uc5bb\uc5bc\uc5bd\uc5be\uc5bf\uc5c0\uc5c1\uc5c2\uc5c3\uc5c4\uc5c5\uc5c6\uc5c7\uc5c8\uc5c9\uc5ca\uc5cb\uc5cc\uc5cd\uc5ce\uc5cf\uc5d0\uc5d1\uc5d2\uc5d3\uc5d4\uc5d5\uc5d6\uc5d7\uc5d8\uc5d9\uc5da\uc5db\uc5dc\uc5dd\uc5de\uc5df\uc5e0\uc5e1\uc5e2\uc5e3\uc5e4\uc5e5\uc5e6\uc5e7\uc5e8\uc5e9\uc5ea\uc5eb\uc5ec\uc5ed\uc5ee\uc5ef\uc5f0\uc5f1\uc5f2\uc5f3\uc5f4\uc5f5\uc5f6\uc5f7\uc5f8\uc5f9\uc5fa\uc5fb\uc5fc\uc5fd\uc5fe\uc5ff\uc600\uc601\uc602\uc603\uc604\uc605\uc606\uc607\uc608\uc609\uc60a\uc60b\uc60c\uc60d\uc60e\uc60f\uc610\uc611\uc612\uc613\uc614\uc615\uc616\uc617\uc618\uc619\uc61a\uc61b\uc61c\uc61d\uc61e\uc61f\uc620\uc621\uc622\uc623\uc624\uc625\uc626\uc627\uc628\uc629\uc62a\uc62b\uc62c\uc62d\uc62e\uc62f\uc630\uc631\uc632\uc633\uc634\uc635\uc636\uc637\uc638\uc639\uc63a\uc63b\uc63c\uc63d\uc63e\uc63f\uc640\uc641\uc642\uc643\uc644\uc645\uc646\uc647\uc648\uc649\uc64a\uc64b\uc64c\uc64d\uc64e\uc64f\uc650\uc651\uc652\uc653\uc654\uc655\uc656\uc657\uc658\uc659\uc65a\uc65b\uc65c\uc65d\uc65e\uc65f\uc660\uc661\uc662\uc663\uc664\uc665\uc666\uc667\uc668\uc669\uc66a\uc66b\uc66c\uc66d\uc66e\uc66f\uc670\uc671\uc672\uc673\uc674\uc675\uc676\uc677\uc678\uc679\uc67a\uc67b\uc67c\uc67d\uc67e\uc67f\uc680\uc681\uc682\uc683\uc684\uc685\uc686\uc687\uc688\uc689\uc68a\uc68b\uc68c\uc68d\uc68e\uc68f\uc690\uc691\uc692\uc693\uc694\uc695\uc696\uc697\uc698\uc699\uc69a\uc69b\uc69c\uc69d\uc69e\uc69f\uc6a0\uc6a1\uc6a2\uc6a3\uc6a4\uc6a5\uc6a6\uc6a7\uc6a8\uc6a9\uc6aa\uc6ab\uc6ac\uc6ad\uc6ae\uc6af\uc6b0\uc6b1\uc6b2\uc6b3\uc6b4\uc6b5\uc6b6\uc6b7\uc6b8\uc6b9\uc6ba\uc6bb\uc6bc\uc6bd\uc6be\uc6bf\uc6c0\uc6c1\uc6c2\uc6c3\uc6c4\uc6c5\uc6c6\uc6c7\uc6c8\uc6c9\uc6ca\uc6cb\uc6cc\uc6cd\uc6ce\uc6cf\uc6d0\uc6d1\uc6d2\uc6d3\uc6d4\uc6d5\uc6d6\uc6d7\uc6d8\uc6d9\uc6da\uc6db\uc6dc\uc6dd\uc6de\uc6df\uc6e0\uc6e1\uc6e2\uc6e3\uc6e4\uc6e5\uc6e6\uc6e7\uc6e8\uc6e9\uc6ea\uc6eb\uc6ec\uc6ed\uc6ee\uc6ef\uc6f0\uc6f1\uc6f2\uc6f3\uc6f4\uc6f5\uc6f6\uc6f7\uc6f8\uc6f9\uc6fa\uc6fb\uc6fc\uc6fd\uc6fe\uc6ff\uc700\uc701\uc702\uc703\uc704\uc705\uc706\uc707\uc708\uc709\uc70a\uc70b\uc70c\uc70d\uc70e\uc70f\uc710\uc711\uc712\uc713\uc714\uc715\uc716\uc717\uc718\uc719\uc71a\uc71b\uc71c\uc71d\uc71e\uc71f\uc720\uc721\uc722\uc723\uc724\uc725\uc726\uc727\uc728\uc729\uc72a\uc72b\uc72c\uc72d\uc72e\uc72f\uc730\uc731\uc732\uc733\uc734\uc735\uc736\uc737\uc738\uc739\uc73a\uc73b\uc73c\uc73d\uc73e\uc73f\uc740\uc741\uc742\uc743\uc744\uc745\uc746\uc747\uc748\uc749\uc74a\uc74b\uc74c\uc74d\uc74e\uc74f\uc750\uc751\uc752\uc753\uc754\uc755\uc756\uc757\uc758\uc759\uc75a\uc75b\uc75c\uc75d\uc75e\uc75f\uc760\uc761\uc762\uc763\uc764\uc765\uc766\uc767\uc768\uc769\uc76a\uc76b\uc76c\uc76d\uc76e\uc76f\uc770\uc771\uc772\uc773\uc774\uc775\uc776\uc777\uc778\uc779\uc77a\uc77b\uc77c\uc77d\uc77e\uc77f\uc780\uc781\uc782\uc783\uc784\uc785\uc786\uc787\uc788\uc789\uc78a\uc78b\uc78c\uc78d\uc78e\uc78f\uc790\uc791\uc792\uc793\uc794\uc795\uc796\uc797\uc798\uc799\uc79a\uc79b\uc79c\uc79d\uc79e\uc79f\uc7a0\uc7a1\uc7a2\uc7a3\uc7a4\uc7a5\uc7a6\uc7a7\uc7a8\uc7a9\uc7aa\uc7ab\uc7ac\uc7ad\uc7ae\uc7af\uc7b0\uc7b1\uc7b2\uc7b3\uc7b4\uc7b5\uc7b6\uc7b7\uc7b8\uc7b9\uc7ba\uc7bb\uc7bc\uc7bd\uc7be\uc7bf\uc7c0\uc7c1\uc7c2\uc7c3\uc7c4\uc7c5\uc7c6\uc7c7\uc7c8\uc7c9\uc7ca\uc7cb\uc7cc\uc7cd\uc7ce\uc7cf\uc7d0\uc7d1\uc7d2\uc7d3\uc7d4\uc7d5\uc7d6\uc7d7\uc7d8\uc7d9\uc7da\uc7db\uc7dc\uc7dd\uc7de\uc7df\uc7e0\uc7e1\uc7e2\uc7e3\uc7e4\uc7e5\uc7e6\uc7e7\uc7e8\uc7e9\uc7ea\uc7eb\uc7ec\uc7ed\uc7ee\uc7ef\uc7f0\uc7f1\uc7f2\uc7f3\uc7f4\uc7f5\uc7f6\uc7f7\uc7f8\uc7f9\uc7fa\uc7fb\uc7fc\uc7fd\uc7fe\uc7ff\uc800\uc801\uc802\uc803\uc804\uc805\uc806\uc807\uc808\uc809\uc80a\uc80b\uc80c\uc80d\uc80e\uc80f\uc810\uc811\uc812\uc813\uc814\uc815\uc816\uc817\uc818\uc819\uc81a\uc81b\uc81c\uc81d\uc81e\uc81f\uc820\uc821\uc822\uc823\uc824\uc825\uc826\uc827\uc828\uc829\uc82a\uc82b\uc82c\uc82d\uc82e\uc82f\uc830\uc831\uc832\uc833\uc834\uc835\uc836\uc837\uc838\uc839\uc83a\uc83b\uc83c\uc83d\uc83e\uc83f\uc840\uc841\uc842\uc843\uc844\uc845\uc846\uc847\uc848\uc849\uc84a\uc84b\uc84c\uc84d\uc84e\uc84f\uc850\uc851\uc852\uc853\uc854\uc855\uc856\uc857\uc858\uc859\uc85a\uc85b\uc85c\uc85d\uc85e\uc85f\uc860\uc861\uc862\uc863\uc864\uc865\uc866\uc867\uc868\uc869\uc86a\uc86b\uc86c\uc86d\uc86e\uc86f\uc870\uc871\uc872\uc873\uc874\uc875\uc876\uc877\uc878\uc879\uc87a\uc87b\uc87c\uc87d\uc87e\uc87f\uc880\uc881\uc882\uc883\uc884\uc885\uc886\uc887\uc888\uc889\uc88a\uc88b\uc88c\uc88d\uc88e\uc88f\uc890\uc891\uc892\uc893\uc894\uc895\uc896\uc897\uc898\uc899\uc89a\uc89b\uc89c\uc89d\uc89e\uc89f\uc8a0\uc8a1\uc8a2\uc8a3\uc8a4\uc8a5\uc8a6\uc8a7\uc8a8\uc8a9\uc8aa\uc8ab\uc8ac\uc8ad\uc8ae\uc8af\uc8b0\uc8b1\uc8b2\uc8b3\uc8b4\uc8b5\uc8b6\uc8b7\uc8b8\uc8b9\uc8ba\uc8bb\uc8bc\uc8bd\uc8be\uc8bf\uc8c0\uc8c1\uc8c2\uc8c3\uc8c4\uc8c5\uc8c6\uc8c7\uc8c8\uc8c9\uc8ca\uc8cb\uc8cc\uc8cd\uc8ce\uc8cf\uc8d0\uc8d1\uc8d2\uc8d3\uc8d4\uc8d5\uc8d6\uc8d7\uc8d8\uc8d9\uc8da\uc8db\uc8dc\uc8dd\uc8de\uc8df\uc8e0\uc8e1\uc8e2\uc8e3\uc8e4\uc8e5\uc8e6\uc8e7\uc8e8\uc8e9\uc8ea\uc8eb\uc8ec\uc8ed\uc8ee\uc8ef\uc8f0\uc8f1\uc8f2\uc8f3\uc8f4\uc8f5\uc8f6\uc8f7\uc8f8\uc8f9\uc8fa\uc8fb\uc8fc\uc8fd\uc8fe\uc8ff\uc900\uc901\uc902\uc903\uc904\uc905\uc906\uc907\uc908\uc909\uc90a\uc90b\uc90c\uc90d\uc90e\uc90f\uc910\uc911\uc912\uc913\uc914\uc915\uc916\uc917\uc918\uc919\uc91a\uc91b\uc91c\uc91d\uc91e\uc91f\uc920\uc921\uc922\uc923\uc924\uc925\uc926\uc927\uc928\uc929\uc92a\uc92b\uc92c\uc92d\uc92e\uc92f\uc930\uc931\uc932\uc933\uc934\uc935\uc936\uc937\uc938\uc939\uc93a\uc93b\uc93c\uc93d\uc93e\uc93f\uc940\uc941\uc942\uc943\uc944\uc945\uc946\uc947\uc948\uc949\uc94a\uc94b\uc94c\uc94d\uc94e\uc94f\uc950\uc951\uc952\uc953\uc954\uc955\uc956\uc957\uc958\uc959\uc95a\uc95b\uc95c\uc95d\uc95e\uc95f\uc960\uc961\uc962\uc963\uc964\uc965\uc966\uc967\uc968\uc969\uc96a\uc96b\uc96c\uc96d\uc96e\uc96f\uc970\uc971\uc972\uc973\uc974\uc975\uc976\uc977\uc978\uc979\uc97a\uc97b\uc97c\uc97d\uc97e\uc97f\uc980\uc981\uc982\uc983\uc984\uc985\uc986\uc987\uc988\uc989\uc98a\uc98b\uc98c\uc98d\uc98e\uc98f\uc990\uc991\uc992\uc993\uc994\uc995\uc996\uc997\uc998\uc999\uc99a\uc99b\uc99c\uc99d\uc99e\uc99f\uc9a0\uc9a1\uc9a2\uc9a3\uc9a4\uc9a5\uc9a6\uc9a7\uc9a8\uc9a9\uc9aa\uc9ab\uc9ac\uc9ad\uc9ae\uc9af\uc9b0\uc9b1\uc9b2\uc9b3\uc9b4\uc9b5\uc9b6\uc9b7\uc9b8\uc9b9\uc9ba\uc9bb\uc9bc\uc9bd\uc9be\uc9bf\uc9c0\uc9c1\uc9c2\uc9c3\uc9c4\uc9c5\uc9c6\uc9c7\uc9c8\uc9c9\uc9ca\uc9cb\uc9cc\uc9cd\uc9ce\uc9cf\uc9d0\uc9d1\uc9d2\uc9d3\uc9d4\uc9d5\uc9d6\uc9d7\uc9d8\uc9d9\uc9da\uc9db\uc9dc\uc9dd\uc9de\uc9df\uc9e0\uc9e1\uc9e2\uc9e3\uc9e4\uc9e5\uc9e6\uc9e7\uc9e8\uc9e9\uc9ea\uc9eb\uc9ec\uc9ed\uc9ee\uc9ef\uc9f0\uc9f1\uc9f2\uc9f3\uc9f4\uc9f5\uc9f6\uc9f7\uc9f8\uc9f9\uc9fa\uc9fb\uc9fc\uc9fd\uc9fe\uc9ff\uca00\uca01\uca02\uca03\uca04\uca05\uca06\uca07\uca08\uca09\uca0a\uca0b\uca0c\uca0d\uca0e\uca0f\uca10\uca11\uca12\uca13\uca14\uca15\uca16\uca17\uca18\uca19\uca1a\uca1b\uca1c\uca1d\uca1e\uca1f\uca20\uca21\uca22\uca23\uca24\uca25\uca26\uca27\uca28\uca29\uca2a\uca2b\uca2c\uca2d\uca2e\uca2f\uca30\uca31\uca32\uca33\uca34\uca35\uca36\uca37\uca38\uca39\uca3a\uca3b\uca3c\uca3d\uca3e\uca3f\uca40\uca41\uca42\uca43\uca44\uca45\uca46\uca47\uca48\uca49\uca4a\uca4b\uca4c\uca4d\uca4e\uca4f\uca50\uca51\uca52\uca53\uca54\uca55\uca56\uca57\uca58\uca59\uca5a\uca5b\uca5c\uca5d\uca5e\uca5f\uca60\uca61\uca62\uca63\uca64\uca65\uca66\uca67\uca68\uca69\uca6a\uca6b\uca6c\uca6d\uca6e\uca6f\uca70\uca71\uca72\uca73\uca74\uca75\uca76\uca77\uca78\uca79\uca7a\uca7b\uca7c\uca7d\uca7e\uca7f\uca80\uca81\uca82\uca83\uca84\uca85\uca86\uca87\uca88\uca89\uca8a\uca8b\uca8c\uca8d\uca8e\uca8f\uca90\uca91\uca92\uca93\uca94\uca95\uca96\uca97\uca98\uca99\uca9a\uca9b\uca9c\uca9d\uca9e\uca9f\ucaa0\ucaa1\ucaa2\ucaa3\ucaa4\ucaa5\ucaa6\ucaa7\ucaa8\ucaa9\ucaaa\ucaab\ucaac\ucaad\ucaae\ucaaf\ucab0\ucab1\ucab2\ucab3\ucab4\ucab5\ucab6\ucab7\ucab8\ucab9\ucaba\ucabb\ucabc\ucabd\ucabe\ucabf\ucac0\ucac1\ucac2\ucac3\ucac4\ucac5\ucac6\ucac7\ucac8\ucac9\ucaca\ucacb\ucacc\ucacd\ucace\ucacf\ucad0\ucad1\ucad2\ucad3\ucad4\ucad5\ucad6\ucad7\ucad8\ucad9\ucada\ucadb\ucadc\ucadd\ucade\ucadf\ucae0\ucae1\ucae2\ucae3\ucae4\ucae5\ucae6\ucae7\ucae8\ucae9\ucaea\ucaeb\ucaec\ucaed\ucaee\ucaef\ucaf0\ucaf1\ucaf2\ucaf3\ucaf4\ucaf5\ucaf6\ucaf7\ucaf8\ucaf9\ucafa\ucafb\ucafc\ucafd\ucafe\ucaff\ucb00\ucb01\ucb02\ucb03\ucb04\ucb05\ucb06\ucb07\ucb08\ucb09\ucb0a\ucb0b\ucb0c\ucb0d\ucb0e\ucb0f\ucb10\ucb11\ucb12\ucb13\ucb14\ucb15\ucb16\ucb17\ucb18\ucb19\ucb1a\ucb1b\ucb1c\ucb1d\ucb1e\ucb1f\ucb20\ucb21\ucb22\ucb23\ucb24\ucb25\ucb26\ucb27\ucb28\ucb29\ucb2a\ucb2b\ucb2c\ucb2d\ucb2e\ucb2f\ucb30\ucb31\ucb32\ucb33\ucb34\ucb35\ucb36\ucb37\ucb38\ucb39\ucb3a\ucb3b\ucb3c\ucb3d\ucb3e\ucb3f\ucb40\ucb41\ucb42\ucb43\ucb44\ucb45\ucb46\ucb47\ucb48\ucb49\ucb4a\ucb4b\ucb4c\ucb4d\ucb4e\ucb4f\ucb50\ucb51\ucb52\ucb53\ucb54\ucb55\ucb56\ucb57\ucb58\ucb59\ucb5a\ucb5b\ucb5c\ucb5d\ucb5e\ucb5f\ucb60\ucb61\ucb62\ucb63\ucb64\ucb65\ucb66\ucb67\ucb68\ucb69\ucb6a\ucb6b\ucb6c\ucb6d\ucb6e\ucb6f\ucb70\ucb71\ucb72\ucb73\ucb74\ucb75\ucb76\ucb77\ucb78\ucb79\ucb7a\ucb7b\ucb7c\ucb7d\ucb7e\ucb7f\ucb80\ucb81\ucb82\ucb83\ucb84\ucb85\ucb86\ucb87\ucb88\ucb89\ucb8a\ucb8b\ucb8c\ucb8d\ucb8e\ucb8f\ucb90\ucb91\ucb92\ucb93\ucb94\ucb95\ucb96\ucb97\ucb98\ucb99\ucb9a\ucb9b\ucb9c\ucb9d\ucb9e\ucb9f\ucba0\ucba1\ucba2\ucba3\ucba4\ucba5\ucba6\ucba7\ucba8\ucba9\ucbaa\ucbab\ucbac\ucbad\ucbae\ucbaf\ucbb0\ucbb1\ucbb2\ucbb3\ucbb4\ucbb5\ucbb6\ucbb7\ucbb8\ucbb9\ucbba\ucbbb\ucbbc\ucbbd\ucbbe\ucbbf\ucbc0\ucbc1\ucbc2\ucbc3\ucbc4\ucbc5\ucbc6\ucbc7\ucbc8\ucbc9\ucbca\ucbcb\ucbcc\ucbcd\ucbce\ucbcf\ucbd0\ucbd1\ucbd2\ucbd3\ucbd4\ucbd5\ucbd6\ucbd7\ucbd8\ucbd9\ucbda\ucbdb\ucbdc\ucbdd\ucbde\ucbdf\ucbe0\ucbe1\ucbe2\ucbe3\ucbe4\ucbe5\ucbe6\ucbe7\ucbe8\ucbe9\ucbea\ucbeb\ucbec\ucbed\ucbee\ucbef\ucbf0\ucbf1\ucbf2\ucbf3\ucbf4\ucbf5\ucbf6\ucbf7\ucbf8\ucbf9\ucbfa\ucbfb\ucbfc\ucbfd\ucbfe\ucbff\ucc00\ucc01\ucc02\ucc03\ucc04\ucc05\ucc06\ucc07\ucc08\ucc09\ucc0a\ucc0b\ucc0c\ucc0d\ucc0e\ucc0f\ucc10\ucc11\ucc12\ucc13\ucc14\ucc15\ucc16\ucc17\ucc18\ucc19\ucc1a\ucc1b\ucc1c\ucc1d\ucc1e\ucc1f\ucc20\ucc21\ucc22\ucc23\ucc24\ucc25\ucc26\ucc27\ucc28\ucc29\ucc2a\ucc2b\ucc2c\ucc2d\ucc2e\ucc2f\ucc30\ucc31\ucc32\ucc33\ucc34\ucc35\ucc36\ucc37\ucc38\ucc39\ucc3a\ucc3b\ucc3c\ucc3d\ucc3e\ucc3f\ucc40\ucc41\ucc42\ucc43\ucc44\ucc45\ucc46\ucc47\ucc48\ucc49\ucc4a\ucc4b\ucc4c\ucc4d\ucc4e\ucc4f\ucc50\ucc51\ucc52\ucc53\ucc54\ucc55\ucc56\ucc57\ucc58\ucc59\ucc5a\ucc5b\ucc5c\ucc5d\ucc5e\ucc5f\ucc60\ucc61\ucc62\ucc63\ucc64\ucc65\ucc66\ucc67\ucc68\ucc69\ucc6a\ucc6b\ucc6c\ucc6d\ucc6e\ucc6f\ucc70\ucc71\ucc72\ucc73\ucc74\ucc75\ucc76\ucc77\ucc78\ucc79\ucc7a\ucc7b\ucc7c\ucc7d\ucc7e\ucc7f\ucc80\ucc81\ucc82\ucc83\ucc84\ucc85\ucc86\ucc87\ucc88\ucc89\ucc8a\ucc8b\ucc8c\ucc8d\ucc8e\ucc8f\ucc90\ucc91\ucc92\ucc93\ucc94\ucc95\ucc96\ucc97\ucc98\ucc99\ucc9a\ucc9b\ucc9c\ucc9d\ucc9e\ucc9f\ucca0\ucca1\ucca2\ucca3\ucca4\ucca5\ucca6\ucca7\ucca8\ucca9\uccaa\uccab\uccac\uccad\uccae\uccaf\uccb0\uccb1\uccb2\uccb3\uccb4\uccb5\uccb6\uccb7\uccb8\uccb9\uccba\uccbb\uccbc\uccbd\uccbe\uccbf\uccc0\uccc1\uccc2\uccc3\uccc4\uccc5\uccc6\uccc7\uccc8\uccc9\uccca\ucccb\ucccc\ucccd\uccce\ucccf\uccd0\uccd1\uccd2\uccd3\uccd4\uccd5\uccd6\uccd7\uccd8\uccd9\uccda\uccdb\uccdc\uccdd\uccde\uccdf\ucce0\ucce1\ucce2\ucce3\ucce4\ucce5\ucce6\ucce7\ucce8\ucce9\uccea\ucceb\uccec\ucced\uccee\uccef\uccf0\uccf1\uccf2\uccf3\uccf4\uccf5\uccf6\uccf7\uccf8\uccf9\uccfa\uccfb\uccfc\uccfd\uccfe\uccff\ucd00\ucd01\ucd02\ucd03\ucd04\ucd05\ucd06\ucd07\ucd08\ucd09\ucd0a\ucd0b\ucd0c\ucd0d\ucd0e\ucd0f\ucd10\ucd11\ucd12\ucd13\ucd14\ucd15\ucd16\ucd17\ucd18\ucd19\ucd1a\ucd1b\ucd1c\ucd1d\ucd1e\ucd1f\ucd20\ucd21\ucd22\ucd23\ucd24\ucd25\ucd26\ucd27\ucd28\ucd29\ucd2a\ucd2b\ucd2c\ucd2d\ucd2e\ucd2f\ucd30\ucd31\ucd32\ucd33\ucd34\ucd35\ucd36\ucd37\ucd38\ucd39\ucd3a\ucd3b\ucd3c\ucd3d\ucd3e\ucd3f\ucd40\ucd41\ucd42\ucd43\ucd44\ucd45\ucd46\ucd47\ucd48\ucd49\ucd4a\ucd4b\ucd4c\ucd4d\ucd4e\ucd4f\ucd50\ucd51\ucd52\ucd53\ucd54\ucd55\ucd56\ucd57\ucd58\ucd59\ucd5a\ucd5b\ucd5c\ucd5d\ucd5e\ucd5f\ucd60\ucd61\ucd62\ucd63\ucd64\ucd65\ucd66\ucd67\ucd68\ucd69\ucd6a\ucd6b\ucd6c\ucd6d\ucd6e\ucd6f\ucd70\ucd71\ucd72\ucd73\ucd74\ucd75\ucd76\ucd77\ucd78\ucd79\ucd7a\ucd7b\ucd7c\ucd7d\ucd7e\ucd7f\ucd80\ucd81\ucd82\ucd83\ucd84\ucd85\ucd86\ucd87\ucd88\ucd89\ucd8a\ucd8b\ucd8c\ucd8d\ucd8e\ucd8f\ucd90\ucd91\ucd92\ucd93\ucd94\ucd95\ucd96\ucd97\ucd98\ucd99\ucd9a\ucd9b\ucd9c\ucd9d\ucd9e\ucd9f\ucda0\ucda1\ucda2\ucda3\ucda4\ucda5\ucda6\ucda7\ucda8\ucda9\ucdaa\ucdab\ucdac\ucdad\ucdae\ucdaf\ucdb0\ucdb1\ucdb2\ucdb3\ucdb4\ucdb5\ucdb6\ucdb7\ucdb8\ucdb9\ucdba\ucdbb\ucdbc\ucdbd\ucdbe\ucdbf\ucdc0\ucdc1\ucdc2\ucdc3\ucdc4\ucdc5\ucdc6\ucdc7\ucdc8\ucdc9\ucdca\ucdcb\ucdcc\ucdcd\ucdce\ucdcf\ucdd0\ucdd1\ucdd2\ucdd3\ucdd4\ucdd5\ucdd6\ucdd7\ucdd8\ucdd9\ucdda\ucddb\ucddc\ucddd\ucdde\ucddf\ucde0\ucde1\ucde2\ucde3\ucde4\ucde5\ucde6\ucde7\ucde8\ucde9\ucdea\ucdeb\ucdec\ucded\ucdee\ucdef\ucdf0\ucdf1\ucdf2\ucdf3\ucdf4\ucdf5\ucdf6\ucdf7\ucdf8\ucdf9\ucdfa\ucdfb\ucdfc\ucdfd\ucdfe\ucdff\uce00\uce01\uce02\uce03\uce04\uce05\uce06\uce07\uce08\uce09\uce0a\uce0b\uce0c\uce0d\uce0e\uce0f\uce10\uce11\uce12\uce13\uce14\uce15\uce16\uce17\uce18\uce19\uce1a\uce1b\uce1c\uce1d\uce1e\uce1f\uce20\uce21\uce22\uce23\uce24\uce25\uce26\uce27\uce28\uce29\uce2a\uce2b\uce2c\uce2d\uce2e\uce2f\uce30\uce31\uce32\uce33\uce34\uce35\uce36\uce37\uce38\uce39\uce3a\uce3b\uce3c\uce3d\uce3e\uce3f\uce40\uce41\uce42\uce43\uce44\uce45\uce46\uce47\uce48\uce49\uce4a\uce4b\uce4c\uce4d\uce4e\uce4f\uce50\uce51\uce52\uce53\uce54\uce55\uce56\uce57\uce58\uce59\uce5a\uce5b\uce5c\uce5d\uce5e\uce5f\uce60\uce61\uce62\uce63\uce64\uce65\uce66\uce67\uce68\uce69\uce6a\uce6b\uce6c\uce6d\uce6e\uce6f\uce70\uce71\uce72\uce73\uce74\uce75\uce76\uce77\uce78\uce79\uce7a\uce7b\uce7c\uce7d\uce7e\uce7f\uce80\uce81\uce82\uce83\uce84\uce85\uce86\uce87\uce88\uce89\uce8a\uce8b\uce8c\uce8d\uce8e\uce8f\uce90\uce91\uce92\uce93\uce94\uce95\uce96\uce97\uce98\uce99\uce9a\uce9b\uce9c\uce9d\uce9e\uce9f\ucea0\ucea1\ucea2\ucea3\ucea4\ucea5\ucea6\ucea7\ucea8\ucea9\uceaa\uceab\uceac\ucead\uceae\uceaf\uceb0\uceb1\uceb2\uceb3\uceb4\uceb5\uceb6\uceb7\uceb8\uceb9\uceba\ucebb\ucebc\ucebd\ucebe\ucebf\ucec0\ucec1\ucec2\ucec3\ucec4\ucec5\ucec6\ucec7\ucec8\ucec9\uceca\ucecb\ucecc\ucecd\ucece\ucecf\uced0\uced1\uced2\uced3\uced4\uced5\uced6\uced7\uced8\uced9\uceda\ucedb\ucedc\ucedd\ucede\ucedf\ucee0\ucee1\ucee2\ucee3\ucee4\ucee5\ucee6\ucee7\ucee8\ucee9\uceea\uceeb\uceec\uceed\uceee\uceef\ucef0\ucef1\ucef2\ucef3\ucef4\ucef5\ucef6\ucef7\ucef8\ucef9\ucefa\ucefb\ucefc\ucefd\ucefe\uceff\ucf00\ucf01\ucf02\ucf03\ucf04\ucf05\ucf06\ucf07\ucf08\ucf09\ucf0a\ucf0b\ucf0c\ucf0d\ucf0e\ucf0f\ucf10\ucf11\ucf12\ucf13\ucf14\ucf15\ucf16\ucf17\ucf18\ucf19\ucf1a\ucf1b\ucf1c\ucf1d\ucf1e\ucf1f\ucf20\ucf21\ucf22\ucf23\ucf24\ucf25\ucf26\ucf27\ucf28\ucf29\ucf2a\ucf2b\ucf2c\ucf2d\ucf2e\ucf2f\ucf30\ucf31\ucf32\ucf33\ucf34\ucf35\ucf36\ucf37\ucf38\ucf39\ucf3a\ucf3b\ucf3c\ucf3d\ucf3e\ucf3f\ucf40\ucf41\ucf42\ucf43\ucf44\ucf45\ucf46\ucf47\ucf48\ucf49\ucf4a\ucf4b\ucf4c\ucf4d\ucf4e\ucf4f\ucf50\ucf51\ucf52\ucf53\ucf54\ucf55\ucf56\ucf57\ucf58\ucf59\ucf5a\ucf5b\ucf5c\ucf5d\ucf5e\ucf5f\ucf60\ucf61\ucf62\ucf63\ucf64\ucf65\ucf66\ucf67\ucf68\ucf69\ucf6a\ucf6b\ucf6c\ucf6d\ucf6e\ucf6f\ucf70\ucf71\ucf72\ucf73\ucf74\ucf75\ucf76\ucf77\ucf78\ucf79\ucf7a\ucf7b\ucf7c\ucf7d\ucf7e\ucf7f\ucf80\ucf81\ucf82\ucf83\ucf84\ucf85\ucf86\ucf87\ucf88\ucf89\ucf8a\ucf8b\ucf8c\ucf8d\ucf8e\ucf8f\ucf90\ucf91\ucf92\ucf93\ucf94\ucf95\ucf96\ucf97\ucf98\ucf99\ucf9a\ucf9b\ucf9c\ucf9d\ucf9e\ucf9f\ucfa0\ucfa1\ucfa2\ucfa3\ucfa4\ucfa5\ucfa6\ucfa7\ucfa8\ucfa9\ucfaa\ucfab\ucfac\ucfad\ucfae\ucfaf\ucfb0\ucfb1\ucfb2\ucfb3\ucfb4\ucfb5\ucfb6\ucfb7\ucfb8\ucfb9\ucfba\ucfbb\ucfbc\ucfbd\ucfbe\ucfbf\ucfc0\ucfc1\ucfc2\ucfc3\ucfc4\ucfc5\ucfc6\ucfc7\ucfc8\ucfc9\ucfca\ucfcb\ucfcc\ucfcd\ucfce\ucfcf\ucfd0\ucfd1\ucfd2\ucfd3\ucfd4\ucfd5\ucfd6\ucfd7\ucfd8\ucfd9\ucfda\ucfdb\ucfdc\ucfdd\ucfde\ucfdf\ucfe0\ucfe1\ucfe2\ucfe3\ucfe4\ucfe5\ucfe6\ucfe7\ucfe8\ucfe9\ucfea\ucfeb\ucfec\ucfed\ucfee\ucfef\ucff0\ucff1\ucff2\ucff3\ucff4\ucff5\ucff6\ucff7\ucff8\ucff9\ucffa\ucffb\ucffc\ucffd\ucffe\ucfff\ud000\ud001\ud002\ud003\ud004\ud005\ud006\ud007\ud008\ud009\ud00a\ud00b\ud00c\ud00d\ud00e\ud00f\ud010\ud011\ud012\ud013\ud014\ud015\ud016\ud017\ud018\ud019\ud01a\ud01b\ud01c\ud01d\ud01e\ud01f\ud020\ud021\ud022\ud023\ud024\ud025\ud026\ud027\ud028\ud029\ud02a\ud02b\ud02c\ud02d\ud02e\ud02f\ud030\ud031\ud032\ud033\ud034\ud035\ud036\ud037\ud038\ud039\ud03a\ud03b\ud03c\ud03d\ud03e\ud03f\ud040\ud041\ud042\ud043\ud044\ud045\ud046\ud047\ud048\ud049\ud04a\ud04b\ud04c\ud04d\ud04e\ud04f\ud050\ud051\ud052\ud053\ud054\ud055\ud056\ud057\ud058\ud059\ud05a\ud05b\ud05c\ud05d\ud05e\ud05f\ud060\ud061\ud062\ud063\ud064\ud065\ud066\ud067\ud068\ud069\ud06a\ud06b\ud06c\ud06d\ud06e\ud06f\ud070\ud071\ud072\ud073\ud074\ud075\ud076\ud077\ud078\ud079\ud07a\ud07b\ud07c\ud07d\ud07e\ud07f\ud080\ud081\ud082\ud083\ud084\ud085\ud086\ud087\ud088\ud089\ud08a\ud08b\ud08c\ud08d\ud08e\ud08f\ud090\ud091\ud092\ud093\ud094\ud095\ud096\ud097\ud098\ud099\ud09a\ud09b\ud09c\ud09d\ud09e\ud09f\ud0a0\ud0a1\ud0a2\ud0a3\ud0a4\ud0a5\ud0a6\ud0a7\ud0a8\ud0a9\ud0aa\ud0ab\ud0ac\ud0ad\ud0ae\ud0af\ud0b0\ud0b1\ud0b2\ud0b3\ud0b4\ud0b5\ud0b6\ud0b7\ud0b8\ud0b9\ud0ba\ud0bb\ud0bc\ud0bd\ud0be\ud0bf\ud0c0\ud0c1\ud0c2\ud0c3\ud0c4\ud0c5\ud0c6\ud0c7\ud0c8\ud0c9\ud0ca\ud0cb\ud0cc\ud0cd\ud0ce\ud0cf\ud0d0\ud0d1\ud0d2\ud0d3\ud0d4\ud0d5\ud0d6\ud0d7\ud0d8\ud0d9\ud0da\ud0db\ud0dc\ud0dd\ud0de\ud0df\ud0e0\ud0e1\ud0e2\ud0e3\ud0e4\ud0e5\ud0e6\ud0e7\ud0e8\ud0e9\ud0ea\ud0eb\ud0ec\ud0ed\ud0ee\ud0ef\ud0f0\ud0f1\ud0f2\ud0f3\ud0f4\ud0f5\ud0f6\ud0f7\ud0f8\ud0f9\ud0fa\ud0fb\ud0fc\ud0fd\ud0fe\ud0ff\ud100\ud101\ud102\ud103\ud104\ud105\ud106\ud107\ud108\ud109\ud10a\ud10b\ud10c\ud10d\ud10e\ud10f\ud110\ud111\ud112\ud113\ud114\ud115\ud116\ud117\ud118\ud119\ud11a\ud11b\ud11c\ud11d\ud11e\ud11f\ud120\ud121\ud122\ud123\ud124\ud125\ud126\ud127\ud128\ud129\ud12a\ud12b\ud12c\ud12d\ud12e\ud12f\ud130\ud131\ud132\ud133\ud134\ud135\ud136\ud137\ud138\ud139\ud13a\ud13b\ud13c\ud13d\ud13e\ud13f\ud140\ud141\ud142\ud143\ud144\ud145\ud146\ud147\ud148\ud149\ud14a\ud14b\ud14c\ud14d\ud14e\ud14f\ud150\ud151\ud152\ud153\ud154\ud155\ud156\ud157\ud158\ud159\ud15a\ud15b\ud15c\ud15d\ud15e\ud15f\ud160\ud161\ud162\ud163\ud164\ud165\ud166\ud167\ud168\ud169\ud16a\ud16b\ud16c\ud16d\ud16e\ud16f\ud170\ud171\ud172\ud173\ud174\ud175\ud176\ud177\ud178\ud179\ud17a\ud17b\ud17c\ud17d\ud17e\ud17f\ud180\ud181\ud182\ud183\ud184\ud185\ud186\ud187\ud188\ud189\ud18a\ud18b\ud18c\ud18d\ud18e\ud18f\ud190\ud191\ud192\ud193\ud194\ud195\ud196\ud197\ud198\ud199\ud19a\ud19b\ud19c\ud19d\ud19e\ud19f\ud1a0\ud1a1\ud1a2\ud1a3\ud1a4\ud1a5\ud1a6\ud1a7\ud1a8\ud1a9\ud1aa\ud1ab\ud1ac\ud1ad\ud1ae\ud1af\ud1b0\ud1b1\ud1b2\ud1b3\ud1b4\ud1b5\ud1b6\ud1b7\ud1b8\ud1b9\ud1ba\ud1bb\ud1bc\ud1bd\ud1be\ud1bf\ud1c0\ud1c1\ud1c2\ud1c3\ud1c4\ud1c5\ud1c6\ud1c7\ud1c8\ud1c9\ud1ca\ud1cb\ud1cc\ud1cd\ud1ce\ud1cf\ud1d0\ud1d1\ud1d2\ud1d3\ud1d4\ud1d5\ud1d6\ud1d7\ud1d8\ud1d9\ud1da\ud1db\ud1dc\ud1dd\ud1de\ud1df\ud1e0\ud1e1\ud1e2\ud1e3\ud1e4\ud1e5\ud1e6\ud1e7\ud1e8\ud1e9\ud1ea\ud1eb\ud1ec\ud1ed\ud1ee\ud1ef\ud1f0\ud1f1\ud1f2\ud1f3\ud1f4\ud1f5\ud1f6\ud1f7\ud1f8\ud1f9\ud1fa\ud1fb\ud1fc\ud1fd\ud1fe\ud1ff\ud200\ud201\ud202\ud203\ud204\ud205\ud206\ud207\ud208\ud209\ud20a\ud20b\ud20c\ud20d\ud20e\ud20f\ud210\ud211\ud212\ud213\ud214\ud215\ud216\ud217\ud218\ud219\ud21a\ud21b\ud21c\ud21d\ud21e\ud21f\ud220\ud221\ud222\ud223\ud224\ud225\ud226\ud227\ud228\ud229\ud22a\ud22b\ud22c\ud22d\ud22e\ud22f\ud230\ud231\ud232\ud233\ud234\ud235\ud236\ud237\ud238\ud239\ud23a\ud23b\ud23c\ud23d\ud23e\ud23f\ud240\ud241\ud242\ud243\ud244\ud245\ud246\ud247\ud248\ud249\ud24a\ud24b\ud24c\ud24d\ud24e\ud24f\ud250\ud251\ud252\ud253\ud254\ud255\ud256\ud257\ud258\ud259\ud25a\ud25b\ud25c\ud25d\ud25e\ud25f\ud260\ud261\ud262\ud263\ud264\ud265\ud266\ud267\ud268\ud269\ud26a\ud26b\ud26c\ud26d\ud26e\ud26f\ud270\ud271\ud272\ud273\ud274\ud275\ud276\ud277\ud278\ud279\ud27a\ud27b\ud27c\ud27d\ud27e\ud27f\ud280\ud281\ud282\ud283\ud284\ud285\ud286\ud287\ud288\ud289\ud28a\ud28b\ud28c\ud28d\ud28e\ud28f\ud290\ud291\ud292\ud293\ud294\ud295\ud296\ud297\ud298\ud299\ud29a\ud29b\ud29c\ud29d\ud29e\ud29f\ud2a0\ud2a1\ud2a2\ud2a3\ud2a4\ud2a5\ud2a6\ud2a7\ud2a8\ud2a9\ud2aa\ud2ab\ud2ac\ud2ad\ud2ae\ud2af\ud2b0\ud2b1\ud2b2\ud2b3\ud2b4\ud2b5\ud2b6\ud2b7\ud2b8\ud2b9\ud2ba\ud2bb\ud2bc\ud2bd\ud2be\ud2bf\ud2c0\ud2c1\ud2c2\ud2c3\ud2c4\ud2c5\ud2c6\ud2c7\ud2c8\ud2c9\ud2ca\ud2cb\ud2cc\ud2cd\ud2ce\ud2cf\ud2d0\ud2d1\ud2d2\ud2d3\ud2d4\ud2d5\ud2d6\ud2d7\ud2d8\ud2d9\ud2da\ud2db\ud2dc\ud2dd\ud2de\ud2df\ud2e0\ud2e1\ud2e2\ud2e3\ud2e4\ud2e5\ud2e6\ud2e7\ud2e8\ud2e9\ud2ea\ud2eb\ud2ec\ud2ed\ud2ee\ud2ef\ud2f0\ud2f1\ud2f2\ud2f3\ud2f4\ud2f5\ud2f6\ud2f7\ud2f8\ud2f9\ud2fa\ud2fb\ud2fc\ud2fd\ud2fe\ud2ff\ud300\ud301\ud302\ud303\ud304\ud305\ud306\ud307\ud308\ud309\ud30a\ud30b\ud30c\ud30d\ud30e\ud30f\ud310\ud311\ud312\ud313\ud314\ud315\ud316\ud317\ud318\ud319\ud31a\ud31b\ud31c\ud31d\ud31e\ud31f\ud320\ud321\ud322\ud323\ud324\ud325\ud326\ud327\ud328\ud329\ud32a\ud32b\ud32c\ud32d\ud32e\ud32f\ud330\ud331\ud332\ud333\ud334\ud335\ud336\ud337\ud338\ud339\ud33a\ud33b\ud33c\ud33d\ud33e\ud33f\ud340\ud341\ud342\ud343\ud344\ud345\ud346\ud347\ud348\ud349\ud34a\ud34b\ud34c\ud34d\ud34e\ud34f\ud350\ud351\ud352\ud353\ud354\ud355\ud356\ud357\ud358\ud359\ud35a\ud35b\ud35c\ud35d\ud35e\ud35f\ud360\ud361\ud362\ud363\ud364\ud365\ud366\ud367\ud368\ud369\ud36a\ud36b\ud36c\ud36d\ud36e\ud36f\ud370\ud371\ud372\ud373\ud374\ud375\ud376\ud377\ud378\ud379\ud37a\ud37b\ud37c\ud37d\ud37e\ud37f\ud380\ud381\ud382\ud383\ud384\ud385\ud386\ud387\ud388\ud389\ud38a\ud38b\ud38c\ud38d\ud38e\ud38f\ud390\ud391\ud392\ud393\ud394\ud395\ud396\ud397\ud398\ud399\ud39a\ud39b\ud39c\ud39d\ud39e\ud39f\ud3a0\ud3a1\ud3a2\ud3a3\ud3a4\ud3a5\ud3a6\ud3a7\ud3a8\ud3a9\ud3aa\ud3ab\ud3ac\ud3ad\ud3ae\ud3af\ud3b0\ud3b1\ud3b2\ud3b3\ud3b4\ud3b5\ud3b6\ud3b7\ud3b8\ud3b9\ud3ba\ud3bb\ud3bc\ud3bd\ud3be\ud3bf\ud3c0\ud3c1\ud3c2\ud3c3\ud3c4\ud3c5\ud3c6\ud3c7\ud3c8\ud3c9\ud3ca\ud3cb\ud3cc\ud3cd\ud3ce\ud3cf\ud3d0\ud3d1\ud3d2\ud3d3\ud3d4\ud3d5\ud3d6\ud3d7\ud3d8\ud3d9\ud3da\ud3db\ud3dc\ud3dd\ud3de\ud3df\ud3e0\ud3e1\ud3e2\ud3e3\ud3e4\ud3e5\ud3e6\ud3e7\ud3e8\ud3e9\ud3ea\ud3eb\ud3ec\ud3ed\ud3ee\ud3ef\ud3f0\ud3f1\ud3f2\ud3f3\ud3f4\ud3f5\ud3f6\ud3f7\ud3f8\ud3f9\ud3fa\ud3fb\ud3fc\ud3fd\ud3fe\ud3ff\ud400\ud401\ud402\ud403\ud404\ud405\ud406\ud407\ud408\ud409\ud40a\ud40b\ud40c\ud40d\ud40e\ud40f\ud410\ud411\ud412\ud413\ud414\ud415\ud416\ud417\ud418\ud419\ud41a\ud41b\ud41c\ud41d\ud41e\ud41f\ud420\ud421\ud422\ud423\ud424\ud425\ud426\ud427\ud428\ud429\ud42a\ud42b\ud42c\ud42d\ud42e\ud42f\ud430\ud431\ud432\ud433\ud434\ud435\ud436\ud437\ud438\ud439\ud43a\ud43b\ud43c\ud43d\ud43e\ud43f\ud440\ud441\ud442\ud443\ud444\ud445\ud446\ud447\ud448\ud449\ud44a\ud44b\ud44c\ud44d\ud44e\ud44f\ud450\ud451\ud452\ud453\ud454\ud455\ud456\ud457\ud458\ud459\ud45a\ud45b\ud45c\ud45d\ud45e\ud45f\ud460\ud461\ud462\ud463\ud464\ud465\ud466\ud467\ud468\ud469\ud46a\ud46b\ud46c\ud46d\ud46e\ud46f\ud470\ud471\ud472\ud473\ud474\ud475\ud476\ud477\ud478\ud479\ud47a\ud47b\ud47c\ud47d\ud47e\ud47f\ud480\ud481\ud482\ud483\ud484\ud485\ud486\ud487\ud488\ud489\ud48a\ud48b\ud48c\ud48d\ud48e\ud48f\ud490\ud491\ud492\ud493\ud494\ud495\ud496\ud497\ud498\ud499\ud49a\ud49b\ud49c\ud49d\ud49e\ud49f\ud4a0\ud4a1\ud4a2\ud4a3\ud4a4\ud4a5\ud4a6\ud4a7\ud4a8\ud4a9\ud4aa\ud4ab\ud4ac\ud4ad\ud4ae\ud4af\ud4b0\ud4b1\ud4b2\ud4b3\ud4b4\ud4b5\ud4b6\ud4b7\ud4b8\ud4b9\ud4ba\ud4bb\ud4bc\ud4bd\ud4be\ud4bf\ud4c0\ud4c1\ud4c2\ud4c3\ud4c4\ud4c5\ud4c6\ud4c7\ud4c8\ud4c9\ud4ca\ud4cb\ud4cc\ud4cd\ud4ce\ud4cf\ud4d0\ud4d1\ud4d2\ud4d3\ud4d4\ud4d5\ud4d6\ud4d7\ud4d8\ud4d9\ud4da\ud4db\ud4dc\ud4dd\ud4de\ud4df\ud4e0\ud4e1\ud4e2\ud4e3\ud4e4\ud4e5\ud4e6\ud4e7\ud4e8\ud4e9\ud4ea\ud4eb\ud4ec\ud4ed\ud4ee\ud4ef\ud4f0\ud4f1\ud4f2\ud4f3\ud4f4\ud4f5\ud4f6\ud4f7\ud4f8\ud4f9\ud4fa\ud4fb\ud4fc\ud4fd\ud4fe\ud4ff\ud500\ud501\ud502\ud503\ud504\ud505\ud506\ud507\ud508\ud509\ud50a\ud50b\ud50c\ud50d\ud50e\ud50f\ud510\ud511\ud512\ud513\ud514\ud515\ud516\ud517\ud518\ud519\ud51a\ud51b\ud51c\ud51d\ud51e\ud51f\ud520\ud521\ud522\ud523\ud524\ud525\ud526\ud527\ud528\ud529\ud52a\ud52b\ud52c\ud52d\ud52e\ud52f\ud530\ud531\ud532\ud533\ud534\ud535\ud536\ud537\ud538\ud539\ud53a\ud53b\ud53c\ud53d\ud53e\ud53f\ud540\ud541\ud542\ud543\ud544\ud545\ud546\ud547\ud548\ud549\ud54a\ud54b\ud54c\ud54d\ud54e\ud54f\ud550\ud551\ud552\ud553\ud554\ud555\ud556\ud557\ud558\ud559\ud55a\ud55b\ud55c\ud55d\ud55e\ud55f\ud560\ud561\ud562\ud563\ud564\ud565\ud566\ud567\ud568\ud569\ud56a\ud56b\ud56c\ud56d\ud56e\ud56f\ud570\ud571\ud572\ud573\ud574\ud575\ud576\ud577\ud578\ud579\ud57a\ud57b\ud57c\ud57d\ud57e\ud57f\ud580\ud581\ud582\ud583\ud584\ud585\ud586\ud587\ud588\ud589\ud58a\ud58b\ud58c\ud58d\ud58e\ud58f\ud590\ud591\ud592\ud593\ud594\ud595\ud596\ud597\ud598\ud599\ud59a\ud59b\ud59c\ud59d\ud59e\ud59f\ud5a0\ud5a1\ud5a2\ud5a3\ud5a4\ud5a5\ud5a6\ud5a7\ud5a8\ud5a9\ud5aa\ud5ab\ud5ac\ud5ad\ud5ae\ud5af\ud5b0\ud5b1\ud5b2\ud5b3\ud5b4\ud5b5\ud5b6\ud5b7\ud5b8\ud5b9\ud5ba\ud5bb\ud5bc\ud5bd\ud5be\ud5bf\ud5c0\ud5c1\ud5c2\ud5c3\ud5c4\ud5c5\ud5c6\ud5c7\ud5c8\ud5c9\ud5ca\ud5cb\ud5cc\ud5cd\ud5ce\ud5cf\ud5d0\ud5d1\ud5d2\ud5d3\ud5d4\ud5d5\ud5d6\ud5d7\ud5d8\ud5d9\ud5da\ud5db\ud5dc\ud5dd\ud5de\ud5df\ud5e0\ud5e1\ud5e2\ud5e3\ud5e4\ud5e5\ud5e6\ud5e7\ud5e8\ud5e9\ud5ea\ud5eb\ud5ec\ud5ed\ud5ee\ud5ef\ud5f0\ud5f1\ud5f2\ud5f3\ud5f4\ud5f5\ud5f6\ud5f7\ud5f8\ud5f9\ud5fa\ud5fb\ud5fc\ud5fd\ud5fe\ud5ff\ud600\ud601\ud602\ud603\ud604\ud605\ud606\ud607\ud608\ud609\ud60a\ud60b\ud60c\ud60d\ud60e\ud60f\ud610\ud611\ud612\ud613\ud614\ud615\ud616\ud617\ud618\ud619\ud61a\ud61b\ud61c\ud61d\ud61e\ud61f\ud620\ud621\ud622\ud623\ud624\ud625\ud626\ud627\ud628\ud629\ud62a\ud62b\ud62c\ud62d\ud62e\ud62f\ud630\ud631\ud632\ud633\ud634\ud635\ud636\ud637\ud638\ud639\ud63a\ud63b\ud63c\ud63d\ud63e\ud63f\ud640\ud641\ud642\ud643\ud644\ud645\ud646\ud647\ud648\ud649\ud64a\ud64b\ud64c\ud64d\ud64e\ud64f\ud650\ud651\ud652\ud653\ud654\ud655\ud656\ud657\ud658\ud659\ud65a\ud65b\ud65c\ud65d\ud65e\ud65f\ud660\ud661\ud662\ud663\ud664\ud665\ud666\ud667\ud668\ud669\ud66a\ud66b\ud66c\ud66d\ud66e\ud66f\ud670\ud671\ud672\ud673\ud674\ud675\ud676\ud677\ud678\ud679\ud67a\ud67b\ud67c\ud67d\ud67e\ud67f\ud680\ud681\ud682\ud683\ud684\ud685\ud686\ud687\ud688\ud689\ud68a\ud68b\ud68c\ud68d\ud68e\ud68f\ud690\ud691\ud692\ud693\ud694\ud695\ud696\ud697\ud698\ud699\ud69a\ud69b\ud69c\ud69d\ud69e\ud69f\ud6a0\ud6a1\ud6a2\ud6a3\ud6a4\ud6a5\ud6a6\ud6a7\ud6a8\ud6a9\ud6aa\ud6ab\ud6ac\ud6ad\ud6ae\ud6af\ud6b0\ud6b1\ud6b2\ud6b3\ud6b4\ud6b5\ud6b6\ud6b7\ud6b8\ud6b9\ud6ba\ud6bb\ud6bc\ud6bd\ud6be\ud6bf\ud6c0\ud6c1\ud6c2\ud6c3\ud6c4\ud6c5\ud6c6\ud6c7\ud6c8\ud6c9\ud6ca\ud6cb\ud6cc\ud6cd\ud6ce\ud6cf\ud6d0\ud6d1\ud6d2\ud6d3\ud6d4\ud6d5\ud6d6\ud6d7\ud6d8\ud6d9\ud6da\ud6db\ud6dc\ud6dd\ud6de\ud6df\ud6e0\ud6e1\ud6e2\ud6e3\ud6e4\ud6e5\ud6e6\ud6e7\ud6e8\ud6e9\ud6ea\ud6eb\ud6ec\ud6ed\ud6ee\ud6ef\ud6f0\ud6f1\ud6f2\ud6f3\ud6f4\ud6f5\ud6f6\ud6f7\ud6f8\ud6f9\ud6fa\ud6fb\ud6fc\ud6fd\ud6fe\ud6ff\ud700\ud701\ud702\ud703\ud704\ud705\ud706\ud707\ud708\ud709\ud70a\ud70b\ud70c\ud70d\ud70e\ud70f\ud710\ud711\ud712\ud713\ud714\ud715\ud716\ud717\ud718\ud719\ud71a\ud71b\ud71c\ud71d\ud71e\ud71f\ud720\ud721\ud722\ud723\ud724\ud725\ud726\ud727\ud728\ud729\ud72a\ud72b\ud72c\ud72d\ud72e\ud72f\ud730\ud731\ud732\ud733\ud734\ud735\ud736\ud737\ud738\ud739\ud73a\ud73b\ud73c\ud73d\ud73e\ud73f\ud740\ud741\ud742\ud743\ud744\ud745\ud746\ud747\ud748\ud749\ud74a\ud74b\ud74c\ud74d\ud74e\ud74f\ud750\ud751\ud752\ud753\ud754\ud755\ud756\ud757\ud758\ud759\ud75a\ud75b\ud75c\ud75d\ud75e\ud75f\ud760\ud761\ud762\ud763\ud764\ud765\ud766\ud767\ud768\ud769\ud76a\ud76b\ud76c\ud76d\ud76e\ud76f\ud770\ud771\ud772\ud773\ud774\ud775\ud776\ud777\ud778\ud779\ud77a\ud77b\ud77c\ud77d\ud77e\ud77f\ud780\ud781\ud782\ud783\ud784\ud785\ud786\ud787\ud788\ud789\ud78a\ud78b\ud78c\ud78d\ud78e\ud78f\ud790\ud791\ud792\ud793\ud794\ud795\ud796\ud797\ud798\ud799\ud79a\ud79b\ud79c\ud79d\ud79e\ud79f\ud7a0\ud7a1\ud7a2\ud7a3\uf900\uf901\uf902\uf903\uf904\uf905\uf906\uf907\uf908\uf909\uf90a\uf90b\uf90c\uf90d\uf90e\uf90f\uf910\uf911\uf912\uf913\uf914\uf915\uf916\uf917\uf918\uf919\uf91a\uf91b\uf91c\uf91d\uf91e\uf91f\uf920\uf921\uf922\uf923\uf924\uf925\uf926\uf927\uf928\uf929\uf92a\uf92b\uf92c\uf92d\uf92e\uf92f\uf930\uf931\uf932\uf933\uf934\uf935\uf936\uf937\uf938\uf939\uf93a\uf93b\uf93c\uf93d\uf93e\uf93f\uf940\uf941\uf942\uf943\uf944\uf945\uf946\uf947\uf948\uf949\uf94a\uf94b\uf94c\uf94d\uf94e\uf94f\uf950\uf951\uf952\uf953\uf954\uf955\uf956\uf957\uf958\uf959\uf95a\uf95b\uf95c\uf95d\uf95e\uf95f\uf960\uf961\uf962\uf963\uf964\uf965\uf966\uf967\uf968\uf969\uf96a\uf96b\uf96c\uf96d\uf96e\uf96f\uf970\uf971\uf972\uf973\uf974\uf975\uf976\uf977\uf978\uf979\uf97a\uf97b\uf97c\uf97d\uf97e\uf97f\uf980\uf981\uf982\uf983\uf984\uf985\uf986\uf987\uf988\uf989\uf98a\uf98b\uf98c\uf98d\uf98e\uf98f\uf990\uf991\uf992\uf993\uf994\uf995\uf996\uf997\uf998\uf999\uf99a\uf99b\uf99c\uf99d\uf99e\uf99f\uf9a0\uf9a1\uf9a2\uf9a3\uf9a4\uf9a5\uf9a6\uf9a7\uf9a8\uf9a9\uf9aa\uf9ab\uf9ac\uf9ad\uf9ae\uf9af\uf9b0\uf9b1\uf9b2\uf9b3\uf9b4\uf9b5\uf9b6\uf9b7\uf9b8\uf9b9\uf9ba\uf9bb\uf9bc\uf9bd\uf9be\uf9bf\uf9c0\uf9c1\uf9c2\uf9c3\uf9c4\uf9c5\uf9c6\uf9c7\uf9c8\uf9c9\uf9ca\uf9cb\uf9cc\uf9cd\uf9ce\uf9cf\uf9d0\uf9d1\uf9d2\uf9d3\uf9d4\uf9d5\uf9d6\uf9d7\uf9d8\uf9d9\uf9da\uf9db\uf9dc\uf9dd\uf9de\uf9df\uf9e0\uf9e1\uf9e2\uf9e3\uf9e4\uf9e5\uf9e6\uf9e7\uf9e8\uf9e9\uf9ea\uf9eb\uf9ec\uf9ed\uf9ee\uf9ef\uf9f0\uf9f1\uf9f2\uf9f3\uf9f4\uf9f5\uf9f6\uf9f7\uf9f8\uf9f9\uf9fa\uf9fb\uf9fc\uf9fd\uf9fe\uf9ff\ufa00\ufa01\ufa02\ufa03\ufa04\ufa05\ufa06\ufa07\ufa08\ufa09\ufa0a\ufa0b\ufa0c\ufa0d\ufa0e\ufa0f\ufa10\ufa11\ufa12\ufa13\ufa14\ufa15\ufa16\ufa17\ufa18\ufa19\ufa1a\ufa1b\ufa1c\ufa1d\ufa1e\ufa1f\ufa20\ufa21\ufa22\ufa23\ufa24\ufa25\ufa26\ufa27\ufa28\ufa29\ufa2a\ufa2b\ufa2c\ufa2d\ufa30\ufa31\ufa32\ufa33\ufa34\ufa35\ufa36\ufa37\ufa38\ufa39\ufa3a\ufa3b\ufa3c\ufa3d\ufa3e\ufa3f\ufa40\ufa41\ufa42\ufa43\ufa44\ufa45\ufa46\ufa47\ufa48\ufa49\ufa4a\ufa4b\ufa4c\ufa4d\ufa4e\ufa4f\ufa50\ufa51\ufa52\ufa53\ufa54\ufa55\ufa56\ufa57\ufa58\ufa59\ufa5a\ufa5b\ufa5c\ufa5d\ufa5e\ufa5f\ufa60\ufa61\ufa62\ufa63\ufa64\ufa65\ufa66\ufa67\ufa68\ufa69\ufa6a\ufa70\ufa71\ufa72\ufa73\ufa74\ufa75\ufa76\ufa77\ufa78\ufa79\ufa7a\ufa7b\ufa7c\ufa7d\ufa7e\ufa7f\ufa80\ufa81\ufa82\ufa83\ufa84\ufa85\ufa86\ufa87\ufa88\ufa89\ufa8a\ufa8b\ufa8c\ufa8d\ufa8e\ufa8f\ufa90\ufa91\ufa92\ufa93\ufa94\ufa95\ufa96\ufa97\ufa98\ufa99\ufa9a\ufa9b\ufa9c\ufa9d\ufa9e\ufa9f\ufaa0\ufaa1\ufaa2\ufaa3\ufaa4\ufaa5\ufaa6\ufaa7\ufaa8\ufaa9\ufaaa\ufaab\ufaac\ufaad\ufaae\ufaaf\ufab0\ufab1\ufab2\ufab3\ufab4\ufab5\ufab6\ufab7\ufab8\ufab9\ufaba\ufabb\ufabc\ufabd\ufabe\ufabf\ufac0\ufac1\ufac2\ufac3\ufac4\ufac5\ufac6\ufac7\ufac8\ufac9\ufaca\ufacb\ufacc\ufacd\uface\ufacf\ufad0\ufad1\ufad2\ufad3\ufad4\ufad5\ufad6\ufad7\ufad8\ufad9\ufb1d\ufb1f\ufb20\ufb21\ufb22\ufb23\ufb24\ufb25\ufb26\ufb27\ufb28\ufb2a\ufb2b\ufb2c\ufb2d\ufb2e\ufb2f\ufb30\ufb31\ufb32\ufb33\ufb34\ufb35\ufb36\ufb38\ufb39\ufb3a\ufb3b\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46\ufb47\ufb48\ufb49\ufb4a\ufb4b\ufb4c\ufb4d\ufb4e\ufb4f\ufb50\ufb51\ufb52\ufb53\ufb54\ufb55\ufb56\ufb57\ufb58\ufb59\ufb5a\ufb5b\ufb5c\ufb5d\ufb5e\ufb5f\ufb60\ufb61\ufb62\ufb63\ufb64\ufb65\ufb66\ufb67\ufb68\ufb69\ufb6a\ufb6b\ufb6c\ufb6d\ufb6e\ufb6f\ufb70\ufb71\ufb72\ufb73\ufb74\ufb75\ufb76\ufb77\ufb78\ufb79\ufb7a\ufb7b\ufb7c\ufb7d\ufb7e\ufb7f\ufb80\ufb81\ufb82\ufb83\ufb84\ufb85\ufb86\ufb87\ufb88\ufb89\ufb8a\ufb8b\ufb8c\ufb8d\ufb8e\ufb8f\ufb90\ufb91\ufb92\ufb93\ufb94\ufb95\ufb96\ufb97\ufb98\ufb99\ufb9a\ufb9b\ufb9c\ufb9d\ufb9e\ufb9f\ufba0\ufba1\ufba2\ufba3\ufba4\ufba5\ufba6\ufba7\ufba8\ufba9\ufbaa\ufbab\ufbac\ufbad\ufbae\ufbaf\ufbb0\ufbb1\ufbd3\ufbd4\ufbd5\ufbd6\ufbd7\ufbd8\ufbd9\ufbda\ufbdb\ufbdc\ufbdd\ufbde\ufbdf\ufbe0\ufbe1\ufbe2\ufbe3\ufbe4\ufbe5\ufbe6\ufbe7\ufbe8\ufbe9\ufbea\ufbeb\ufbec\ufbed\ufbee\ufbef\ufbf0\ufbf1\ufbf2\ufbf3\ufbf4\ufbf5\ufbf6\ufbf7\ufbf8\ufbf9\ufbfa\ufbfb\ufbfc\ufbfd\ufbfe\ufbff\ufc00\ufc01\ufc02\ufc03\ufc04\ufc05\ufc06\ufc07\ufc08\ufc09\ufc0a\ufc0b\ufc0c\ufc0d\ufc0e\ufc0f\ufc10\ufc11\ufc12\ufc13\ufc14\ufc15\ufc16\ufc17\ufc18\ufc19\ufc1a\ufc1b\ufc1c\ufc1d\ufc1e\ufc1f\ufc20\ufc21\ufc22\ufc23\ufc24\ufc25\ufc26\ufc27\ufc28\ufc29\ufc2a\ufc2b\ufc2c\ufc2d\ufc2e\ufc2f\ufc30\ufc31\ufc32\ufc33\ufc34\ufc35\ufc36\ufc37\ufc38\ufc39\ufc3a\ufc3b\ufc3c\ufc3d\ufc3e\ufc3f\ufc40\ufc41\ufc42\ufc43\ufc44\ufc45\ufc46\ufc47\ufc48\ufc49\ufc4a\ufc4b\ufc4c\ufc4d\ufc4e\ufc4f\ufc50\ufc51\ufc52\ufc53\ufc54\ufc55\ufc56\ufc57\ufc58\ufc59\ufc5a\ufc5b\ufc5c\ufc5d\ufc5e\ufc5f\ufc60\ufc61\ufc62\ufc63\ufc64\ufc65\ufc66\ufc67\ufc68\ufc69\ufc6a\ufc6b\ufc6c\ufc6d\ufc6e\ufc6f\ufc70\ufc71\ufc72\ufc73\ufc74\ufc75\ufc76\ufc77\ufc78\ufc79\ufc7a\ufc7b\ufc7c\ufc7d\ufc7e\ufc7f\ufc80\ufc81\ufc82\ufc83\ufc84\ufc85\ufc86\ufc87\ufc88\ufc89\ufc8a\ufc8b\ufc8c\ufc8d\ufc8e\ufc8f\ufc90\ufc91\ufc92\ufc93\ufc94\ufc95\ufc96\ufc97\ufc98\ufc99\ufc9a\ufc9b\ufc9c\ufc9d\ufc9e\ufc9f\ufca0\ufca1\ufca2\ufca3\ufca4\ufca5\ufca6\ufca7\ufca8\ufca9\ufcaa\ufcab\ufcac\ufcad\ufcae\ufcaf\ufcb0\ufcb1\ufcb2\ufcb3\ufcb4\ufcb5\ufcb6\ufcb7\ufcb8\ufcb9\ufcba\ufcbb\ufcbc\ufcbd\ufcbe\ufcbf\ufcc0\ufcc1\ufcc2\ufcc3\ufcc4\ufcc5\ufcc6\ufcc7\ufcc8\ufcc9\ufcca\ufccb\ufccc\ufccd\ufcce\ufccf\ufcd0\ufcd1\ufcd2\ufcd3\ufcd4\ufcd5\ufcd6\ufcd7\ufcd8\ufcd9\ufcda\ufcdb\ufcdc\ufcdd\ufcde\ufcdf\ufce0\ufce1\ufce2\ufce3\ufce4\ufce5\ufce6\ufce7\ufce8\ufce9\ufcea\ufceb\ufcec\ufced\ufcee\ufcef\ufcf0\ufcf1\ufcf2\ufcf3\ufcf4\ufcf5\ufcf6\ufcf7\ufcf8\ufcf9\ufcfa\ufcfb\ufcfc\ufcfd\ufcfe\ufcff\ufd00\ufd01\ufd02\ufd03\ufd04\ufd05\ufd06\ufd07\ufd08\ufd09\ufd0a\ufd0b\ufd0c\ufd0d\ufd0e\ufd0f\ufd10\ufd11\ufd12\ufd13\ufd14\ufd15\ufd16\ufd17\ufd18\ufd19\ufd1a\ufd1b\ufd1c\ufd1d\ufd1e\ufd1f\ufd20\ufd21\ufd22\ufd23\ufd24\ufd25\ufd26\ufd27\ufd28\ufd29\ufd2a\ufd2b\ufd2c\ufd2d\ufd2e\ufd2f\ufd30\ufd31\ufd32\ufd33\ufd34\ufd35\ufd36\ufd37\ufd38\ufd39\ufd3a\ufd3b\ufd3c\ufd3d\ufd50\ufd51\ufd52\ufd53\ufd54\ufd55\ufd56\ufd57\ufd58\ufd59\ufd5a\ufd5b\ufd5c\ufd5d\ufd5e\ufd5f\ufd60\ufd61\ufd62\ufd63\ufd64\ufd65\ufd66\ufd67\ufd68\ufd69\ufd6a\ufd6b\ufd6c\ufd6d\ufd6e\ufd6f\ufd70\ufd71\ufd72\ufd73\ufd74\ufd75\ufd76\ufd77\ufd78\ufd79\ufd7a\ufd7b\ufd7c\ufd7d\ufd7e\ufd7f\ufd80\ufd81\ufd82\ufd83\ufd84\ufd85\ufd86\ufd87\ufd88\ufd89\ufd8a\ufd8b\ufd8c\ufd8d\ufd8e\ufd8f\ufd92\ufd93\ufd94\ufd95\ufd96\ufd97\ufd98\ufd99\ufd9a\ufd9b\ufd9c\ufd9d\ufd9e\ufd9f\ufda0\ufda1\ufda2\ufda3\ufda4\ufda5\ufda6\ufda7\ufda8\ufda9\ufdaa\ufdab\ufdac\ufdad\ufdae\ufdaf\ufdb0\ufdb1\ufdb2\ufdb3\ufdb4\ufdb5\ufdb6\ufdb7\ufdb8\ufdb9\ufdba\ufdbb\ufdbc\ufdbd\ufdbe\ufdbf\ufdc0\ufdc1\ufdc2\ufdc3\ufdc4\ufdc5\ufdc6\ufdc7\ufdf0\ufdf1\ufdf2\ufdf3\ufdf4\ufdf5\ufdf6\ufdf7\ufdf8\ufdf9\ufdfa\ufdfb\ufe70\ufe71\ufe72\ufe73\ufe74\ufe76\ufe77\ufe78\ufe79\ufe7a\ufe7b\ufe7c\ufe7d\ufe7e\ufe7f\ufe80\ufe81\ufe82\ufe83\ufe84\ufe85\ufe86\ufe87\ufe88\ufe89\ufe8a\ufe8b\ufe8c\ufe8d\ufe8e\ufe8f\ufe90\ufe91\ufe92\ufe93\ufe94\ufe95\ufe96\ufe97\ufe98\ufe99\ufe9a\ufe9b\ufe9c\ufe9d\ufe9e\ufe9f\ufea0\ufea1\ufea2\ufea3\ufea4\ufea5\ufea6\ufea7\ufea8\ufea9\ufeaa\ufeab\ufeac\ufead\ufeae\ufeaf\ufeb0\ufeb1\ufeb2\ufeb3\ufeb4\ufeb5\ufeb6\ufeb7\ufeb8\ufeb9\ufeba\ufebb\ufebc\ufebd\ufebe\ufebf\ufec0\ufec1\ufec2\ufec3\ufec4\ufec5\ufec6\ufec7\ufec8\ufec9\ufeca\ufecb\ufecc\ufecd\ufece\ufecf\ufed0\ufed1\ufed2\ufed3\ufed4\ufed5\ufed6\ufed7\ufed8\ufed9\ufeda\ufedb\ufedc\ufedd\ufede\ufedf\ufee0\ufee1\ufee2\ufee3\ufee4\ufee5\ufee6\ufee7\ufee8\ufee9\ufeea\ufeeb\ufeec\ufeed\ufeee\ufeef\ufef0\ufef1\ufef2\ufef3\ufef4\ufef5\ufef6\ufef7\ufef8\ufef9\ufefa\ufefb\ufefc\uff66\uff67\uff68\uff69\uff6a\uff6b\uff6c\uff6d\uff6e\uff6f\uff71\uff72\uff73\uff74\uff75\uff76\uff77\uff78\uff79\uff7a\uff7b\uff7c\uff7d\uff7e\uff7f\uff80\uff81\uff82\uff83\uff84\uff85\uff86\uff87\uff88\uff89\uff8a\uff8b\uff8c\uff8d\uff8e\uff8f\uff90\uff91\uff92\uff93\uff94\uff95\uff96\uff97\uff98\uff99\uff9a\uff9b\uff9c\uff9d\uffa0\uffa1\uffa2\uffa3\uffa4\uffa5\uffa6\uffa7\uffa8\uffa9\uffaa\uffab\uffac\uffad\uffae\uffaf\uffb0\uffb1\uffb2\uffb3\uffb4\uffb5\uffb6\uffb7\uffb8\uffb9\uffba\uffbb\uffbc\uffbd\uffbe\uffc2\uffc3\uffc4\uffc5\uffc6\uffc7\uffca\uffcb\uffcc\uffcd\uffce\uffcf\uffd2\uffd3\uffd4\uffd5\uffd6\uffd7\uffda\uffdb\uffdc'
-
-Lt = u'\u01c5\u01c8\u01cb\u01f2\u1f88\u1f89\u1f8a\u1f8b\u1f8c\u1f8d\u1f8e\u1f8f\u1f98\u1f99\u1f9a\u1f9b\u1f9c\u1f9d\u1f9e\u1f9f\u1fa8\u1fa9\u1faa\u1fab\u1fac\u1fad\u1fae\u1faf\u1fbc\u1fcc\u1ffc'
-
-Lu = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd8\xd9\xda\xdb\xdc\xdd\xde\u0100\u0102\u0104\u0106\u0108\u010a\u010c\u010e\u0110\u0112\u0114\u0116\u0118\u011a\u011c\u011e\u0120\u0122\u0124\u0126\u0128\u012a\u012c\u012e\u0130\u0132\u0134\u0136\u0139\u013b\u013d\u013f\u0141\u0143\u0145\u0147\u014a\u014c\u014e\u0150\u0152\u0154\u0156\u0158\u015a\u015c\u015e\u0160\u0162\u0164\u0166\u0168\u016a\u016c\u016e\u0170\u0172\u0174\u0176\u0178\u0179\u017b\u017d\u0181\u0182\u0184\u0186\u0187\u0189\u018a\u018b\u018e\u018f\u0190\u0191\u0193\u0194\u0196\u0197\u0198\u019c\u019d\u019f\u01a0\u01a2\u01a4\u01a6\u01a7\u01a9\u01ac\u01ae\u01af\u01b1\u01b2\u01b3\u01b5\u01b7\u01b8\u01bc\u01c4\u01c7\u01ca\u01cd\u01cf\u01d1\u01d3\u01d5\u01d7\u01d9\u01db\u01de\u01e0\u01e2\u01e4\u01e6\u01e8\u01ea\u01ec\u01ee\u01f1\u01f4\u01f6\u01f7\u01f8\u01fa\u01fc\u01fe\u0200\u0202\u0204\u0206\u0208\u020a\u020c\u020e\u0210\u0212\u0214\u0216\u0218\u021a\u021c\u021e\u0220\u0222\u0224\u0226\u0228\u022a\u022c\u022e\u0230\u0232\u023a\u023b\u023d\u023e\u0241\u0386\u0388\u0389\u038a\u038c\u038e\u038f\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9\u03aa\u03ab\u03d2\u03d3\u03d4\u03d8\u03da\u03dc\u03de\u03e0\u03e2\u03e4\u03e6\u03e8\u03ea\u03ec\u03ee\u03f4\u03f7\u03f9\u03fa\u03fd\u03fe\u03ff\u0400\u0401\u0402\u0403\u0404\u0405\u0406\u0407\u0408\u0409\u040a\u040b\u040c\u040d\u040e\u040f\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0460\u0462\u0464\u0466\u0468\u046a\u046c\u046e\u0470\u0472\u0474\u0476\u0478\u047a\u047c\u047e\u0480\u048a\u048c\u048e\u0490\u0492\u0494\u0496\u0498\u049a\u049c\u049e\u04a0\u04a2\u04a4\u04a6\u04a8\u04aa\u04ac\u04ae\u04b0\u04b2\u04b4\u04b6\u04b8\u04ba\u04bc\u04be\u04c0\u04c1\u04c3\u04c5\u04c7\u04c9\u04cb\u04cd\u04d0\u04d2\u04d4\u04d6\u04d8\u04da\u04dc\u04de\u04e0\u04e2\u04e4\u04e6\u04e8\u04ea\u04ec\u04ee\u04f0\u04f2\u04f4\u04f6\u04f8\u0500\u0502\u0504\u0506\u0508\u050a\u050c\u050e\u0531\u0532\u0533\u0534\u0535\u0536\u0537\u0538\u0539\u053a\u053b\u053c\u053d\u053e\u053f\u0540\u0541\u0542\u0543\u0544\u0545\u0546\u0547\u0548\u0549\u054a\u054b\u054c\u054d\u054e\u054f\u0550\u0551\u0552\u0553\u0554\u0555\u0556\u10a0\u10a1\u10a2\u10a3\u10a4\u10a5\u10a6\u10a7\u10a8\u10a9\u10aa\u10ab\u10ac\u10ad\u10ae\u10af\u10b0\u10b1\u10b2\u10b3\u10b4\u10b5\u10b6\u10b7\u10b8\u10b9\u10ba\u10bb\u10bc\u10bd\u10be\u10bf\u10c0\u10c1\u10c2\u10c3\u10c4\u10c5\u1e00\u1e02\u1e04\u1e06\u1e08\u1e0a\u1e0c\u1e0e\u1e10\u1e12\u1e14\u1e16\u1e18\u1e1a\u1e1c\u1e1e\u1e20\u1e22\u1e24\u1e26\u1e28\u1e2a\u1e2c\u1e2e\u1e30\u1e32\u1e34\u1e36\u1e38\u1e3a\u1e3c\u1e3e\u1e40\u1e42\u1e44\u1e46\u1e48\u1e4a\u1e4c\u1e4e\u1e50\u1e52\u1e54\u1e56\u1e58\u1e5a\u1e5c\u1e5e\u1e60\u1e62\u1e64\u1e66\u1e68\u1e6a\u1e6c\u1e6e\u1e70\u1e72\u1e74\u1e76\u1e78\u1e7a\u1e7c\u1e7e\u1e80\u1e82\u1e84\u1e86\u1e88\u1e8a\u1e8c\u1e8e\u1e90\u1e92\u1e94\u1ea0\u1ea2\u1ea4\u1ea6\u1ea8\u1eaa\u1eac\u1eae\u1eb0\u1eb2\u1eb4\u1eb6\u1eb8\u1eba\u1ebc\u1ebe\u1ec0\u1ec2\u1ec4\u1ec6\u1ec8\u1eca\u1ecc\u1ece\u1ed0\u1ed2\u1ed4\u1ed6\u1ed8\u1eda\u1edc\u1ede\u1ee0\u1ee2\u1ee4\u1ee6\u1ee8\u1eea\u1eec\u1eee\u1ef0\u1ef2\u1ef4\u1ef6\u1ef8\u1f08\u1f09\u1f0a\u1f0b\u1f0c\u1f0d\u1f0e\u1f0f\u1f18\u1f19\u1f1a\u1f1b\u1f1c\u1f1d\u1f28\u1f29\u1f2a\u1f2b\u1f2c\u1f2d\u1f2e\u1f2f\u1f38\u1f39\u1f3a\u1f3b\u1f3c\u1f3d\u1f3e\u1f3f\u1f48\u1f49\u1f4a\u1f4b\u1f4c\u1f4d\u1f59\u1f5b\u1f5d\u1f5f\u1f68\u1f69\u1f6a\u1f6b\u1f6c\u1f6d\u1f6e\u1f6f\u1fb8\u1fb9\u1fba\u1fbb\u1fc8\u1fc9\u1fca\u1fcb\u1fd8\u1fd9\u1fda\u1fdb\u1fe8\u1fe9\u1fea\u1feb\u1fec\u1ff8\u1ff9\u1ffa\u1ffb\u2102\u2107\u210b\u210c\u210d\u2110\u2111\u2112\u2115\u2119\u211a\u211b\u211c\u211d\u2124\u2126\u2128\u212a\u212b\u212c\u212d\u2130\u2131\u2133\u213e\u213f\u2145\u2c00\u2c01\u2c02\u2c03\u2c04\u2c05\u2c06\u2c07\u2c08\u2c09\u2c0a\u2c0b\u2c0c\u2c0d\u2c0e\u2c0f\u2c10\u2c11\u2c12\u2c13\u2c14\u2c15\u2c16\u2c17\u2c18\u2c19\u2c1a\u2c1b\u2c1c\u2c1d\u2c1e\u2c1f\u2c20\u2c21\u2c22\u2c23\u2c24\u2c25\u2c26\u2c27\u2c28\u2c29\u2c2a\u2c2b\u2c2c\u2c2d\u2c2e\u2c80\u2c82\u2c84\u2c86\u2c88\u2c8a\u2c8c\u2c8e\u2c90\u2c92\u2c94\u2c96\u2c98\u2c9a\u2c9c\u2c9e\u2ca0\u2ca2\u2ca4\u2ca6\u2ca8\u2caa\u2cac\u2cae\u2cb0\u2cb2\u2cb4\u2cb6\u2cb8\u2cba\u2cbc\u2cbe\u2cc0\u2cc2\u2cc4\u2cc6\u2cc8\u2cca\u2ccc\u2cce\u2cd0\u2cd2\u2cd4\u2cd6\u2cd8\u2cda\u2cdc\u2cde\u2ce0\u2ce2\uff21\uff22\uff23\uff24\uff25\uff26\uff27\uff28\uff29\uff2a\uff2b\uff2c\uff2d\uff2e\uff2f\uff30\uff31\uff32\uff33\uff34\uff35\uff36\uff37\uff38\uff39\uff3a'
-
-Mc = u'\u0903\u093e\u093f\u0940\u0949\u094a\u094b\u094c\u0982\u0983\u09be\u09bf\u09c0\u09c7\u09c8\u09cb\u09cc\u09d7\u0a03\u0a3e\u0a3f\u0a40\u0a83\u0abe\u0abf\u0ac0\u0ac9\u0acb\u0acc\u0b02\u0b03\u0b3e\u0b40\u0b47\u0b48\u0b4b\u0b4c\u0b57\u0bbe\u0bbf\u0bc1\u0bc2\u0bc6\u0bc7\u0bc8\u0bca\u0bcb\u0bcc\u0bd7\u0c01\u0c02\u0c03\u0c41\u0c42\u0c43\u0c44\u0c82\u0c83\u0cbe\u0cc0\u0cc1\u0cc2\u0cc3\u0cc4\u0cc7\u0cc8\u0cca\u0ccb\u0cd5\u0cd6\u0d02\u0d03\u0d3e\u0d3f\u0d40\u0d46\u0d47\u0d48\u0d4a\u0d4b\u0d4c\u0d57\u0d82\u0d83\u0dcf\u0dd0\u0dd1\u0dd8\u0dd9\u0dda\u0ddb\u0ddc\u0ddd\u0dde\u0ddf\u0df2\u0df3\u0f3e\u0f3f\u0f7f\u102c\u1031\u1038\u1056\u1057\u17b6\u17be\u17bf\u17c0\u17c1\u17c2\u17c3\u17c4\u17c5\u17c7\u17c8\u1923\u1924\u1925\u1926\u1929\u192a\u192b\u1930\u1931\u1933\u1934\u1935\u1936\u1937\u1938\u19b0\u19b1\u19b2\u19b3\u19b4\u19b5\u19b6\u19b7\u19b8\u19b9\u19ba\u19bb\u19bc\u19bd\u19be\u19bf\u19c0\u19c8\u19c9\u1a19\u1a1a\u1a1b\ua802\ua823\ua824\ua827'
-
-Me = u'\u0488\u0489\u06de\u20dd\u20de\u20df\u20e0\u20e2\u20e3\u20e4'
-
-Mn = u'\u0300\u0301\u0302\u0303\u0304\u0305\u0306\u0307\u0308\u0309\u030a\u030b\u030c\u030d\u030e\u030f\u0310\u0311\u0312\u0313\u0314\u0315\u0316\u0317\u0318\u0319\u031a\u031b\u031c\u031d\u031e\u031f\u0320\u0321\u0322\u0323\u0324\u0325\u0326\u0327\u0328\u0329\u032a\u032b\u032c\u032d\u032e\u032f\u0330\u0331\u0332\u0333\u0334\u0335\u0336\u0337\u0338\u0339\u033a\u033b\u033c\u033d\u033e\u033f\u0340\u0341\u0342\u0343\u0344\u0345\u0346\u0347\u0348\u0349\u034a\u034b\u034c\u034d\u034e\u034f\u0350\u0351\u0352\u0353\u0354\u0355\u0356\u0357\u0358\u0359\u035a\u035b\u035c\u035d\u035e\u035f\u0360\u0361\u0362\u0363\u0364\u0365\u0366\u0367\u0368\u0369\u036a\u036b\u036c\u036d\u036e\u036f\u0483\u0484\u0485\u0486\u0591\u0592\u0593\u0594\u0595\u0596\u0597\u0598\u0599\u059a\u059b\u059c\u059d\u059e\u059f\u05a0\u05a1\u05a2\u05a3\u05a4\u05a5\u05a6\u05a7\u05a8\u05a9\u05aa\u05ab\u05ac\u05ad\u05ae\u05af\u05b0\u05b1\u05b2\u05b3\u05b4\u05b5\u05b6\u05b7\u05b8\u05b9\u05bb\u05bc\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610\u0611\u0612\u0613\u0614\u0615\u064b\u064c\u064d\u064e\u064f\u0650\u0651\u0652\u0653\u0654\u0655\u0656\u0657\u0658\u0659\u065a\u065b\u065c\u065d\u065e\u0670\u06d6\u06d7\u06d8\u06d9\u06da\u06db\u06dc\u06df\u06e0\u06e1\u06e2\u06e3\u06e4\u06e7\u06e8\u06ea\u06eb\u06ec\u06ed\u0711\u0730\u0731\u0732\u0733\u0734\u0735\u0736\u0737\u0738\u0739\u073a\u073b\u073c\u073d\u073e\u073f\u0740\u0741\u0742\u0743\u0744\u0745\u0746\u0747\u0748\u0749\u074a\u07a6\u07a7\u07a8\u07a9\u07aa\u07ab\u07ac\u07ad\u07ae\u07af\u07b0\u0901\u0902\u093c\u0941\u0942\u0943\u0944\u0945\u0946\u0947\u0948\u094d\u0951\u0952\u0953\u0954\u0962\u0963\u0981\u09bc\u09c1\u09c2\u09c3\u09c4\u09cd\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b\u0a4c\u0a4d\u0a70\u0a71\u0a81\u0a82\u0abc\u0ac1\u0ac2\u0ac3\u0ac4\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3f\u0b41\u0b42\u0b43\u0b4d\u0b56\u0b82\u0bc0\u0bcd\u0c3e\u0c3f\u0c40\u0c46\u0c47\u0c48\u0c4a\u0c4b\u0c4c\u0c4d\u0c55\u0c56\u0cbc\u0cbf\u0cc6\u0ccc\u0ccd\u0d41\u0d42\u0d43\u0d4d\u0dca\u0dd2\u0dd3\u0dd4\u0dd6\u0e31\u0e34\u0e35\u0e36\u0e37\u0e38\u0e39\u0e3a\u0e47\u0e48\u0e49\u0e4a\u0e4b\u0e4c\u0e4d\u0e4e\u0eb1\u0eb4\u0eb5\u0eb6\u0eb7\u0eb8\u0eb9\u0ebb\u0ebc\u0ec8\u0ec9\u0eca\u0ecb\u0ecc\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71\u0f72\u0f73\u0f74\u0f75\u0f76\u0f77\u0f78\u0f79\u0f7a\u0f7b\u0f7c\u0f7d\u0f7e\u0f80\u0f81\u0f82\u0f83\u0f84\u0f86\u0f87\u0f90\u0f91\u0f92\u0f93\u0f94\u0f95\u0f96\u0f97\u0f99\u0f9a\u0f9b\u0f9c\u0f9d\u0f9e\u0f9f\u0fa0\u0fa1\u0fa2\u0fa3\u0fa4\u0fa5\u0fa6\u0fa7\u0fa8\u0fa9\u0faa\u0fab\u0fac\u0fad\u0fae\u0faf\u0fb0\u0fb1\u0fb2\u0fb3\u0fb4\u0fb5\u0fb6\u0fb7\u0fb8\u0fb9\u0fba\u0fbb\u0fbc\u0fc6\u102d\u102e\u102f\u1030\u1032\u1036\u1037\u1039\u1058\u1059\u135f\u1712\u1713\u1714\u1732\u1733\u1734\u1752\u1753\u1772\u1773\u17b7\u17b8\u17b9\u17ba\u17bb\u17bc\u17bd\u17c6\u17c9\u17ca\u17cb\u17cc\u17cd\u17ce\u17cf\u17d0\u17d1\u17d2\u17d3\u17dd\u180b\u180c\u180d\u18a9\u1920\u1921\u1922\u1927\u1928\u1932\u1939\u193a\u193b\u1a17\u1a18\u1dc0\u1dc1\u1dc2\u1dc3\u20d0\u20d1\u20d2\u20d3\u20d4\u20d5\u20d6\u20d7\u20d8\u20d9\u20da\u20db\u20dc\u20e1\u20e5\u20e6\u20e7\u20e8\u20e9\u20ea\u20eb\u302a\u302b\u302c\u302d\u302e\u302f\u3099\u309a\ua806\ua80b\ua825\ua826\ufb1e\ufe00\ufe01\ufe02\ufe03\ufe04\ufe05\ufe06\ufe07\ufe08\ufe09\ufe0a\ufe0b\ufe0c\ufe0d\ufe0e\ufe0f\ufe20\ufe21\ufe22\ufe23'
-
-Nd = u'0123456789\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669\u06f0\u06f1\u06f2\u06f3\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u0966\u0967\u0968\u0969\u096a\u096b\u096c\u096d\u096e\u096f\u09e6\u09e7\u09e8\u09e9\u09ea\u09eb\u09ec\u09ed\u09ee\u09ef\u0a66\u0a67\u0a68\u0a69\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0ae6\u0ae7\u0ae8\u0ae9\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef\u0b66\u0b67\u0b68\u0b69\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0be6\u0be7\u0be8\u0be9\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0c66\u0c67\u0c68\u0c69\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e\u0c6f\u0ce6\u0ce7\u0ce8\u0ce9\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0d66\u0d67\u0d68\u0d69\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0e50\u0e51\u0e52\u0e53\u0e54\u0e55\u0e56\u0e57\u0e58\u0e59\u0ed0\u0ed1\u0ed2\u0ed3\u0ed4\u0ed5\u0ed6\u0ed7\u0ed8\u0ed9\u0f20\u0f21\u0f22\u0f23\u0f24\u0f25\u0f26\u0f27\u0f28\u0f29\u1040\u1041\u1042\u1043\u1044\u1045\u1046\u1047\u1048\u1049\u17e0\u17e1\u17e2\u17e3\u17e4\u17e5\u17e6\u17e7\u17e8\u17e9\u1810\u1811\u1812\u1813\u1814\u1815\u1816\u1817\u1818\u1819\u1946\u1947\u1948\u1949\u194a\u194b\u194c\u194d\u194e\u194f\u19d0\u19d1\u19d2\u19d3\u19d4\u19d5\u19d6\u19d7\u19d8\u19d9\uff10\uff11\uff12\uff13\uff14\uff15\uff16\uff17\uff18\uff19'
-
-Nl = u'\u16ee\u16ef\u16f0\u2160\u2161\u2162\u2163\u2164\u2165\u2166\u2167\u2168\u2169\u216a\u216b\u216c\u216d\u216e\u216f\u2170\u2171\u2172\u2173\u2174\u2175\u2176\u2177\u2178\u2179\u217a\u217b\u217c\u217d\u217e\u217f\u2180\u2181\u2182\u2183\u3007\u3021\u3022\u3023\u3024\u3025\u3026\u3027\u3028\u3029\u3038\u3039\u303a'
-
-No = u'\xb2\xb3\xb9\xbc\xbd\xbe\u09f4\u09f5\u09f6\u09f7\u09f8\u09f9\u0bf0\u0bf1\u0bf2\u0f2a\u0f2b\u0f2c\u0f2d\u0f2e\u0f2f\u0f30\u0f31\u0f32\u0f33\u1369\u136a\u136b\u136c\u136d\u136e\u136f\u1370\u1371\u1372\u1373\u1374\u1375\u1376\u1377\u1378\u1379\u137a\u137b\u137c\u17f0\u17f1\u17f2\u17f3\u17f4\u17f5\u17f6\u17f7\u17f8\u17f9\u2070\u2074\u2075\u2076\u2077\u2078\u2079\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089\u2153\u2154\u2155\u2156\u2157\u2158\u2159\u215a\u215b\u215c\u215d\u215e\u215f\u2460\u2461\u2462\u2463\u2464\u2465\u2466\u2467\u2468\u2469\u246a\u246b\u246c\u246d\u246e\u246f\u2470\u2471\u2472\u2473\u2474\u2475\u2476\u2477\u2478\u2479\u247a\u247b\u247c\u247d\u247e\u247f\u2480\u2481\u2482\u2483\u2484\u2485\u2486\u2487\u2488\u2489\u248a\u248b\u248c\u248d\u248e\u248f\u2490\u2491\u2492\u2493\u2494\u2495\u2496\u2497\u2498\u2499\u249a\u249b\u24ea\u24eb\u24ec\u24ed\u24ee\u24ef\u24f0\u24f1\u24f2\u24f3\u24f4\u24f5\u24f6\u24f7\u24f8\u24f9\u24fa\u24fb\u24fc\u24fd\u24fe\u24ff\u2776\u2777\u2778\u2779\u277a\u277b\u277c\u277d\u277e\u277f\u2780\u2781\u2782\u2783\u2784\u2785\u2786\u2787\u2788\u2789\u278a\u278b\u278c\u278d\u278e\u278f\u2790\u2791\u2792\u2793\u2cfd\u3192\u3193\u3194\u3195\u3220\u3221\u3222\u3223\u3224\u3225\u3226\u3227\u3228\u3229\u3251\u3252\u3253\u3254\u3255\u3256\u3257\u3258\u3259\u325a\u325b\u325c\u325d\u325e\u325f\u3280\u3281\u3282\u3283\u3284\u3285\u3286\u3287\u3288\u3289\u32b1\u32b2\u32b3\u32b4\u32b5\u32b6\u32b7\u32b8\u32b9\u32ba\u32bb\u32bc\u32bd\u32be\u32bf'
-
-Pc = u'_\u203f\u2040\u2054\ufe33\ufe34\ufe4d\ufe4e\ufe4f\uff3f'
-
-Pd = u'-\u058a\u1806\u2010\u2011\u2012\u2013\u2014\u2015\u2e17\u301c\u3030\u30a0\ufe31\ufe32\ufe58\ufe63\uff0d'
-
-Pe = u')]}\u0f3b\u0f3d\u169c\u2046\u207e\u208e\u232a\u23b5\u2769\u276b\u276d\u276f\u2771\u2773\u2775\u27c6\u27e7\u27e9\u27eb\u2984\u2986\u2988\u298a\u298c\u298e\u2990\u2992\u2994\u2996\u2998\u29d9\u29db\u29fd\u3009\u300b\u300d\u300f\u3011\u3015\u3017\u3019\u301b\u301e\u301f\ufd3f\ufe18\ufe36\ufe38\ufe3a\ufe3c\ufe3e\ufe40\ufe42\ufe44\ufe48\ufe5a\ufe5c\ufe5e\uff09\uff3d\uff5d\uff60\uff63'
-
-Pf = u'\xbb\u2019\u201d\u203a\u2e03\u2e05\u2e0a\u2e0d\u2e1d'
-
-Pi = u'\xab\u2018\u201b\u201c\u201f\u2039\u2e02\u2e04\u2e09\u2e0c\u2e1c'
-
-Po = u'!"#%&\'*,./:;?@\\\xa1\xb7\xbf\u037e\u0387\u055a\u055b\u055c\u055d\u055e\u055f\u0589\u05be\u05c0\u05c3\u05c6\u05f3\u05f4\u060c\u060d\u061b\u061e\u061f\u066a\u066b\u066c\u066d\u06d4\u0700\u0701\u0702\u0703\u0704\u0705\u0706\u0707\u0708\u0709\u070a\u070b\u070c\u070d\u0964\u0965\u0970\u0df4\u0e4f\u0e5a\u0e5b\u0f04\u0f05\u0f06\u0f07\u0f08\u0f09\u0f0a\u0f0b\u0f0c\u0f0d\u0f0e\u0f0f\u0f10\u0f11\u0f12\u0f85\u0fd0\u0fd1\u104a\u104b\u104c\u104d\u104e\u104f\u10fb\u1361\u1362\u1363\u1364\u1365\u1366\u1367\u1368\u166d\u166e\u16eb\u16ec\u16ed\u1735\u1736\u17d4\u17d5\u17d6\u17d8\u17d9\u17da\u1800\u1801\u1802\u1803\u1804\u1805\u1807\u1808\u1809\u180a\u1944\u1945\u19de\u19df\u1a1e\u1a1f\u2016\u2017\u2020\u2021\u2022\u2023\u2024\u2025\u2026\u2027\u2030\u2031\u2032\u2033\u2034\u2035\u2036\u2037\u2038\u203b\u203c\u203d\u203e\u2041\u2042\u2043\u2047\u2048\u2049\u204a\u204b\u204c\u204d\u204e\u204f\u2050\u2051\u2053\u2055\u2056\u2057\u2058\u2059\u205a\u205b\u205c\u205d\u205e\u23b6\u2cf9\u2cfa\u2cfb\u2cfc\u2cfe\u2cff\u2e00\u2e01\u2e06\u2e07\u2e08\u2e0b\u2e0e\u2e0f\u2e10\u2e11\u2e12\u2e13\u2e14\u2e15\u2e16\u3001\u3002\u3003\u303d\u30fb\ufe10\ufe11\ufe12\ufe13\ufe14\ufe15\ufe16\ufe19\ufe30\ufe45\ufe46\ufe49\ufe4a\ufe4b\ufe4c\ufe50\ufe51\ufe52\ufe54\ufe55\ufe56\ufe57\ufe5f\ufe60\ufe61\ufe68\ufe6a\ufe6b\uff01\uff02\uff03\uff05\uff06\uff07\uff0a\uff0c\uff0e\uff0f\uff1a\uff1b\uff1f\uff20\uff3c\uff61\uff64\uff65'
-
-Ps = u'([{\u0f3a\u0f3c\u169b\u201a\u201e\u2045\u207d\u208d\u2329\u23b4\u2768\u276a\u276c\u276e\u2770\u2772\u2774\u27c5\u27e6\u27e8\u27ea\u2983\u2985\u2987\u2989\u298b\u298d\u298f\u2991\u2993\u2995\u2997\u29d8\u29da\u29fc\u3008\u300a\u300c\u300e\u3010\u3014\u3016\u3018\u301a\u301d\ufd3e\ufe17\ufe35\ufe37\ufe39\ufe3b\ufe3d\ufe3f\ufe41\ufe43\ufe47\ufe59\ufe5b\ufe5d\uff08\uff3b\uff5b\uff5f\uff62'
-
-Sc = u'$\xa2\xa3\xa4\xa5\u060b\u09f2\u09f3\u0af1\u0bf9\u0e3f\u17db\u20a0\u20a1\u20a2\u20a3\u20a4\u20a5\u20a6\u20a7\u20a8\u20a9\u20aa\u20ab\u20ac\u20ad\u20ae\u20af\u20b0\u20b1\u20b2\u20b3\u20b4\u20b5\ufdfc\ufe69\uff04\uffe0\uffe1\uffe5\uffe6'
-
-Sk = u'^`\xa8\xaf\xb4\xb8\u02c2\u02c3\u02c4\u02c5\u02d2\u02d3\u02d4\u02d5\u02d6\u02d7\u02d8\u02d9\u02da\u02db\u02dc\u02dd\u02de\u02df\u02e5\u02e6\u02e7\u02e8\u02e9\u02ea\u02eb\u02ec\u02ed\u02ef\u02f0\u02f1\u02f2\u02f3\u02f4\u02f5\u02f6\u02f7\u02f8\u02f9\u02fa\u02fb\u02fc\u02fd\u02fe\u02ff\u0374\u0375\u0384\u0385\u1fbd\u1fbf\u1fc0\u1fc1\u1fcd\u1fce\u1fcf\u1fdd\u1fde\u1fdf\u1fed\u1fee\u1fef\u1ffd\u1ffe\u309b\u309c\ua700\ua701\ua702\ua703\ua704\ua705\ua706\ua707\ua708\ua709\ua70a\ua70b\ua70c\ua70d\ua70e\ua70f\ua710\ua711\ua712\ua713\ua714\ua715\ua716\uff3e\uff40\uffe3'
-
-Sm = u'+<=>|~\xac\xb1\xd7\xf7\u03f6\u2044\u2052\u207a\u207b\u207c\u208a\u208b\u208c\u2140\u2141\u2142\u2143\u2144\u214b\u2190\u2191\u2192\u2193\u2194\u219a\u219b\u21a0\u21a3\u21a6\u21ae\u21ce\u21cf\u21d2\u21d4\u21f4\u21f5\u21f6\u21f7\u21f8\u21f9\u21fa\u21fb\u21fc\u21fd\u21fe\u21ff\u2200\u2201\u2202\u2203\u2204\u2205\u2206\u2207\u2208\u2209\u220a\u220b\u220c\u220d\u220e\u220f\u2210\u2211\u2212\u2213\u2214\u2215\u2216\u2217\u2218\u2219\u221a\u221b\u221c\u221d\u221e\u221f\u2220\u2221\u2222\u2223\u2224\u2225\u2226\u2227\u2228\u2229\u222a\u222b\u222c\u222d\u222e\u222f\u2230\u2231\u2232\u2233\u2234\u2235\u2236\u2237\u2238\u2239\u223a\u223b\u223c\u223d\u223e\u223f\u2240\u2241\u2242\u2243\u2244\u2245\u2246\u2247\u2248\u2249\u224a\u224b\u224c\u224d\u224e\u224f\u2250\u2251\u2252\u2253\u2254\u2255\u2256\u2257\u2258\u2259\u225a\u225b\u225c\u225d\u225e\u225f\u2260\u2261\u2262\u2263\u2264\u2265\u2266\u2267\u2268\u2269\u226a\u226b\u226c\u226d\u226e\u226f\u2270\u2271\u2272\u2273\u2274\u2275\u2276\u2277\u2278\u2279\u227a\u227b\u227c\u227d\u227e\u227f\u2280\u2281\u2282\u2283\u2284\u2285\u2286\u2287\u2288\u2289\u228a\u228b\u228c\u228d\u228e\u228f\u2290\u2291\u2292\u2293\u2294\u2295\u2296\u2297\u2298\u2299\u229a\u229b\u229c\u229d\u229e\u229f\u22a0\u22a1\u22a2\u22a3\u22a4\u22a5\u22a6\u22a7\u22a8\u22a9\u22aa\u22ab\u22ac\u22ad\u22ae\u22af\u22b0\u22b1\u22b2\u22b3\u22b4\u22b5\u22b6\u22b7\u22b8\u22b9\u22ba\u22bb\u22bc\u22bd\u22be\u22bf\u22c0\u22c1\u22c2\u22c3\u22c4\u22c5\u22c6\u22c7\u22c8\u22c9\u22ca\u22cb\u22cc\u22cd\u22ce\u22cf\u22d0\u22d1\u22d2\u22d3\u22d4\u22d5\u22d6\u22d7\u22d8\u22d9\u22da\u22db\u22dc\u22dd\u22de\u22df\u22e0\u22e1\u22e2\u22e3\u22e4\u22e5\u22e6\u22e7\u22e8\u22e9\u22ea\u22eb\u22ec\u22ed\u22ee\u22ef\u22f0\u22f1\u22f2\u22f3\u22f4\u22f5\u22f6\u22f7\u22f8\u22f9\u22fa\u22fb\u22fc\u22fd\u22fe\u22ff\u2308\u2309\u230a\u230b\u2320\u2321\u237c\u239b\u239c\u239d\u239e\u239f\u23a0\u23a1\u23a2\u23a3\u23a4\u23a5\u23a6\u23a7\u23a8\u23a9\u23aa\u23ab\u23ac\u23ad\u23ae\u23af\u23b0\u23b1\u23b2\u23b3\u25b7\u25c1\u25f8\u25f9\u25fa\u25fb\u25fc\u25fd\u25fe\u25ff\u266f\u27c0\u27c1\u27c2\u27c3\u27c4\u27d0\u27d1\u27d2\u27d3\u27d4\u27d5\u27d6\u27d7\u27d8\u27d9\u27da\u27db\u27dc\u27dd\u27de\u27df\u27e0\u27e1\u27e2\u27e3\u27e4\u27e5\u27f0\u27f1\u27f2\u27f3\u27f4\u27f5\u27f6\u27f7\u27f8\u27f9\u27fa\u27fb\u27fc\u27fd\u27fe\u27ff\u2900\u2901\u2902\u2903\u2904\u2905\u2906\u2907\u2908\u2909\u290a\u290b\u290c\u290d\u290e\u290f\u2910\u2911\u2912\u2913\u2914\u2915\u2916\u2917\u2918\u2919\u291a\u291b\u291c\u291d\u291e\u291f\u2920\u2921\u2922\u2923\u2924\u2925\u2926\u2927\u2928\u2929\u292a\u292b\u292c\u292d\u292e\u292f\u2930\u2931\u2932\u2933\u2934\u2935\u2936\u2937\u2938\u2939\u293a\u293b\u293c\u293d\u293e\u293f\u2940\u2941\u2942\u2943\u2944\u2945\u2946\u2947\u2948\u2949\u294a\u294b\u294c\u294d\u294e\u294f\u2950\u2951\u2952\u2953\u2954\u2955\u2956\u2957\u2958\u2959\u295a\u295b\u295c\u295d\u295e\u295f\u2960\u2961\u2962\u2963\u2964\u2965\u2966\u2967\u2968\u2969\u296a\u296b\u296c\u296d\u296e\u296f\u2970\u2971\u2972\u2973\u2974\u2975\u2976\u2977\u2978\u2979\u297a\u297b\u297c\u297d\u297e\u297f\u2980\u2981\u2982\u2999\u299a\u299b\u299c\u299d\u299e\u299f\u29a0\u29a1\u29a2\u29a3\u29a4\u29a5\u29a6\u29a7\u29a8\u29a9\u29aa\u29ab\u29ac\u29ad\u29ae\u29af\u29b0\u29b1\u29b2\u29b3\u29b4\u29b5\u29b6\u29b7\u29b8\u29b9\u29ba\u29bb\u29bc\u29bd\u29be\u29bf\u29c0\u29c1\u29c2\u29c3\u29c4\u29c5\u29c6\u29c7\u29c8\u29c9\u29ca\u29cb\u29cc\u29cd\u29ce\u29cf\u29d0\u29d1\u29d2\u29d3\u29d4\u29d5\u29d6\u29d7\u29dc\u29dd\u29de\u29df\u29e0\u29e1\u29e2\u29e3\u29e4\u29e5\u29e6\u29e7\u29e8\u29e9\u29ea\u29eb\u29ec\u29ed\u29ee\u29ef\u29f0\u29f1\u29f2\u29f3\u29f4\u29f5\u29f6\u29f7\u29f8\u29f9\u29fa\u29fb\u29fe\u29ff\u2a00\u2a01\u2a02\u2a03\u2a04\u2a05\u2a06\u2a07\u2a08\u2a09\u2a0a\u2a0b\u2a0c\u2a0d\u2a0e\u2a0f\u2a10\u2a11\u2a12\u2a13\u2a14\u2a15\u2a16\u2a17\u2a18\u2a19\u2a1a\u2a1b\u2a1c\u2a1d\u2a1e\u2a1f\u2a20\u2a21\u2a22\u2a23\u2a24\u2a25\u2a26\u2a27\u2a28\u2a29\u2a2a\u2a2b\u2a2c\u2a2d\u2a2e\u2a2f\u2a30\u2a31\u2a32\u2a33\u2a34\u2a35\u2a36\u2a37\u2a38\u2a39\u2a3a\u2a3b\u2a3c\u2a3d\u2a3e\u2a3f\u2a40\u2a41\u2a42\u2a43\u2a44\u2a45\u2a46\u2a47\u2a48\u2a49\u2a4a\u2a4b\u2a4c\u2a4d\u2a4e\u2a4f\u2a50\u2a51\u2a52\u2a53\u2a54\u2a55\u2a56\u2a57\u2a58\u2a59\u2a5a\u2a5b\u2a5c\u2a5d\u2a5e\u2a5f\u2a60\u2a61\u2a62\u2a63\u2a64\u2a65\u2a66\u2a67\u2a68\u2a69\u2a6a\u2a6b\u2a6c\u2a6d\u2a6e\u2a6f\u2a70\u2a71\u2a72\u2a73\u2a74\u2a75\u2a76\u2a77\u2a78\u2a79\u2a7a\u2a7b\u2a7c\u2a7d\u2a7e\u2a7f\u2a80\u2a81\u2a82\u2a83\u2a84\u2a85\u2a86\u2a87\u2a88\u2a89\u2a8a\u2a8b\u2a8c\u2a8d\u2a8e\u2a8f\u2a90\u2a91\u2a92\u2a93\u2a94\u2a95\u2a96\u2a97\u2a98\u2a99\u2a9a\u2a9b\u2a9c\u2a9d\u2a9e\u2a9f\u2aa0\u2aa1\u2aa2\u2aa3\u2aa4\u2aa5\u2aa6\u2aa7\u2aa8\u2aa9\u2aaa\u2aab\u2aac\u2aad\u2aae\u2aaf\u2ab0\u2ab1\u2ab2\u2ab3\u2ab4\u2ab5\u2ab6\u2ab7\u2ab8\u2ab9\u2aba\u2abb\u2abc\u2abd\u2abe\u2abf\u2ac0\u2ac1\u2ac2\u2ac3\u2ac4\u2ac5\u2ac6\u2ac7\u2ac8\u2ac9\u2aca\u2acb\u2acc\u2acd\u2ace\u2acf\u2ad0\u2ad1\u2ad2\u2ad3\u2ad4\u2ad5\u2ad6\u2ad7\u2ad8\u2ad9\u2ada\u2adb\u2adc\u2add\u2ade\u2adf\u2ae0\u2ae1\u2ae2\u2ae3\u2ae4\u2ae5\u2ae6\u2ae7\u2ae8\u2ae9\u2aea\u2aeb\u2aec\u2aed\u2aee\u2aef\u2af0\u2af1\u2af2\u2af3\u2af4\u2af5\u2af6\u2af7\u2af8\u2af9\u2afa\u2afb\u2afc\u2afd\u2afe\u2aff\ufb29\ufe62\ufe64\ufe65\ufe66\uff0b\uff1c\uff1d\uff1e\uff5c\uff5e\uffe2\uffe9\uffea\uffeb\uffec'
-
-So = u'\xa6\xa7\xa9\xae\xb0\xb6\u0482\u060e\u060f\u06e9\u06fd\u06fe\u09fa\u0b70\u0bf3\u0bf4\u0bf5\u0bf6\u0bf7\u0bf8\u0bfa\u0f01\u0f02\u0f03\u0f13\u0f14\u0f15\u0f16\u0f17\u0f1a\u0f1b\u0f1c\u0f1d\u0f1e\u0f1f\u0f34\u0f36\u0f38\u0fbe\u0fbf\u0fc0\u0fc1\u0fc2\u0fc3\u0fc4\u0fc5\u0fc7\u0fc8\u0fc9\u0fca\u0fcb\u0fcc\u0fcf\u1360\u1390\u1391\u1392\u1393\u1394\u1395\u1396\u1397\u1398\u1399\u1940\u19e0\u19e1\u19e2\u19e3\u19e4\u19e5\u19e6\u19e7\u19e8\u19e9\u19ea\u19eb\u19ec\u19ed\u19ee\u19ef\u19f0\u19f1\u19f2\u19f3\u19f4\u19f5\u19f6\u19f7\u19f8\u19f9\u19fa\u19fb\u19fc\u19fd\u19fe\u19ff\u2100\u2101\u2103\u2104\u2105\u2106\u2108\u2109\u2114\u2116\u2117\u2118\u211e\u211f\u2120\u2121\u2122\u2123\u2125\u2127\u2129\u212e\u2132\u213a\u213b\u214a\u214c\u2195\u2196\u2197\u2198\u2199\u219c\u219d\u219e\u219f\u21a1\u21a2\u21a4\u21a5\u21a7\u21a8\u21a9\u21aa\u21ab\u21ac\u21ad\u21af\u21b0\u21b1\u21b2\u21b3\u21b4\u21b5\u21b6\u21b7\u21b8\u21b9\u21ba\u21bb\u21bc\u21bd\u21be\u21bf\u21c0\u21c1\u21c2\u21c3\u21c4\u21c5\u21c6\u21c7\u21c8\u21c9\u21ca\u21cb\u21cc\u21cd\u21d0\u21d1\u21d3\u21d5\u21d6\u21d7\u21d8\u21d9\u21da\u21db\u21dc\u21dd\u21de\u21df\u21e0\u21e1\u21e2\u21e3\u21e4\u21e5\u21e6\u21e7\u21e8\u21e9\u21ea\u21eb\u21ec\u21ed\u21ee\u21ef\u21f0\u21f1\u21f2\u21f3\u2300\u2301\u2302\u2303\u2304\u2305\u2306\u2307\u230c\u230d\u230e\u230f\u2310\u2311\u2312\u2313\u2314\u2315\u2316\u2317\u2318\u2319\u231a\u231b\u231c\u231d\u231e\u231f\u2322\u2323\u2324\u2325\u2326\u2327\u2328\u232b\u232c\u232d\u232e\u232f\u2330\u2331\u2332\u2333\u2334\u2335\u2336\u2337\u2338\u2339\u233a\u233b\u233c\u233d\u233e\u233f\u2340\u2341\u2342\u2343\u2344\u2345\u2346\u2347\u2348\u2349\u234a\u234b\u234c\u234d\u234e\u234f\u2350\u2351\u2352\u2353\u2354\u2355\u2356\u2357\u2358\u2359\u235a\u235b\u235c\u235d\u235e\u235f\u2360\u2361\u2362\u2363\u2364\u2365\u2366\u2367\u2368\u2369\u236a\u236b\u236c\u236d\u236e\u236f\u2370\u2371\u2372\u2373\u2374\u2375\u2376\u2377\u2378\u2379\u237a\u237b\u237d\u237e\u237f\u2380\u2381\u2382\u2383\u2384\u2385\u2386\u2387\u2388\u2389\u238a\u238b\u238c\u238d\u238e\u238f\u2390\u2391\u2392\u2393\u2394\u2395\u2396\u2397\u2398\u2399\u239a\u23b7\u23b8\u23b9\u23ba\u23bb\u23bc\u23bd\u23be\u23bf\u23c0\u23c1\u23c2\u23c3\u23c4\u23c5\u23c6\u23c7\u23c8\u23c9\u23ca\u23cb\u23cc\u23cd\u23ce\u23cf\u23d0\u23d1\u23d2\u23d3\u23d4\u23d5\u23d6\u23d7\u23d8\u23d9\u23da\u23db\u2400\u2401\u2402\u2403\u2404\u2405\u2406\u2407\u2408\u2409\u240a\u240b\u240c\u240d\u240e\u240f\u2410\u2411\u2412\u2413\u2414\u2415\u2416\u2417\u2418\u2419\u241a\u241b\u241c\u241d\u241e\u241f\u2420\u2421\u2422\u2423\u2424\u2425\u2426\u2440\u2441\u2442\u2443\u2444\u2445\u2446\u2447\u2448\u2449\u244a\u249c\u249d\u249e\u249f\u24a0\u24a1\u24a2\u24a3\u24a4\u24a5\u24a6\u24a7\u24a8\u24a9\u24aa\u24ab\u24ac\u24ad\u24ae\u24af\u24b0\u24b1\u24b2\u24b3\u24b4\u24b5\u24b6\u24b7\u24b8\u24b9\u24ba\u24bb\u24bc\u24bd\u24be\u24bf\u24c0\u24c1\u24c2\u24c3\u24c4\u24c5\u24c6\u24c7\u24c8\u24c9\u24ca\u24cb\u24cc\u24cd\u24ce\u24cf\u24d0\u24d1\u24d2\u24d3\u24d4\u24d5\u24d6\u24d7\u24d8\u24d9\u24da\u24db\u24dc\u24dd\u24de\u24df\u24e0\u24e1\u24e2\u24e3\u24e4\u24e5\u24e6\u24e7\u24e8\u24e9\u2500\u2501\u2502\u2503\u2504\u2505\u2506\u2507\u2508\u2509\u250a\u250b\u250c\u250d\u250e\u250f\u2510\u2511\u2512\u2513\u2514\u2515\u2516\u2517\u2518\u2519\u251a\u251b\u251c\u251d\u251e\u251f\u2520\u2521\u2522\u2523\u2524\u2525\u2526\u2527\u2528\u2529\u252a\u252b\u252c\u252d\u252e\u252f\u2530\u2531\u2532\u2533\u2534\u2535\u2536\u2537\u2538\u2539\u253a\u253b\u253c\u253d\u253e\u253f\u2540\u2541\u2542\u2543\u2544\u2545\u2546\u2547\u2548\u2549\u254a\u254b\u254c\u254d\u254e\u254f\u2550\u2551\u2552\u2553\u2554\u2555\u2556\u2557\u2558\u2559\u255a\u255b\u255c\u255d\u255e\u255f\u2560\u2561\u2562\u2563\u2564\u2565\u2566\u2567\u2568\u2569\u256a\u256b\u256c\u256d\u256e\u256f\u2570\u2571\u2572\u2573\u2574\u2575\u2576\u2577\u2578\u2579\u257a\u257b\u257c\u257d\u257e\u257f\u2580\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588\u2589\u258a\u258b\u258c\u258d\u258e\u258f\u2590\u2591\u2592\u2593\u2594\u2595\u2596\u2597\u2598\u2599\u259a\u259b\u259c\u259d\u259e\u259f\u25a0\u25a1\u25a2\u25a3\u25a4\u25a5\u25a6\u25a7\u25a8\u25a9\u25aa\u25ab\u25ac\u25ad\u25ae\u25af\u25b0\u25b1\u25b2\u25b3\u25b4\u25b5\u25b6\u25b8\u25b9\u25ba\u25bb\u25bc\u25bd\u25be\u25bf\u25c0\u25c2\u25c3\u25c4\u25c5\u25c6\u25c7\u25c8\u25c9\u25ca\u25cb\u25cc\u25cd\u25ce\u25cf\u25d0\u25d1\u25d2\u25d3\u25d4\u25d5\u25d6\u25d7\u25d8\u25d9\u25da\u25db\u25dc\u25dd\u25de\u25df\u25e0\u25e1\u25e2\u25e3\u25e4\u25e5\u25e6\u25e7\u25e8\u25e9\u25ea\u25eb\u25ec\u25ed\u25ee\u25ef\u25f0\u25f1\u25f2\u25f3\u25f4\u25f5\u25f6\u25f7\u2600\u2601\u2602\u2603\u2604\u2605\u2606\u2607\u2608\u2609\u260a\u260b\u260c\u260d\u260e\u260f\u2610\u2611\u2612\u2613\u2614\u2615\u2616\u2617\u2618\u2619\u261a\u261b\u261c\u261d\u261e\u261f\u2620\u2621\u2622\u2623\u2624\u2625\u2626\u2627\u2628\u2629\u262a\u262b\u262c\u262d\u262e\u262f\u2630\u2631\u2632\u2633\u2634\u2635\u2636\u2637\u2638\u2639\u263a\u263b\u263c\u263d\u263e\u263f\u2640\u2641\u2642\u2643\u2644\u2645\u2646\u2647\u2648\u2649\u264a\u264b\u264c\u264d\u264e\u264f\u2650\u2651\u2652\u2653\u2654\u2655\u2656\u2657\u2658\u2659\u265a\u265b\u265c\u265d\u265e\u265f\u2660\u2661\u2662\u2663\u2664\u2665\u2666\u2667\u2668\u2669\u266a\u266b\u266c\u266d\u266e\u2670\u2671\u2672\u2673\u2674\u2675\u2676\u2677\u2678\u2679\u267a\u267b\u267c\u267d\u267e\u267f\u2680\u2681\u2682\u2683\u2684\u2685\u2686\u2687\u2688\u2689\u268a\u268b\u268c\u268d\u268e\u268f\u2690\u2691\u2692\u2693\u2694\u2695\u2696\u2697\u2698\u2699\u269a\u269b\u269c\u26a0\u26a1\u26a2\u26a3\u26a4\u26a5\u26a6\u26a7\u26a8\u26a9\u26aa\u26ab\u26ac\u26ad\u26ae\u26af\u26b0\u26b1\u2701\u2702\u2703\u2704\u2706\u2707\u2708\u2709\u270c\u270d\u270e\u270f\u2710\u2711\u2712\u2713\u2714\u2715\u2716\u2717\u2718\u2719\u271a\u271b\u271c\u271d\u271e\u271f\u2720\u2721\u2722\u2723\u2724\u2725\u2726\u2727\u2729\u272a\u272b\u272c\u272d\u272e\u272f\u2730\u2731\u2732\u2733\u2734\u2735\u2736\u2737\u2738\u2739\u273a\u273b\u273c\u273d\u273e\u273f\u2740\u2741\u2742\u2743\u2744\u2745\u2746\u2747\u2748\u2749\u274a\u274b\u274d\u274f\u2750\u2751\u2752\u2756\u2758\u2759\u275a\u275b\u275c\u275d\u275e\u2761\u2762\u2763\u2764\u2765\u2766\u2767\u2794\u2798\u2799\u279a\u279b\u279c\u279d\u279e\u279f\u27a0\u27a1\u27a2\u27a3\u27a4\u27a5\u27a6\u27a7\u27a8\u27a9\u27aa\u27ab\u27ac\u27ad\u27ae\u27af\u27b1\u27b2\u27b3\u27b4\u27b5\u27b6\u27b7\u27b8\u27b9\u27ba\u27bb\u27bc\u27bd\u27be\u2800\u2801\u2802\u2803\u2804\u2805\u2806\u2807\u2808\u2809\u280a\u280b\u280c\u280d\u280e\u280f\u2810\u2811\u2812\u2813\u2814\u2815\u2816\u2817\u2818\u2819\u281a\u281b\u281c\u281d\u281e\u281f\u2820\u2821\u2822\u2823\u2824\u2825\u2826\u2827\u2828\u2829\u282a\u282b\u282c\u282d\u282e\u282f\u2830\u2831\u2832\u2833\u2834\u2835\u2836\u2837\u2838\u2839\u283a\u283b\u283c\u283d\u283e\u283f\u2840\u2841\u2842\u2843\u2844\u2845\u2846\u2847\u2848\u2849\u284a\u284b\u284c\u284d\u284e\u284f\u2850\u2851\u2852\u2853\u2854\u2855\u2856\u2857\u2858\u2859\u285a\u285b\u285c\u285d\u285e\u285f\u2860\u2861\u2862\u2863\u2864\u2865\u2866\u2867\u2868\u2869\u286a\u286b\u286c\u286d\u286e\u286f\u2870\u2871\u2872\u2873\u2874\u2875\u2876\u2877\u2878\u2879\u287a\u287b\u287c\u287d\u287e\u287f\u2880\u2881\u2882\u2883\u2884\u2885\u2886\u2887\u2888\u2889\u288a\u288b\u288c\u288d\u288e\u288f\u2890\u2891\u2892\u2893\u2894\u2895\u2896\u2897\u2898\u2899\u289a\u289b\u289c\u289d\u289e\u289f\u28a0\u28a1\u28a2\u28a3\u28a4\u28a5\u28a6\u28a7\u28a8\u28a9\u28aa\u28ab\u28ac\u28ad\u28ae\u28af\u28b0\u28b1\u28b2\u28b3\u28b4\u28b5\u28b6\u28b7\u28b8\u28b9\u28ba\u28bb\u28bc\u28bd\u28be\u28bf\u28c0\u28c1\u28c2\u28c3\u28c4\u28c5\u28c6\u28c7\u28c8\u28c9\u28ca\u28cb\u28cc\u28cd\u28ce\u28cf\u28d0\u28d1\u28d2\u28d3\u28d4\u28d5\u28d6\u28d7\u28d8\u28d9\u28da\u28db\u28dc\u28dd\u28de\u28df\u28e0\u28e1\u28e2\u28e3\u28e4\u28e5\u28e6\u28e7\u28e8\u28e9\u28ea\u28eb\u28ec\u28ed\u28ee\u28ef\u28f0\u28f1\u28f2\u28f3\u28f4\u28f5\u28f6\u28f7\u28f8\u28f9\u28fa\u28fb\u28fc\u28fd\u28fe\u28ff\u2b00\u2b01\u2b02\u2b03\u2b04\u2b05\u2b06\u2b07\u2b08\u2b09\u2b0a\u2b0b\u2b0c\u2b0d\u2b0e\u2b0f\u2b10\u2b11\u2b12\u2b13\u2ce5\u2ce6\u2ce7\u2ce8\u2ce9\u2cea\u2e80\u2e81\u2e82\u2e83\u2e84\u2e85\u2e86\u2e87\u2e88\u2e89\u2e8a\u2e8b\u2e8c\u2e8d\u2e8e\u2e8f\u2e90\u2e91\u2e92\u2e93\u2e94\u2e95\u2e96\u2e97\u2e98\u2e99\u2e9b\u2e9c\u2e9d\u2e9e\u2e9f\u2ea0\u2ea1\u2ea2\u2ea3\u2ea4\u2ea5\u2ea6\u2ea7\u2ea8\u2ea9\u2eaa\u2eab\u2eac\u2ead\u2eae\u2eaf\u2eb0\u2eb1\u2eb2\u2eb3\u2eb4\u2eb5\u2eb6\u2eb7\u2eb8\u2eb9\u2eba\u2ebb\u2ebc\u2ebd\u2ebe\u2ebf\u2ec0\u2ec1\u2ec2\u2ec3\u2ec4\u2ec5\u2ec6\u2ec7\u2ec8\u2ec9\u2eca\u2ecb\u2ecc\u2ecd\u2ece\u2ecf\u2ed0\u2ed1\u2ed2\u2ed3\u2ed4\u2ed5\u2ed6\u2ed7\u2ed8\u2ed9\u2eda\u2edb\u2edc\u2edd\u2ede\u2edf\u2ee0\u2ee1\u2ee2\u2ee3\u2ee4\u2ee5\u2ee6\u2ee7\u2ee8\u2ee9\u2eea\u2eeb\u2eec\u2eed\u2eee\u2eef\u2ef0\u2ef1\u2ef2\u2ef3\u2f00\u2f01\u2f02\u2f03\u2f04\u2f05\u2f06\u2f07\u2f08\u2f09\u2f0a\u2f0b\u2f0c\u2f0d\u2f0e\u2f0f\u2f10\u2f11\u2f12\u2f13\u2f14\u2f15\u2f16\u2f17\u2f18\u2f19\u2f1a\u2f1b\u2f1c\u2f1d\u2f1e\u2f1f\u2f20\u2f21\u2f22\u2f23\u2f24\u2f25\u2f26\u2f27\u2f28\u2f29\u2f2a\u2f2b\u2f2c\u2f2d\u2f2e\u2f2f\u2f30\u2f31\u2f32\u2f33\u2f34\u2f35\u2f36\u2f37\u2f38\u2f39\u2f3a\u2f3b\u2f3c\u2f3d\u2f3e\u2f3f\u2f40\u2f41\u2f42\u2f43\u2f44\u2f45\u2f46\u2f47\u2f48\u2f49\u2f4a\u2f4b\u2f4c\u2f4d\u2f4e\u2f4f\u2f50\u2f51\u2f52\u2f53\u2f54\u2f55\u2f56\u2f57\u2f58\u2f59\u2f5a\u2f5b\u2f5c\u2f5d\u2f5e\u2f5f\u2f60\u2f61\u2f62\u2f63\u2f64\u2f65\u2f66\u2f67\u2f68\u2f69\u2f6a\u2f6b\u2f6c\u2f6d\u2f6e\u2f6f\u2f70\u2f71\u2f72\u2f73\u2f74\u2f75\u2f76\u2f77\u2f78\u2f79\u2f7a\u2f7b\u2f7c\u2f7d\u2f7e\u2f7f\u2f80\u2f81\u2f82\u2f83\u2f84\u2f85\u2f86\u2f87\u2f88\u2f89\u2f8a\u2f8b\u2f8c\u2f8d\u2f8e\u2f8f\u2f90\u2f91\u2f92\u2f93\u2f94\u2f95\u2f96\u2f97\u2f98\u2f99\u2f9a\u2f9b\u2f9c\u2f9d\u2f9e\u2f9f\u2fa0\u2fa1\u2fa2\u2fa3\u2fa4\u2fa5\u2fa6\u2fa7\u2fa8\u2fa9\u2faa\u2fab\u2fac\u2fad\u2fae\u2faf\u2fb0\u2fb1\u2fb2\u2fb3\u2fb4\u2fb5\u2fb6\u2fb7\u2fb8\u2fb9\u2fba\u2fbb\u2fbc\u2fbd\u2fbe\u2fbf\u2fc0\u2fc1\u2fc2\u2fc3\u2fc4\u2fc5\u2fc6\u2fc7\u2fc8\u2fc9\u2fca\u2fcb\u2fcc\u2fcd\u2fce\u2fcf\u2fd0\u2fd1\u2fd2\u2fd3\u2fd4\u2fd5\u2ff0\u2ff1\u2ff2\u2ff3\u2ff4\u2ff5\u2ff6\u2ff7\u2ff8\u2ff9\u2ffa\u2ffb\u3004\u3012\u3013\u3020\u3036\u3037\u303e\u303f\u3190\u3191\u3196\u3197\u3198\u3199\u319a\u319b\u319c\u319d\u319e\u319f\u31c0\u31c1\u31c2\u31c3\u31c4\u31c5\u31c6\u31c7\u31c8\u31c9\u31ca\u31cb\u31cc\u31cd\u31ce\u31cf\u3200\u3201\u3202\u3203\u3204\u3205\u3206\u3207\u3208\u3209\u320a\u320b\u320c\u320d\u320e\u320f\u3210\u3211\u3212\u3213\u3214\u3215\u3216\u3217\u3218\u3219\u321a\u321b\u321c\u321d\u321e\u322a\u322b\u322c\u322d\u322e\u322f\u3230\u3231\u3232\u3233\u3234\u3235\u3236\u3237\u3238\u3239\u323a\u323b\u323c\u323d\u323e\u323f\u3240\u3241\u3242\u3243\u3250\u3260\u3261\u3262\u3263\u3264\u3265\u3266\u3267\u3268\u3269\u326a\u326b\u326c\u326d\u326e\u326f\u3270\u3271\u3272\u3273\u3274\u3275\u3276\u3277\u3278\u3279\u327a\u327b\u327c\u327d\u327e\u327f\u328a\u328b\u328c\u328d\u328e\u328f\u3290\u3291\u3292\u3293\u3294\u3295\u3296\u3297\u3298\u3299\u329a\u329b\u329c\u329d\u329e\u329f\u32a0\u32a1\u32a2\u32a3\u32a4\u32a5\u32a6\u32a7\u32a8\u32a9\u32aa\u32ab\u32ac\u32ad\u32ae\u32af\u32b0\u32c0\u32c1\u32c2\u32c3\u32c4\u32c5\u32c6\u32c7\u32c8\u32c9\u32ca\u32cb\u32cc\u32cd\u32ce\u32cf\u32d0\u32d1\u32d2\u32d3\u32d4\u32d5\u32d6\u32d7\u32d8\u32d9\u32da\u32db\u32dc\u32dd\u32de\u32df\u32e0\u32e1\u32e2\u32e3\u32e4\u32e5\u32e6\u32e7\u32e8\u32e9\u32ea\u32eb\u32ec\u32ed\u32ee\u32ef\u32f0\u32f1\u32f2\u32f3\u32f4\u32f5\u32f6\u32f7\u32f8\u32f9\u32fa\u32fb\u32fc\u32fd\u32fe\u3300\u3301\u3302\u3303\u3304\u3305\u3306\u3307\u3308\u3309\u330a\u330b\u330c\u330d\u330e\u330f\u3310\u3311\u3312\u3313\u3314\u3315\u3316\u3317\u3318\u3319\u331a\u331b\u331c\u331d\u331e\u331f\u3320\u3321\u3322\u3323\u3324\u3325\u3326\u3327\u3328\u3329\u332a\u332b\u332c\u332d\u332e\u332f\u3330\u3331\u3332\u3333\u3334\u3335\u3336\u3337\u3338\u3339\u333a\u333b\u333c\u333d\u333e\u333f\u3340\u3341\u3342\u3343\u3344\u3345\u3346\u3347\u3348\u3349\u334a\u334b\u334c\u334d\u334e\u334f\u3350\u3351\u3352\u3353\u3354\u3355\u3356\u3357\u3358\u3359\u335a\u335b\u335c\u335d\u335e\u335f\u3360\u3361\u3362\u3363\u3364\u3365\u3366\u3367\u3368\u3369\u336a\u336b\u336c\u336d\u336e\u336f\u3370\u3371\u3372\u3373\u3374\u3375\u3376\u3377\u3378\u3379\u337a\u337b\u337c\u337d\u337e\u337f\u3380\u3381\u3382\u3383\u3384\u3385\u3386\u3387\u3388\u3389\u338a\u338b\u338c\u338d\u338e\u338f\u3390\u3391\u3392\u3393\u3394\u3395\u3396\u3397\u3398\u3399\u339a\u339b\u339c\u339d\u339e\u339f\u33a0\u33a1\u33a2\u33a3\u33a4\u33a5\u33a6\u33a7\u33a8\u33a9\u33aa\u33ab\u33ac\u33ad\u33ae\u33af\u33b0\u33b1\u33b2\u33b3\u33b4\u33b5\u33b6\u33b7\u33b8\u33b9\u33ba\u33bb\u33bc\u33bd\u33be\u33bf\u33c0\u33c1\u33c2\u33c3\u33c4\u33c5\u33c6\u33c7\u33c8\u33c9\u33ca\u33cb\u33cc\u33cd\u33ce\u33cf\u33d0\u33d1\u33d2\u33d3\u33d4\u33d5\u33d6\u33d7\u33d8\u33d9\u33da\u33db\u33dc\u33dd\u33de\u33df\u33e0\u33e1\u33e2\u33e3\u33e4\u33e5\u33e6\u33e7\u33e8\u33e9\u33ea\u33eb\u33ec\u33ed\u33ee\u33ef\u33f0\u33f1\u33f2\u33f3\u33f4\u33f5\u33f6\u33f7\u33f8\u33f9\u33fa\u33fb\u33fc\u33fd\u33fe\u33ff\u4dc0\u4dc1\u4dc2\u4dc3\u4dc4\u4dc5\u4dc6\u4dc7\u4dc8\u4dc9\u4dca\u4dcb\u4dcc\u4dcd\u4dce\u4dcf\u4dd0\u4dd1\u4dd2\u4dd3\u4dd4\u4dd5\u4dd6\u4dd7\u4dd8\u4dd9\u4dda\u4ddb\u4ddc\u4ddd\u4dde\u4ddf\u4de0\u4de1\u4de2\u4de3\u4de4\u4de5\u4de6\u4de7\u4de8\u4de9\u4dea\u4deb\u4dec\u4ded\u4dee\u4def\u4df0\u4df1\u4df2\u4df3\u4df4\u4df5\u4df6\u4df7\u4df8\u4df9\u4dfa\u4dfb\u4dfc\u4dfd\u4dfe\u4dff\ua490\ua491\ua492\ua493\ua494\ua495\ua496\ua497\ua498\ua499\ua49a\ua49b\ua49c\ua49d\ua49e\ua49f\ua4a0\ua4a1\ua4a2\ua4a3\ua4a4\ua4a5\ua4a6\ua4a7\ua4a8\ua4a9\ua4aa\ua4ab\ua4ac\ua4ad\ua4ae\ua4af\ua4b0\ua4b1\ua4b2\ua4b3\ua4b4\ua4b5\ua4b6\ua4b7\ua4b8\ua4b9\ua4ba\ua4bb\ua4bc\ua4bd\ua4be\ua4bf\ua4c0\ua4c1\ua4c2\ua4c3\ua4c4\ua4c5\ua4c6\ua828\ua829\ua82a\ua82b\ufdfd\uffe4\uffe8\uffed\uffee\ufffc\ufffd'
-
-Zl = u'\u2028'
-
-Zp = u'\u2029'
-
-Zs = u' \xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
-
-cats = ['Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', 'Mc', 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po', 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs']
-
-def combine(*args):
- return u''.join([globals()[cat] for cat in args])
-
-xid_start = u'\u0041-\u005A\u005F\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u01BA\u01BB\u01BC-\u01BF\u01C0-\u01C3\u01C4-\u0241\u0250-\u02AF\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EE\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03F5\u03F7-\u0481\u048A-\u04CE\u04D0-\u04F9\u0500-\u050F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0621-\u063A\u0640\u0641-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06E5-\u06E6\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u076D\u0780-\u07A5\u07B1\u0904-\u0939\u093D\u0950\u0958-\u0961\u097D\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D60-\u0D61\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E40-\u0E45\u0E46\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDD\u0F00\u0F40-\u0F47\u0F49-\u0F6A\u0F88-\u0F8B\u1000-\u1021\u1023-\u1027\u1029-\u102A\u1050-\u1055\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1159\u115F-\u11A2\u11A8-\u11F9\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u1676\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1842\u1843\u1844-\u1877\u1880-\u18A8\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19A9\u19C1-\u19C7\u1A00-\u1A16\u1D00-\u1D2B\u1D2C-\u1D61\u1D62-\u1D77\u1D78\u1D79-\u1D9A\u1D9B-\u1DBF\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u2094\u2102\u2107\u210A-\u2113\u2115\u2118\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212E\u212F-\u2131\u2133-\u2134\u2135-\u2138\u2139\u213C-\u213F\u2145-\u2149\u2160-\u2183\u2C00-\u2C2E\u2C30-\u2C5E\u2C80-\u2CE4\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005\u3006\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303A\u303B\u303C\u3041-\u3096\u309D-\u309E\u309F\u30A1-\u30FA\u30FC-\u30FE\u30FF\u3105-\u312C\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FBB\uA000-\uA014\uA015\uA016-\uA48C\uA800-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uAC00-\uD7A3\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFC5D\uFC64-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDF9\uFE71\uFE73\uFE77\uFE79\uFE7B\uFE7D\uFE7F-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFF6F\uFF70\uFF71-\uFF9D\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC'
-
-xid_continue = u'\u0030-\u0039\u0041-\u005A\u005F\u0061-\u007A\u00AA\u00B5\u00B7\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u01BA\u01BB\u01BC-\u01BF\u01C0-\u01C3\u01C4-\u0241\u0250-\u02AF\u02B0-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EE\u0300-\u036F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03CE\u03D0-\u03F5\u03F7-\u0481\u0483-\u0486\u048A-\u04CE\u04D0-\u04F9\u0500-\u050F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05B9\u05BB-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u0615\u0621-\u063A\u0640\u0641-\u064A\u064B-\u065E\u0660-\u0669\u066E-\u066F\u0670\u0671-\u06D3\u06D5\u06D6-\u06DC\u06DF-\u06E4\u06E5-\u06E6\u06E7-\u06E8\u06EA-\u06ED\u06EE-\u06EF\u06F0-\u06F9\u06FA-\u06FC\u06FF\u0710\u0711\u0712-\u072F\u0730-\u074A\u074D-\u076D\u0780-\u07A5\u07A6-\u07B0\u07B1\u0901-\u0902\u0903\u0904-\u0939\u093C\u093D\u093E-\u0940\u0941-\u0948\u0949-\u094C\u094D\u0950\u0951-\u0954\u0958-\u0961\u0962-\u0963\u0966-\u096F\u097D\u0981\u0982-\u0983\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC\u09BD\u09BE-\u09C0\u09C1-\u09C4\u09C7-\u09C8\u09CB-\u09CC\u09CD\u09CE\u09D7\u09DC-\u09DD\u09DF-\u09E1\u09E2-\u09E3\u09E6-\u09EF\u09F0-\u09F1\u0A01-\u0A02\u0A03\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A3C\u0A3E-\u0A40\u0A41-\u0A42\u0A47-\u0A48\u0A4B-\u0A4D\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A70-\u0A71\u0A72-\u0A74\u0A81-\u0A82\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABC\u0ABD\u0ABE-\u0AC0\u0AC1-\u0AC5\u0AC7-\u0AC8\u0AC9\u0ACB-\u0ACC\u0ACD\u0AD0\u0AE0-\u0AE1\u0AE2-\u0AE3\u0AE6-\u0AEF\u0B01\u0B02-\u0B03\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3C\u0B3D\u0B3E\u0B3F\u0B40\u0B41-\u0B43\u0B47-\u0B48\u0B4B-\u0B4C\u0B4D\u0B56\u0B57\u0B5C-\u0B5D\u0B5F-\u0B61\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BBF\u0BC0\u0BC1-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCC\u0BCD\u0BD7\u0BE6-\u0BEF\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3E-\u0C40\u0C41-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55-\u0C56\u0C60-\u0C61\u0C66-\u0C6F\u0C82-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC\u0CBD\u0CBE\u0CBF\u0CC0-\u0CC4\u0CC6\u0CC7-\u0CC8\u0CCA-\u0CCB\u0CCC-\u0CCD\u0CD5-\u0CD6\u0CDE\u0CE0-\u0CE1\u0CE6-\u0CEF\u0D02-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D28\u0D2A-\u0D39\u0D3E-\u0D40\u0D41-\u0D43\u0D46-\u0D48\u0D4A-\u0D4C\u0D4D\u0D57\u0D60-\u0D61\u0D66-\u0D6F\u0D82-\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD1\u0DD2-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2-\u0DF3\u0E01-\u0E30\u0E31\u0E32-\u0E33\u0E34-\u0E3A\u0E40-\u0E45\u0E46\u0E47-\u0E4E\u0E50-\u0E59\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB1\u0EB2-\u0EB3\u0EB4-\u0EB9\u0EBB-\u0EBC\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDD\u0F00\u0F18-\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F3F\u0F40-\u0F47\u0F49-\u0F6A\u0F71-\u0F7E\u0F7F\u0F80-\u0F84\u0F86-\u0F87\u0F88-\u0F8B\u0F90-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1021\u1023-\u1027\u1029-\u102A\u102C\u102D-\u1030\u1031\u1032\u1036-\u1037\u1038\u1039\u1040-\u1049\u1050-\u1055\u1056-\u1057\u1058-\u1059\u10A0-\u10C5\u10D0-\u10FA\u10FC\u1100-\u1159\u115F-\u11A2\u11A8-\u11F9\u1200-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u1676\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1712-\u1714\u1720-\u1731\u1732-\u1734\u1740-\u1751\u1752-\u1753\u1760-\u176C\u176E-\u1770\u1772-\u1773\u1780-\u17B3\u17B6\u17B7-\u17BD\u17BE-\u17C5\u17C6\u17C7-\u17C8\u17C9-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1842\u1843\u1844-\u1877\u1880-\u18A8\u18A9\u1900-\u191C\u1920-\u1922\u1923-\u1926\u1927-\u1928\u1929-\u192B\u1930-\u1931\u1932\u1933-\u1938\u1939-\u193B\u1946-\u194F\u1950-\u196D\u1970-\u1974\u1980-\u19A9\u19B0-\u19C0\u19C1-\u19C7\u19C8-\u19C9\u19D0-\u19D9\u1A00-\u1A16\u1A17-\u1A18\u1A19-\u1A1B\u1D00-\u1D2B\u1D2C-\u1D61\u1D62-\u1D77\u1D78\u1D79-\u1D9A\u1D9B-\u1DBF\u1DC0-\u1DC3\u1E00-\u1E9B\u1EA0-\u1EF9\u1F00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F-\u2040\u2054\u2071\u207F\u2090-\u2094\u20D0-\u20DC\u20E1\u20E5-\u20EB\u2102\u2107\u210A-\u2113\u2115\u2118\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212E\u212F-\u2131\u2133-\u2134\u2135-\u2138\u2139\u213C-\u213F\u2145-\u2149\u2160-\u2183\u2C00-\u2C2E\u2C30-\u2C5E\u2C80-\u2CE4\u2D00-\u2D25\u2D30-\u2D65\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005\u3006\u3007\u3021-\u3029\u302A-\u302F\u3031-\u3035\u3038-\u303A\u303B\u303C\u3041-\u3096\u3099-\u309A\u309D-\u309E\u309F\u30A1-\u30FA\u30FC-\u30FE\u30FF\u3105-\u312C\u3131-\u318E\u31A0-\u31B7\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FBB\uA000-\uA014\uA015\uA016-\uA48C\uA800-\uA801\uA802\uA803-\uA805\uA806\uA807-\uA80A\uA80B\uA80C-\uA822\uA823-\uA824\uA825-\uA826\uA827\uAC00-\uD7A3\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1E\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFC5D\uFC64-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDF9\uFE00-\uFE0F\uFE20-\uFE23\uFE33-\uFE34\uFE4D-\uFE4F\uFE71\uFE73\uFE77\uFE79\uFE7B\uFE7D\uFE7F-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFF6F\uFF70\uFF71-\uFF9D\uFF9E-\uFF9F\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC'
-
-def allexcept(*args):
- newcats = cats[:]
- for arg in args:
- newcats.remove(arg)
- return u''.join([globals()[cat] for cat in newcats])
-
-if __name__ == '__main__':
- import unicodedata
-
- categories = {}
-
- f = open(__file__.rstrip('co'))
- try:
- content = f.read()
- finally:
- f.close()
-
- header = content[:content.find('Cc =')]
- footer = content[content.find("def combine("):]
-
- for code in range(65535):
- c = unichr(code)
- cat = unicodedata.category(c)
- categories.setdefault(cat, []).append(c)
-
- f = open(__file__, 'w')
- f.write(header)
-
- for cat in sorted(categories):
- val = u''.join(categories[cat])
- if cat == 'Cs':
- # Jython can't handle isolated surrogates
- f.write("""\
-try:
- Cs = eval(r"%r")
-except UnicodeDecodeError:
- Cs = '' # Jython can't handle isolated surrogates\n\n""" % val)
- else:
- f.write('%s = %r\n\n' % (cat, val))
- f.write('cats = %r\n\n' % sorted(categories.keys()))
-
- f.write(footer)
- f.close()
diff --git a/module/lib/jinja2/bccache.py b/module/lib/jinja2/bccache.py
deleted file mode 100644
index 1e2236c3a..000000000
--- a/module/lib/jinja2/bccache.py
+++ /dev/null
@@ -1,280 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.bccache
- ~~~~~~~~~~~~~~
-
- This module implements the bytecode cache system Jinja is optionally
- using. This is useful if you have very complex template situations and
- the compiliation of all those templates slow down your application too
- much.
-
- Situations where this is useful are often forking web applications that
- are initialized on the first request.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD.
-"""
-from os import path, listdir
-import marshal
-import tempfile
-import cPickle as pickle
-import fnmatch
-from cStringIO import StringIO
-try:
- from hashlib import sha1
-except ImportError:
- from sha import new as sha1
-from jinja2.utils import open_if_exists
-
-
-bc_version = 1
-bc_magic = 'j2'.encode('ascii') + pickle.dumps(bc_version, 2)
-
-
-class Bucket(object):
- """Buckets are used to store the bytecode for one template. It's created
- and initialized by the bytecode cache and passed to the loading functions.
-
- The buckets get an internal checksum from the cache assigned and use this
- to automatically reject outdated cache material. Individual bytecode
- cache subclasses don't have to care about cache invalidation.
- """
-
- def __init__(self, environment, key, checksum):
- self.environment = environment
- self.key = key
- self.checksum = checksum
- self.reset()
-
- def reset(self):
- """Resets the bucket (unloads the bytecode)."""
- self.code = None
-
- def load_bytecode(self, f):
- """Loads bytecode from a file or file like object."""
- # make sure the magic header is correct
- magic = f.read(len(bc_magic))
- if magic != bc_magic:
- self.reset()
- return
- # the source code of the file changed, we need to reload
- checksum = pickle.load(f)
- if self.checksum != checksum:
- self.reset()
- return
- # now load the code. Because marshal is not able to load
- # from arbitrary streams we have to work around that
- if isinstance(f, file):
- self.code = marshal.load(f)
- else:
- self.code = marshal.loads(f.read())
-
- def write_bytecode(self, f):
- """Dump the bytecode into the file or file like object passed."""
- if self.code is None:
- raise TypeError('can\'t write empty bucket')
- f.write(bc_magic)
- pickle.dump(self.checksum, f, 2)
- if isinstance(f, file):
- marshal.dump(self.code, f)
- else:
- f.write(marshal.dumps(self.code))
-
- def bytecode_from_string(self, string):
- """Load bytecode from a string."""
- self.load_bytecode(StringIO(string))
-
- def bytecode_to_string(self):
- """Return the bytecode as string."""
- out = StringIO()
- self.write_bytecode(out)
- return out.getvalue()
-
-
-class BytecodeCache(object):
- """To implement your own bytecode cache you have to subclass this class
- and override :meth:`load_bytecode` and :meth:`dump_bytecode`. Both of
- these methods are passed a :class:`~jinja2.bccache.Bucket`.
-
- A very basic bytecode cache that saves the bytecode on the file system::
-
- from os import path
-
- class MyCache(BytecodeCache):
-
- def __init__(self, directory):
- self.directory = directory
-
- def load_bytecode(self, bucket):
- filename = path.join(self.directory, bucket.key)
- if path.exists(filename):
- with open(filename, 'rb') as f:
- bucket.load_bytecode(f)
-
- def dump_bytecode(self, bucket):
- filename = path.join(self.directory, bucket.key)
- with open(filename, 'wb') as f:
- bucket.write_bytecode(f)
-
- A more advanced version of a filesystem based bytecode cache is part of
- Jinja2.
- """
-
- def load_bytecode(self, bucket):
- """Subclasses have to override this method to load bytecode into a
- bucket. If they are not able to find code in the cache for the
- bucket, it must not do anything.
- """
- raise NotImplementedError()
-
- def dump_bytecode(self, bucket):
- """Subclasses have to override this method to write the bytecode
- from a bucket back to the cache. If it unable to do so it must not
- fail silently but raise an exception.
- """
- raise NotImplementedError()
-
- def clear(self):
- """Clears the cache. This method is not used by Jinja2 but should be
- implemented to allow applications to clear the bytecode cache used
- by a particular environment.
- """
-
- def get_cache_key(self, name, filename=None):
- """Returns the unique hash key for this template name."""
- hash = sha1(name.encode('utf-8'))
- if filename is not None:
- if isinstance(filename, unicode):
- filename = filename.encode('utf-8')
- hash.update('|' + filename)
- return hash.hexdigest()
-
- def get_source_checksum(self, source):
- """Returns a checksum for the source."""
- return sha1(source.encode('utf-8')).hexdigest()
-
- def get_bucket(self, environment, name, filename, source):
- """Return a cache bucket for the given template. All arguments are
- mandatory but filename may be `None`.
- """
- key = self.get_cache_key(name, filename)
- checksum = self.get_source_checksum(source)
- bucket = Bucket(environment, key, checksum)
- self.load_bytecode(bucket)
- return bucket
-
- def set_bucket(self, bucket):
- """Put the bucket into the cache."""
- self.dump_bytecode(bucket)
-
-
-class FileSystemBytecodeCache(BytecodeCache):
- """A bytecode cache that stores bytecode on the filesystem. It accepts
- two arguments: The directory where the cache items are stored and a
- pattern string that is used to build the filename.
-
- If no directory is specified the system temporary items folder is used.
-
- The pattern can be used to have multiple separate caches operate on the
- same directory. The default pattern is ``'__jinja2_%s.cache'``. ``%s``
- is replaced with the cache key.
-
- >>> bcc = FileSystemBytecodeCache('/tmp/jinja_cache', '%s.cache')
-
- This bytecode cache supports clearing of the cache using the clear method.
- """
-
- def __init__(self, directory=None, pattern='__jinja2_%s.cache'):
- if directory is None:
- directory = tempfile.gettempdir()
- self.directory = directory
- self.pattern = pattern
-
- def _get_cache_filename(self, bucket):
- return path.join(self.directory, self.pattern % bucket.key)
-
- def load_bytecode(self, bucket):
- f = open_if_exists(self._get_cache_filename(bucket), 'rb')
- if f is not None:
- try:
- bucket.load_bytecode(f)
- finally:
- f.close()
-
- def dump_bytecode(self, bucket):
- f = open(self._get_cache_filename(bucket), 'wb')
- try:
- bucket.write_bytecode(f)
- finally:
- f.close()
-
- def clear(self):
- # imported lazily here because google app-engine doesn't support
- # write access on the file system and the function does not exist
- # normally.
- from os import remove
- files = fnmatch.filter(listdir(self.directory), self.pattern % '*')
- for filename in files:
- try:
- remove(path.join(self.directory, filename))
- except OSError:
- pass
-
-
-class MemcachedBytecodeCache(BytecodeCache):
- """This class implements a bytecode cache that uses a memcache cache for
- storing the information. It does not enforce a specific memcache library
- (tummy's memcache or cmemcache) but will accept any class that provides
- the minimal interface required.
-
- Libraries compatible with this class:
-
- - `werkzeug <http://werkzeug.pocoo.org/>`_.contrib.cache
- - `python-memcached <http://www.tummy.com/Community/software/python-memcached/>`_
- - `cmemcache <http://gijsbert.org/cmemcache/>`_
-
- (Unfortunately the django cache interface is not compatible because it
- does not support storing binary data, only unicode. You can however pass
- the underlying cache client to the bytecode cache which is available
- as `django.core.cache.cache._client`.)
-
- The minimal interface for the client passed to the constructor is this:
-
- .. class:: MinimalClientInterface
-
- .. method:: set(key, value[, timeout])
-
- Stores the bytecode in the cache. `value` is a string and
- `timeout` the timeout of the key. If timeout is not provided
- a default timeout or no timeout should be assumed, if it's
- provided it's an integer with the number of seconds the cache
- item should exist.
-
- .. method:: get(key)
-
- Returns the value for the cache key. If the item does not
- exist in the cache the return value must be `None`.
-
- The other arguments to the constructor are the prefix for all keys that
- is added before the actual cache key and the timeout for the bytecode in
- the cache system. We recommend a high (or no) timeout.
-
- This bytecode cache does not support clearing of used items in the cache.
- The clear method is a no-operation function.
- """
-
- def __init__(self, client, prefix='jinja2/bytecode/', timeout=None):
- self.client = client
- self.prefix = prefix
- self.timeout = timeout
-
- def load_bytecode(self, bucket):
- code = self.client.get(self.prefix + bucket.key)
- if code is not None:
- bucket.bytecode_from_string(code)
-
- def dump_bytecode(self, bucket):
- args = (self.prefix + bucket.key, bucket.bytecode_to_string())
- if self.timeout is not None:
- args += (self.timeout,)
- self.client.set(*args)
diff --git a/module/lib/jinja2/compiler.py b/module/lib/jinja2/compiler.py
deleted file mode 100644
index 57641596a..000000000
--- a/module/lib/jinja2/compiler.py
+++ /dev/null
@@ -1,1640 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.compiler
- ~~~~~~~~~~~~~~~
-
- Compiles nodes into python code.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-from cStringIO import StringIO
-from itertools import chain
-from copy import deepcopy
-from jinja2 import nodes
-from jinja2.nodes import EvalContext
-from jinja2.visitor import NodeVisitor, NodeTransformer
-from jinja2.exceptions import TemplateAssertionError
-from jinja2.utils import Markup, concat, escape, is_python_keyword, next
-
-
-operators = {
- 'eq': '==',
- 'ne': '!=',
- 'gt': '>',
- 'gteq': '>=',
- 'lt': '<',
- 'lteq': '<=',
- 'in': 'in',
- 'notin': 'not in'
-}
-
-try:
- exec '(0 if 0 else 0)'
-except SyntaxError:
- have_condexpr = False
-else:
- have_condexpr = True
-
-
-# what method to iterate over items do we want to use for dict iteration
-# in generated code? on 2.x let's go with iteritems, on 3.x with items
-if hasattr(dict, 'iteritems'):
- dict_item_iter = 'iteritems'
-else:
- dict_item_iter = 'items'
-
-
-# does if 0: dummy(x) get us x into the scope?
-def unoptimize_before_dead_code():
- x = 42
- def f():
- if 0: dummy(x)
- return f
-unoptimize_before_dead_code = bool(unoptimize_before_dead_code().func_closure)
-
-
-def generate(node, environment, name, filename, stream=None,
- defer_init=False):
- """Generate the python source for a node tree."""
- if not isinstance(node, nodes.Template):
- raise TypeError('Can\'t compile non template nodes')
- generator = CodeGenerator(environment, name, filename, stream, defer_init)
- generator.visit(node)
- if stream is None:
- return generator.stream.getvalue()
-
-
-def has_safe_repr(value):
- """Does the node have a safe representation?"""
- if value is None or value is NotImplemented or value is Ellipsis:
- return True
- if isinstance(value, (bool, int, long, float, complex, basestring,
- xrange, Markup)):
- return True
- if isinstance(value, (tuple, list, set, frozenset)):
- for item in value:
- if not has_safe_repr(item):
- return False
- return True
- elif isinstance(value, dict):
- for key, value in value.iteritems():
- if not has_safe_repr(key):
- return False
- if not has_safe_repr(value):
- return False
- return True
- return False
-
-
-def find_undeclared(nodes, names):
- """Check if the names passed are accessed undeclared. The return value
- is a set of all the undeclared names from the sequence of names found.
- """
- visitor = UndeclaredNameVisitor(names)
- try:
- for node in nodes:
- visitor.visit(node)
- except VisitorExit:
- pass
- return visitor.undeclared
-
-
-class Identifiers(object):
- """Tracks the status of identifiers in frames."""
-
- def __init__(self):
- # variables that are known to be declared (probably from outer
- # frames or because they are special for the frame)
- self.declared = set()
-
- # undeclared variables from outer scopes
- self.outer_undeclared = set()
-
- # names that are accessed without being explicitly declared by
- # this one or any of the outer scopes. Names can appear both in
- # declared and undeclared.
- self.undeclared = set()
-
- # names that are declared locally
- self.declared_locally = set()
-
- # names that are declared by parameters
- self.declared_parameter = set()
-
- def add_special(self, name):
- """Register a special name like `loop`."""
- self.undeclared.discard(name)
- self.declared.add(name)
-
- def is_declared(self, name, local_only=False):
- """Check if a name is declared in this or an outer scope."""
- if name in self.declared_locally or name in self.declared_parameter:
- return True
- if local_only:
- return False
- return name in self.declared
-
- def copy(self):
- return deepcopy(self)
-
-
-class Frame(object):
- """Holds compile time information for us."""
-
- def __init__(self, eval_ctx, parent=None):
- self.eval_ctx = eval_ctx
- self.identifiers = Identifiers()
-
- # a toplevel frame is the root + soft frames such as if conditions.
- self.toplevel = False
-
- # the root frame is basically just the outermost frame, so no if
- # conditions. This information is used to optimize inheritance
- # situations.
- self.rootlevel = False
-
- # in some dynamic inheritance situations the compiler needs to add
- # write tests around output statements.
- self.require_output_check = parent and parent.require_output_check
-
- # inside some tags we are using a buffer rather than yield statements.
- # this for example affects {% filter %} or {% macro %}. If a frame
- # is buffered this variable points to the name of the list used as
- # buffer.
- self.buffer = None
-
- # the name of the block we're in, otherwise None.
- self.block = parent and parent.block or None
-
- # a set of actually assigned names
- self.assigned_names = set()
-
- # the parent of this frame
- self.parent = parent
-
- if parent is not None:
- self.identifiers.declared.update(
- parent.identifiers.declared |
- parent.identifiers.declared_parameter |
- parent.assigned_names
- )
- self.identifiers.outer_undeclared.update(
- parent.identifiers.undeclared -
- self.identifiers.declared
- )
- self.buffer = parent.buffer
-
- def copy(self):
- """Create a copy of the current one."""
- rv = object.__new__(self.__class__)
- rv.__dict__.update(self.__dict__)
- rv.identifiers = object.__new__(self.identifiers.__class__)
- rv.identifiers.__dict__.update(self.identifiers.__dict__)
- return rv
-
- def inspect(self, nodes, hard_scope=False):
- """Walk the node and check for identifiers. If the scope is hard (eg:
- enforce on a python level) overrides from outer scopes are tracked
- differently.
- """
- visitor = FrameIdentifierVisitor(self.identifiers, hard_scope)
- for node in nodes:
- visitor.visit(node)
-
- def find_shadowed(self, extra=()):
- """Find all the shadowed names. extra is an iterable of variables
- that may be defined with `add_special` which may occour scoped.
- """
- i = self.identifiers
- return (i.declared | i.outer_undeclared) & \
- (i.declared_locally | i.declared_parameter) | \
- set(x for x in extra if i.is_declared(x))
-
- def inner(self):
- """Return an inner frame."""
- return Frame(self.eval_ctx, self)
-
- def soft(self):
- """Return a soft frame. A soft frame may not be modified as
- standalone thing as it shares the resources with the frame it
- was created of, but it's not a rootlevel frame any longer.
- """
- rv = self.copy()
- rv.rootlevel = False
- return rv
-
- __copy__ = copy
-
-
-class VisitorExit(RuntimeError):
- """Exception used by the `UndeclaredNameVisitor` to signal a stop."""
-
-
-class DependencyFinderVisitor(NodeVisitor):
- """A visitor that collects filter and test calls."""
-
- def __init__(self):
- self.filters = set()
- self.tests = set()
-
- def visit_Filter(self, node):
- self.generic_visit(node)
- self.filters.add(node.name)
-
- def visit_Test(self, node):
- self.generic_visit(node)
- self.tests.add(node.name)
-
- def visit_Block(self, node):
- """Stop visiting at blocks."""
-
-
-class UndeclaredNameVisitor(NodeVisitor):
- """A visitor that checks if a name is accessed without being
- declared. This is different from the frame visitor as it will
- not stop at closure frames.
- """
-
- def __init__(self, names):
- self.names = set(names)
- self.undeclared = set()
-
- def visit_Name(self, node):
- if node.ctx == 'load' and node.name in self.names:
- self.undeclared.add(node.name)
- if self.undeclared == self.names:
- raise VisitorExit()
- else:
- self.names.discard(node.name)
-
- def visit_Block(self, node):
- """Stop visiting a blocks."""
-
-
-class FrameIdentifierVisitor(NodeVisitor):
- """A visitor for `Frame.inspect`."""
-
- def __init__(self, identifiers, hard_scope):
- self.identifiers = identifiers
- self.hard_scope = hard_scope
-
- def visit_Name(self, node):
- """All assignments to names go through this function."""
- if node.ctx == 'store':
- self.identifiers.declared_locally.add(node.name)
- elif node.ctx == 'param':
- self.identifiers.declared_parameter.add(node.name)
- elif node.ctx == 'load' and not \
- self.identifiers.is_declared(node.name, self.hard_scope):
- self.identifiers.undeclared.add(node.name)
-
- def visit_If(self, node):
- self.visit(node.test)
- real_identifiers = self.identifiers
-
- old_names = real_identifiers.declared_locally | \
- real_identifiers.declared_parameter
-
- def inner_visit(nodes):
- if not nodes:
- return set()
- self.identifiers = real_identifiers.copy()
- for subnode in nodes:
- self.visit(subnode)
- rv = self.identifiers.declared_locally - old_names
- # we have to remember the undeclared variables of this branch
- # because we will have to pull them.
- real_identifiers.undeclared.update(self.identifiers.undeclared)
- self.identifiers = real_identifiers
- return rv
-
- body = inner_visit(node.body)
- else_ = inner_visit(node.else_ or ())
-
- # the differences between the two branches are also pulled as
- # undeclared variables
- real_identifiers.undeclared.update(body.symmetric_difference(else_) -
- real_identifiers.declared)
-
- # remember those that are declared.
- real_identifiers.declared_locally.update(body | else_)
-
- def visit_Macro(self, node):
- self.identifiers.declared_locally.add(node.name)
-
- def visit_Import(self, node):
- self.generic_visit(node)
- self.identifiers.declared_locally.add(node.target)
-
- def visit_FromImport(self, node):
- self.generic_visit(node)
- for name in node.names:
- if isinstance(name, tuple):
- self.identifiers.declared_locally.add(name[1])
- else:
- self.identifiers.declared_locally.add(name)
-
- def visit_Assign(self, node):
- """Visit assignments in the correct order."""
- self.visit(node.node)
- self.visit(node.target)
-
- def visit_For(self, node):
- """Visiting stops at for blocks. However the block sequence
- is visited as part of the outer scope.
- """
- self.visit(node.iter)
-
- def visit_CallBlock(self, node):
- self.visit(node.call)
-
- def visit_FilterBlock(self, node):
- self.visit(node.filter)
-
- def visit_Scope(self, node):
- """Stop visiting at scopes."""
-
- def visit_Block(self, node):
- """Stop visiting at blocks."""
-
-
-class CompilerExit(Exception):
- """Raised if the compiler encountered a situation where it just
- doesn't make sense to further process the code. Any block that
- raises such an exception is not further processed.
- """
-
-
-class CodeGenerator(NodeVisitor):
-
- def __init__(self, environment, name, filename, stream=None,
- defer_init=False):
- if stream is None:
- stream = StringIO()
- self.environment = environment
- self.name = name
- self.filename = filename
- self.stream = stream
- self.created_block_context = False
- self.defer_init = defer_init
-
- # aliases for imports
- self.import_aliases = {}
-
- # a registry for all blocks. Because blocks are moved out
- # into the global python scope they are registered here
- self.blocks = {}
-
- # the number of extends statements so far
- self.extends_so_far = 0
-
- # some templates have a rootlevel extends. In this case we
- # can safely assume that we're a child template and do some
- # more optimizations.
- self.has_known_extends = False
-
- # the current line number
- self.code_lineno = 1
-
- # registry of all filters and tests (global, not block local)
- self.tests = {}
- self.filters = {}
-
- # the debug information
- self.debug_info = []
- self._write_debug_info = None
-
- # the number of new lines before the next write()
- self._new_lines = 0
-
- # the line number of the last written statement
- self._last_line = 0
-
- # true if nothing was written so far.
- self._first_write = True
-
- # used by the `temporary_identifier` method to get new
- # unique, temporary identifier
- self._last_identifier = 0
-
- # the current indentation
- self._indentation = 0
-
- # -- Various compilation helpers
-
- def fail(self, msg, lineno):
- """Fail with a :exc:`TemplateAssertionError`."""
- raise TemplateAssertionError(msg, lineno, self.name, self.filename)
-
- def temporary_identifier(self):
- """Get a new unique identifier."""
- self._last_identifier += 1
- return 't_%d' % self._last_identifier
-
- def buffer(self, frame):
- """Enable buffering for the frame from that point onwards."""
- frame.buffer = self.temporary_identifier()
- self.writeline('%s = []' % frame.buffer)
-
- def return_buffer_contents(self, frame):
- """Return the buffer contents of the frame."""
- if frame.eval_ctx.volatile:
- self.writeline('if context.eval_ctx.autoescape:')
- self.indent()
- self.writeline('return Markup(concat(%s))' % frame.buffer)
- self.outdent()
- self.writeline('else:')
- self.indent()
- self.writeline('return concat(%s)' % frame.buffer)
- self.outdent()
- elif frame.eval_ctx.autoescape:
- self.writeline('return Markup(concat(%s))' % frame.buffer)
- else:
- self.writeline('return concat(%s)' % frame.buffer)
-
- def indent(self):
- """Indent by one."""
- self._indentation += 1
-
- def outdent(self, step=1):
- """Outdent by step."""
- self._indentation -= step
-
- def start_write(self, frame, node=None):
- """Yield or write into the frame buffer."""
- if frame.buffer is None:
- self.writeline('yield ', node)
- else:
- self.writeline('%s.append(' % frame.buffer, node)
-
- def end_write(self, frame):
- """End the writing process started by `start_write`."""
- if frame.buffer is not None:
- self.write(')')
-
- def simple_write(self, s, frame, node=None):
- """Simple shortcut for start_write + write + end_write."""
- self.start_write(frame, node)
- self.write(s)
- self.end_write(frame)
-
- def blockvisit(self, nodes, frame):
- """Visit a list of nodes as block in a frame. If the current frame
- is no buffer a dummy ``if 0: yield None`` is written automatically
- unless the force_generator parameter is set to False.
- """
- if frame.buffer is None:
- self.writeline('if 0: yield None')
- else:
- self.writeline('pass')
- try:
- for node in nodes:
- self.visit(node, frame)
- except CompilerExit:
- pass
-
- def write(self, x):
- """Write a string into the output stream."""
- if self._new_lines:
- if not self._first_write:
- self.stream.write('\n' * self._new_lines)
- self.code_lineno += self._new_lines
- if self._write_debug_info is not None:
- self.debug_info.append((self._write_debug_info,
- self.code_lineno))
- self._write_debug_info = None
- self._first_write = False
- self.stream.write(' ' * self._indentation)
- self._new_lines = 0
- self.stream.write(x)
-
- def writeline(self, x, node=None, extra=0):
- """Combination of newline and write."""
- self.newline(node, extra)
- self.write(x)
-
- def newline(self, node=None, extra=0):
- """Add one or more newlines before the next write."""
- self._new_lines = max(self._new_lines, 1 + extra)
- if node is not None and node.lineno != self._last_line:
- self._write_debug_info = node.lineno
- self._last_line = node.lineno
-
- def signature(self, node, frame, extra_kwargs=None):
- """Writes a function call to the stream for the current node.
- A leading comma is added automatically. The extra keyword
- arguments may not include python keywords otherwise a syntax
- error could occour. The extra keyword arguments should be given
- as python dict.
- """
- # if any of the given keyword arguments is a python keyword
- # we have to make sure that no invalid call is created.
- kwarg_workaround = False
- for kwarg in chain((x.key for x in node.kwargs), extra_kwargs or ()):
- if is_python_keyword(kwarg):
- kwarg_workaround = True
- break
-
- for arg in node.args:
- self.write(', ')
- self.visit(arg, frame)
-
- if not kwarg_workaround:
- for kwarg in node.kwargs:
- self.write(', ')
- self.visit(kwarg, frame)
- if extra_kwargs is not None:
- for key, value in extra_kwargs.iteritems():
- self.write(', %s=%s' % (key, value))
- if node.dyn_args:
- self.write(', *')
- self.visit(node.dyn_args, frame)
-
- if kwarg_workaround:
- if node.dyn_kwargs is not None:
- self.write(', **dict({')
- else:
- self.write(', **{')
- for kwarg in node.kwargs:
- self.write('%r: ' % kwarg.key)
- self.visit(kwarg.value, frame)
- self.write(', ')
- if extra_kwargs is not None:
- for key, value in extra_kwargs.iteritems():
- self.write('%r: %s, ' % (key, value))
- if node.dyn_kwargs is not None:
- self.write('}, **')
- self.visit(node.dyn_kwargs, frame)
- self.write(')')
- else:
- self.write('}')
-
- elif node.dyn_kwargs is not None:
- self.write(', **')
- self.visit(node.dyn_kwargs, frame)
-
- def pull_locals(self, frame):
- """Pull all the references identifiers into the local scope."""
- for name in frame.identifiers.undeclared:
- self.writeline('l_%s = context.resolve(%r)' % (name, name))
-
- def pull_dependencies(self, nodes):
- """Pull all the dependencies."""
- visitor = DependencyFinderVisitor()
- for node in nodes:
- visitor.visit(node)
- for dependency in 'filters', 'tests':
- mapping = getattr(self, dependency)
- for name in getattr(visitor, dependency):
- if name not in mapping:
- mapping[name] = self.temporary_identifier()
- self.writeline('%s = environment.%s[%r]' %
- (mapping[name], dependency, name))
-
- def unoptimize_scope(self, frame):
- """Disable Python optimizations for the frame."""
- # XXX: this is not that nice but it has no real overhead. It
- # mainly works because python finds the locals before dead code
- # is removed. If that breaks we have to add a dummy function
- # that just accepts the arguments and does nothing.
- if frame.identifiers.declared:
- self.writeline('%sdummy(%s)' % (
- unoptimize_before_dead_code and 'if 0: ' or '',
- ', '.join('l_' + name for name in frame.identifiers.declared)
- ))
-
- def push_scope(self, frame, extra_vars=()):
- """This function returns all the shadowed variables in a dict
- in the form name: alias and will write the required assignments
- into the current scope. No indentation takes place.
-
- This also predefines locally declared variables from the loop
- body because under some circumstances it may be the case that
-
- `extra_vars` is passed to `Frame.find_shadowed`.
- """
- aliases = {}
- for name in frame.find_shadowed(extra_vars):
- aliases[name] = ident = self.temporary_identifier()
- self.writeline('%s = l_%s' % (ident, name))
- to_declare = set()
- for name in frame.identifiers.declared_locally:
- if name not in aliases:
- to_declare.add('l_' + name)
- if to_declare:
- self.writeline(' = '.join(to_declare) + ' = missing')
- return aliases
-
- def pop_scope(self, aliases, frame):
- """Restore all aliases and delete unused variables."""
- for name, alias in aliases.iteritems():
- self.writeline('l_%s = %s' % (name, alias))
- to_delete = set()
- for name in frame.identifiers.declared_locally:
- if name not in aliases:
- to_delete.add('l_' + name)
- if to_delete:
- # we cannot use the del statement here because enclosed
- # scopes can trigger a SyntaxError:
- # a = 42; b = lambda: a; del a
- self.writeline(' = '.join(to_delete) + ' = missing')
-
- def function_scoping(self, node, frame, children=None,
- find_special=True):
- """In Jinja a few statements require the help of anonymous
- functions. Those are currently macros and call blocks and in
- the future also recursive loops. As there is currently
- technical limitation that doesn't allow reading and writing a
- variable in a scope where the initial value is coming from an
- outer scope, this function tries to fall back with a common
- error message. Additionally the frame passed is modified so
- that the argumetns are collected and callers are looked up.
-
- This will return the modified frame.
- """
- # we have to iterate twice over it, make sure that works
- if children is None:
- children = node.iter_child_nodes()
- children = list(children)
- func_frame = frame.inner()
- func_frame.inspect(children, hard_scope=True)
-
- # variables that are undeclared (accessed before declaration) and
- # declared locally *and* part of an outside scope raise a template
- # assertion error. Reason: we can't generate reasonable code from
- # it without aliasing all the variables.
- # this could be fixed in Python 3 where we have the nonlocal
- # keyword or if we switch to bytecode generation
- overriden_closure_vars = (
- func_frame.identifiers.undeclared &
- func_frame.identifiers.declared &
- (func_frame.identifiers.declared_locally |
- func_frame.identifiers.declared_parameter)
- )
- if overriden_closure_vars:
- self.fail('It\'s not possible to set and access variables '
- 'derived from an outer scope! (affects: %s)' %
- ', '.join(sorted(overriden_closure_vars)), node.lineno)
-
- # remove variables from a closure from the frame's undeclared
- # identifiers.
- func_frame.identifiers.undeclared -= (
- func_frame.identifiers.undeclared &
- func_frame.identifiers.declared
- )
-
- # no special variables for this scope, abort early
- if not find_special:
- return func_frame
-
- func_frame.accesses_kwargs = False
- func_frame.accesses_varargs = False
- func_frame.accesses_caller = False
- func_frame.arguments = args = ['l_' + x.name for x in node.args]
-
- undeclared = find_undeclared(children, ('caller', 'kwargs', 'varargs'))
-
- if 'caller' in undeclared:
- func_frame.accesses_caller = True
- func_frame.identifiers.add_special('caller')
- args.append('l_caller')
- if 'kwargs' in undeclared:
- func_frame.accesses_kwargs = True
- func_frame.identifiers.add_special('kwargs')
- args.append('l_kwargs')
- if 'varargs' in undeclared:
- func_frame.accesses_varargs = True
- func_frame.identifiers.add_special('varargs')
- args.append('l_varargs')
- return func_frame
-
- def macro_body(self, node, frame, children=None):
- """Dump the function def of a macro or call block."""
- frame = self.function_scoping(node, frame, children)
- # macros are delayed, they never require output checks
- frame.require_output_check = False
- args = frame.arguments
- # XXX: this is an ugly fix for the loop nesting bug
- # (tests.test_old_bugs.test_loop_call_bug). This works around
- # a identifier nesting problem we have in general. It's just more
- # likely to happen in loops which is why we work around it. The
- # real solution would be "nonlocal" all the identifiers that are
- # leaking into a new python frame and might be used both unassigned
- # and assigned.
- if 'loop' in frame.identifiers.declared:
- args = args + ['l_loop=l_loop']
- self.writeline('def macro(%s):' % ', '.join(args), node)
- self.indent()
- self.buffer(frame)
- self.pull_locals(frame)
- self.blockvisit(node.body, frame)
- self.return_buffer_contents(frame)
- self.outdent()
- return frame
-
- def macro_def(self, node, frame):
- """Dump the macro definition for the def created by macro_body."""
- arg_tuple = ', '.join(repr(x.name) for x in node.args)
- name = getattr(node, 'name', None)
- if len(node.args) == 1:
- arg_tuple += ','
- self.write('Macro(environment, macro, %r, (%s), (' %
- (name, arg_tuple))
- for arg in node.defaults:
- self.visit(arg, frame)
- self.write(', ')
- self.write('), %r, %r, %r)' % (
- bool(frame.accesses_kwargs),
- bool(frame.accesses_varargs),
- bool(frame.accesses_caller)
- ))
-
- def position(self, node):
- """Return a human readable position for the node."""
- rv = 'line %d' % node.lineno
- if self.name is not None:
- rv += ' in ' + repr(self.name)
- return rv
-
- # -- Statement Visitors
-
- def visit_Template(self, node, frame=None):
- assert frame is None, 'no root frame allowed'
- eval_ctx = EvalContext(self.environment, self.name)
-
- from jinja2.runtime import __all__ as exported
- self.writeline('from __future__ import division')
- self.writeline('from jinja2.runtime import ' + ', '.join(exported))
- if not unoptimize_before_dead_code:
- self.writeline('dummy = lambda *x: None')
-
- # if we want a deferred initialization we cannot move the
- # environment into a local name
- envenv = not self.defer_init and ', environment=environment' or ''
-
- # do we have an extends tag at all? If not, we can save some
- # overhead by just not processing any inheritance code.
- have_extends = node.find(nodes.Extends) is not None
-
- # find all blocks
- for block in node.find_all(nodes.Block):
- if block.name in self.blocks:
- self.fail('block %r defined twice' % block.name, block.lineno)
- self.blocks[block.name] = block
-
- # find all imports and import them
- for import_ in node.find_all(nodes.ImportedName):
- if import_.importname not in self.import_aliases:
- imp = import_.importname
- self.import_aliases[imp] = alias = self.temporary_identifier()
- if '.' in imp:
- module, obj = imp.rsplit('.', 1)
- self.writeline('from %s import %s as %s' %
- (module, obj, alias))
- else:
- self.writeline('import %s as %s' % (imp, alias))
-
- # add the load name
- self.writeline('name = %r' % self.name)
-
- # generate the root render function.
- self.writeline('def root(context%s):' % envenv, extra=1)
-
- # process the root
- frame = Frame(eval_ctx)
- frame.inspect(node.body)
- frame.toplevel = frame.rootlevel = True
- frame.require_output_check = have_extends and not self.has_known_extends
- self.indent()
- if have_extends:
- self.writeline('parent_template = None')
- if 'self' in find_undeclared(node.body, ('self',)):
- frame.identifiers.add_special('self')
- self.writeline('l_self = TemplateReference(context)')
- self.pull_locals(frame)
- self.pull_dependencies(node.body)
- self.blockvisit(node.body, frame)
- self.outdent()
-
- # make sure that the parent root is called.
- if have_extends:
- if not self.has_known_extends:
- self.indent()
- self.writeline('if parent_template is not None:')
- self.indent()
- self.writeline('for event in parent_template.'
- 'root_render_func(context):')
- self.indent()
- self.writeline('yield event')
- self.outdent(2 + (not self.has_known_extends))
-
- # at this point we now have the blocks collected and can visit them too.
- for name, block in self.blocks.iteritems():
- block_frame = Frame(eval_ctx)
- block_frame.inspect(block.body)
- block_frame.block = name
- self.writeline('def block_%s(context%s):' % (name, envenv),
- block, 1)
- self.indent()
- undeclared = find_undeclared(block.body, ('self', 'super'))
- if 'self' in undeclared:
- block_frame.identifiers.add_special('self')
- self.writeline('l_self = TemplateReference(context)')
- if 'super' in undeclared:
- block_frame.identifiers.add_special('super')
- self.writeline('l_super = context.super(%r, '
- 'block_%s)' % (name, name))
- self.pull_locals(block_frame)
- self.pull_dependencies(block.body)
- self.blockvisit(block.body, block_frame)
- self.outdent()
-
- self.writeline('blocks = {%s}' % ', '.join('%r: block_%s' % (x, x)
- for x in self.blocks),
- extra=1)
-
- # add a function that returns the debug info
- self.writeline('debug_info = %r' % '&'.join('%s=%s' % x for x
- in self.debug_info))
-
- def visit_Block(self, node, frame):
- """Call a block and register it for the template."""
- level = 1
- if frame.toplevel:
- # if we know that we are a child template, there is no need to
- # check if we are one
- if self.has_known_extends:
- return
- if self.extends_so_far > 0:
- self.writeline('if parent_template is None:')
- self.indent()
- level += 1
- context = node.scoped and 'context.derived(locals())' or 'context'
- self.writeline('for event in context.blocks[%r][0](%s):' % (
- node.name, context), node)
- self.indent()
- self.simple_write('event', frame)
- self.outdent(level)
-
- def visit_Extends(self, node, frame):
- """Calls the extender."""
- if not frame.toplevel:
- self.fail('cannot use extend from a non top-level scope',
- node.lineno)
-
- # if the number of extends statements in general is zero so
- # far, we don't have to add a check if something extended
- # the template before this one.
- if self.extends_so_far > 0:
-
- # if we have a known extends we just add a template runtime
- # error into the generated code. We could catch that at compile
- # time too, but i welcome it not to confuse users by throwing the
- # same error at different times just "because we can".
- if not self.has_known_extends:
- self.writeline('if parent_template is not None:')
- self.indent()
- self.writeline('raise TemplateRuntimeError(%r)' %
- 'extended multiple times')
- self.outdent()
-
- # if we have a known extends already we don't need that code here
- # as we know that the template execution will end here.
- if self.has_known_extends:
- raise CompilerExit()
-
- self.writeline('parent_template = environment.get_template(', node)
- self.visit(node.template, frame)
- self.write(', %r)' % self.name)
- self.writeline('for name, parent_block in parent_template.'
- 'blocks.%s():' % dict_item_iter)
- self.indent()
- self.writeline('context.blocks.setdefault(name, []).'
- 'append(parent_block)')
- self.outdent()
-
- # if this extends statement was in the root level we can take
- # advantage of that information and simplify the generated code
- # in the top level from this point onwards
- if frame.rootlevel:
- self.has_known_extends = True
-
- # and now we have one more
- self.extends_so_far += 1
-
- def visit_Include(self, node, frame):
- """Handles includes."""
- if node.with_context:
- self.unoptimize_scope(frame)
- if node.ignore_missing:
- self.writeline('try:')
- self.indent()
-
- func_name = 'get_or_select_template'
- if isinstance(node.template, nodes.Const):
- if isinstance(node.template.value, basestring):
- func_name = 'get_template'
- elif isinstance(node.template.value, (tuple, list)):
- func_name = 'select_template'
- elif isinstance(node.template, (nodes.Tuple, nodes.List)):
- func_name = 'select_template'
-
- self.writeline('template = environment.%s(' % func_name, node)
- self.visit(node.template, frame)
- self.write(', %r)' % self.name)
- if node.ignore_missing:
- self.outdent()
- self.writeline('except TemplateNotFound:')
- self.indent()
- self.writeline('pass')
- self.outdent()
- self.writeline('else:')
- self.indent()
-
- if node.with_context:
- self.writeline('for event in template.root_render_func('
- 'template.new_context(context.parent, True, '
- 'locals())):')
- else:
- self.writeline('for event in template.module._body_stream:')
-
- self.indent()
- self.simple_write('event', frame)
- self.outdent()
-
- if node.ignore_missing:
- self.outdent()
-
- def visit_Import(self, node, frame):
- """Visit regular imports."""
- if node.with_context:
- self.unoptimize_scope(frame)
- self.writeline('l_%s = ' % node.target, node)
- if frame.toplevel:
- self.write('context.vars[%r] = ' % node.target)
- self.write('environment.get_template(')
- self.visit(node.template, frame)
- self.write(', %r).' % self.name)
- if node.with_context:
- self.write('make_module(context.parent, True, locals())')
- else:
- self.write('module')
- if frame.toplevel and not node.target.startswith('_'):
- self.writeline('context.exported_vars.discard(%r)' % node.target)
- frame.assigned_names.add(node.target)
-
- def visit_FromImport(self, node, frame):
- """Visit named imports."""
- self.newline(node)
- self.write('included_template = environment.get_template(')
- self.visit(node.template, frame)
- self.write(', %r).' % self.name)
- if node.with_context:
- self.write('make_module(context.parent, True)')
- else:
- self.write('module')
-
- var_names = []
- discarded_names = []
- for name in node.names:
- if isinstance(name, tuple):
- name, alias = name
- else:
- alias = name
- self.writeline('l_%s = getattr(included_template, '
- '%r, missing)' % (alias, name))
- self.writeline('if l_%s is missing:' % alias)
- self.indent()
- self.writeline('l_%s = environment.undefined(%r %% '
- 'included_template.__name__, '
- 'name=%r)' %
- (alias, 'the template %%r (imported on %s) does '
- 'not export the requested name %s' % (
- self.position(node),
- repr(name)
- ), name))
- self.outdent()
- if frame.toplevel:
- var_names.append(alias)
- if not alias.startswith('_'):
- discarded_names.append(alias)
- frame.assigned_names.add(alias)
-
- if var_names:
- if len(var_names) == 1:
- name = var_names[0]
- self.writeline('context.vars[%r] = l_%s' % (name, name))
- else:
- self.writeline('context.vars.update({%s})' % ', '.join(
- '%r: l_%s' % (name, name) for name in var_names
- ))
- if discarded_names:
- if len(discarded_names) == 1:
- self.writeline('context.exported_vars.discard(%r)' %
- discarded_names[0])
- else:
- self.writeline('context.exported_vars.difference_'
- 'update((%s))' % ', '.join(map(repr, discarded_names)))
-
- def visit_For(self, node, frame):
- # when calculating the nodes for the inner frame we have to exclude
- # the iterator contents from it
- children = node.iter_child_nodes(exclude=('iter',))
- if node.recursive:
- loop_frame = self.function_scoping(node, frame, children,
- find_special=False)
- else:
- loop_frame = frame.inner()
- loop_frame.inspect(children)
-
- # try to figure out if we have an extended loop. An extended loop
- # is necessary if the loop is in recursive mode if the special loop
- # variable is accessed in the body.
- extended_loop = node.recursive or 'loop' in \
- find_undeclared(node.iter_child_nodes(
- only=('body',)), ('loop',))
-
- # if we don't have an recursive loop we have to find the shadowed
- # variables at that point. Because loops can be nested but the loop
- # variable is a special one we have to enforce aliasing for it.
- if not node.recursive:
- aliases = self.push_scope(loop_frame, ('loop',))
-
- # otherwise we set up a buffer and add a function def
- else:
- self.writeline('def loop(reciter, loop_render_func):', node)
- self.indent()
- self.buffer(loop_frame)
- aliases = {}
-
- # make sure the loop variable is a special one and raise a template
- # assertion error if a loop tries to write to loop
- if extended_loop:
- loop_frame.identifiers.add_special('loop')
- for name in node.find_all(nodes.Name):
- if name.ctx == 'store' and name.name == 'loop':
- self.fail('Can\'t assign to special loop variable '
- 'in for-loop target', name.lineno)
-
- self.pull_locals(loop_frame)
- if node.else_:
- iteration_indicator = self.temporary_identifier()
- self.writeline('%s = 1' % iteration_indicator)
-
- # Create a fake parent loop if the else or test section of a
- # loop is accessing the special loop variable and no parent loop
- # exists.
- if 'loop' not in aliases and 'loop' in find_undeclared(
- node.iter_child_nodes(only=('else_', 'test')), ('loop',)):
- self.writeline("l_loop = environment.undefined(%r, name='loop')" %
- ("'loop' is undefined. the filter section of a loop as well "
- "as the else block doesn't have access to the special 'loop'"
- " variable of the current loop. Because there is no parent "
- "loop it's undefined. Happened in loop on %s" %
- self.position(node)))
-
- self.writeline('for ', node)
- self.visit(node.target, loop_frame)
- self.write(extended_loop and ', l_loop in LoopContext(' or ' in ')
-
- # if we have an extened loop and a node test, we filter in the
- # "outer frame".
- if extended_loop and node.test is not None:
- self.write('(')
- self.visit(node.target, loop_frame)
- self.write(' for ')
- self.visit(node.target, loop_frame)
- self.write(' in ')
- if node.recursive:
- self.write('reciter')
- else:
- self.visit(node.iter, loop_frame)
- self.write(' if (')
- test_frame = loop_frame.copy()
- self.visit(node.test, test_frame)
- self.write('))')
-
- elif node.recursive:
- self.write('reciter')
- else:
- self.visit(node.iter, loop_frame)
-
- if node.recursive:
- self.write(', recurse=loop_render_func):')
- else:
- self.write(extended_loop and '):' or ':')
-
- # tests in not extended loops become a continue
- if not extended_loop and node.test is not None:
- self.indent()
- self.writeline('if not ')
- self.visit(node.test, loop_frame)
- self.write(':')
- self.indent()
- self.writeline('continue')
- self.outdent(2)
-
- self.indent()
- self.blockvisit(node.body, loop_frame)
- if node.else_:
- self.writeline('%s = 0' % iteration_indicator)
- self.outdent()
-
- if node.else_:
- self.writeline('if %s:' % iteration_indicator)
- self.indent()
- self.blockvisit(node.else_, loop_frame)
- self.outdent()
-
- # reset the aliases if there are any.
- if not node.recursive:
- self.pop_scope(aliases, loop_frame)
-
- # if the node was recursive we have to return the buffer contents
- # and start the iteration code
- if node.recursive:
- self.return_buffer_contents(loop_frame)
- self.outdent()
- self.start_write(frame, node)
- self.write('loop(')
- self.visit(node.iter, frame)
- self.write(', loop)')
- self.end_write(frame)
-
- def visit_If(self, node, frame):
- if_frame = frame.soft()
- self.writeline('if ', node)
- self.visit(node.test, if_frame)
- self.write(':')
- self.indent()
- self.blockvisit(node.body, if_frame)
- self.outdent()
- if node.else_:
- self.writeline('else:')
- self.indent()
- self.blockvisit(node.else_, if_frame)
- self.outdent()
-
- def visit_Macro(self, node, frame):
- macro_frame = self.macro_body(node, frame)
- self.newline()
- if frame.toplevel:
- if not node.name.startswith('_'):
- self.write('context.exported_vars.add(%r)' % node.name)
- self.writeline('context.vars[%r] = ' % node.name)
- self.write('l_%s = ' % node.name)
- self.macro_def(node, macro_frame)
- frame.assigned_names.add(node.name)
-
- def visit_CallBlock(self, node, frame):
- children = node.iter_child_nodes(exclude=('call',))
- call_frame = self.macro_body(node, frame, children)
- self.writeline('caller = ')
- self.macro_def(node, call_frame)
- self.start_write(frame, node)
- self.visit_Call(node.call, call_frame, forward_caller=True)
- self.end_write(frame)
-
- def visit_FilterBlock(self, node, frame):
- filter_frame = frame.inner()
- filter_frame.inspect(node.iter_child_nodes())
- aliases = self.push_scope(filter_frame)
- self.pull_locals(filter_frame)
- self.buffer(filter_frame)
- self.blockvisit(node.body, filter_frame)
- self.start_write(frame, node)
- self.visit_Filter(node.filter, filter_frame)
- self.end_write(frame)
- self.pop_scope(aliases, filter_frame)
-
- def visit_ExprStmt(self, node, frame):
- self.newline(node)
- self.visit(node.node, frame)
-
- def visit_Output(self, node, frame):
- # if we have a known extends statement, we don't output anything
- # if we are in a require_output_check section
- if self.has_known_extends and frame.require_output_check:
- return
-
- if self.environment.finalize:
- finalize = lambda x: unicode(self.environment.finalize(x))
- else:
- finalize = unicode
-
- # if we are inside a frame that requires output checking, we do so
- outdent_later = False
- if frame.require_output_check:
- self.writeline('if parent_template is None:')
- self.indent()
- outdent_later = True
-
- # try to evaluate as many chunks as possible into a static
- # string at compile time.
- body = []
- for child in node.nodes:
- try:
- const = child.as_const(frame.eval_ctx)
- except nodes.Impossible:
- body.append(child)
- continue
- # the frame can't be volatile here, becaus otherwise the
- # as_const() function would raise an Impossible exception
- # at that point.
- try:
- if frame.eval_ctx.autoescape:
- if hasattr(const, '__html__'):
- const = const.__html__()
- else:
- const = escape(const)
- const = finalize(const)
- except:
- # if something goes wrong here we evaluate the node
- # at runtime for easier debugging
- body.append(child)
- continue
- if body and isinstance(body[-1], list):
- body[-1].append(const)
- else:
- body.append([const])
-
- # if we have less than 3 nodes or a buffer we yield or extend/append
- if len(body) < 3 or frame.buffer is not None:
- if frame.buffer is not None:
- # for one item we append, for more we extend
- if len(body) == 1:
- self.writeline('%s.append(' % frame.buffer)
- else:
- self.writeline('%s.extend((' % frame.buffer)
- self.indent()
- for item in body:
- if isinstance(item, list):
- val = repr(concat(item))
- if frame.buffer is None:
- self.writeline('yield ' + val)
- else:
- self.writeline(val + ', ')
- else:
- if frame.buffer is None:
- self.writeline('yield ', item)
- else:
- self.newline(item)
- close = 1
- if frame.eval_ctx.volatile:
- self.write('(context.eval_ctx.autoescape and'
- ' escape or to_string)(')
- elif frame.eval_ctx.autoescape:
- self.write('escape(')
- else:
- self.write('to_string(')
- if self.environment.finalize is not None:
- self.write('environment.finalize(')
- close += 1
- self.visit(item, frame)
- self.write(')' * close)
- if frame.buffer is not None:
- self.write(', ')
- if frame.buffer is not None:
- # close the open parentheses
- self.outdent()
- self.writeline(len(body) == 1 and ')' or '))')
-
- # otherwise we create a format string as this is faster in that case
- else:
- format = []
- arguments = []
- for item in body:
- if isinstance(item, list):
- format.append(concat(item).replace('%', '%%'))
- else:
- format.append('%s')
- arguments.append(item)
- self.writeline('yield ')
- self.write(repr(concat(format)) + ' % (')
- idx = -1
- self.indent()
- for argument in arguments:
- self.newline(argument)
- close = 0
- if frame.eval_ctx.volatile:
- self.write('(context.eval_ctx.autoescape and'
- ' escape or to_string)(')
- close += 1
- elif frame.eval_ctx.autoescape:
- self.write('escape(')
- close += 1
- if self.environment.finalize is not None:
- self.write('environment.finalize(')
- close += 1
- self.visit(argument, frame)
- self.write(')' * close + ', ')
- self.outdent()
- self.writeline(')')
-
- if outdent_later:
- self.outdent()
-
- def visit_Assign(self, node, frame):
- self.newline(node)
- # toplevel assignments however go into the local namespace and
- # the current template's context. We create a copy of the frame
- # here and add a set so that the Name visitor can add the assigned
- # names here.
- if frame.toplevel:
- assignment_frame = frame.copy()
- assignment_frame.toplevel_assignments = set()
- else:
- assignment_frame = frame
- self.visit(node.target, assignment_frame)
- self.write(' = ')
- self.visit(node.node, frame)
-
- # make sure toplevel assignments are added to the context.
- if frame.toplevel:
- public_names = [x for x in assignment_frame.toplevel_assignments
- if not x.startswith('_')]
- if len(assignment_frame.toplevel_assignments) == 1:
- name = next(iter(assignment_frame.toplevel_assignments))
- self.writeline('context.vars[%r] = l_%s' % (name, name))
- else:
- self.writeline('context.vars.update({')
- for idx, name in enumerate(assignment_frame.toplevel_assignments):
- if idx:
- self.write(', ')
- self.write('%r: l_%s' % (name, name))
- self.write('})')
- if public_names:
- if len(public_names) == 1:
- self.writeline('context.exported_vars.add(%r)' %
- public_names[0])
- else:
- self.writeline('context.exported_vars.update((%s))' %
- ', '.join(map(repr, public_names)))
-
- # -- Expression Visitors
-
- def visit_Name(self, node, frame):
- if node.ctx == 'store' and frame.toplevel:
- frame.toplevel_assignments.add(node.name)
- self.write('l_' + node.name)
- frame.assigned_names.add(node.name)
-
- def visit_Const(self, node, frame):
- val = node.value
- if isinstance(val, float):
- self.write(str(val))
- else:
- self.write(repr(val))
-
- def visit_TemplateData(self, node, frame):
- try:
- self.write(repr(node.as_const(frame.eval_ctx)))
- except nodes.Impossible:
- self.write('(context.eval_ctx.autoescape and Markup or identity)(%r)'
- % node.data)
-
- def visit_Tuple(self, node, frame):
- self.write('(')
- idx = -1
- for idx, item in enumerate(node.items):
- if idx:
- self.write(', ')
- self.visit(item, frame)
- self.write(idx == 0 and ',)' or ')')
-
- def visit_List(self, node, frame):
- self.write('[')
- for idx, item in enumerate(node.items):
- if idx:
- self.write(', ')
- self.visit(item, frame)
- self.write(']')
-
- def visit_Dict(self, node, frame):
- self.write('{')
- for idx, item in enumerate(node.items):
- if idx:
- self.write(', ')
- self.visit(item.key, frame)
- self.write(': ')
- self.visit(item.value, frame)
- self.write('}')
-
- def binop(operator):
- def visitor(self, node, frame):
- self.write('(')
- self.visit(node.left, frame)
- self.write(' %s ' % operator)
- self.visit(node.right, frame)
- self.write(')')
- return visitor
-
- def uaop(operator):
- def visitor(self, node, frame):
- self.write('(' + operator)
- self.visit(node.node, frame)
- self.write(')')
- return visitor
-
- visit_Add = binop('+')
- visit_Sub = binop('-')
- visit_Mul = binop('*')
- visit_Div = binop('/')
- visit_FloorDiv = binop('//')
- visit_Pow = binop('**')
- visit_Mod = binop('%')
- visit_And = binop('and')
- visit_Or = binop('or')
- visit_Pos = uaop('+')
- visit_Neg = uaop('-')
- visit_Not = uaop('not ')
- del binop, uaop
-
- def visit_Concat(self, node, frame):
- if frame.eval_ctx.volatile:
- func_name = '(context.eval_ctx.volatile and' \
- ' markup_join or unicode_join)'
- elif frame.eval_ctx.autoescape:
- func_name = 'markup_join'
- else:
- func_name = 'unicode_join'
- self.write('%s((' % func_name)
- for arg in node.nodes:
- self.visit(arg, frame)
- self.write(', ')
- self.write('))')
-
- def visit_Compare(self, node, frame):
- self.visit(node.expr, frame)
- for op in node.ops:
- self.visit(op, frame)
-
- def visit_Operand(self, node, frame):
- self.write(' %s ' % operators[node.op])
- self.visit(node.expr, frame)
-
- def visit_Getattr(self, node, frame):
- self.write('environment.getattr(')
- self.visit(node.node, frame)
- self.write(', %r)' % node.attr)
-
- def visit_Getitem(self, node, frame):
- # slices bypass the environment getitem method.
- if isinstance(node.arg, nodes.Slice):
- self.visit(node.node, frame)
- self.write('[')
- self.visit(node.arg, frame)
- self.write(']')
- else:
- self.write('environment.getitem(')
- self.visit(node.node, frame)
- self.write(', ')
- self.visit(node.arg, frame)
- self.write(')')
-
- def visit_Slice(self, node, frame):
- if node.start is not None:
- self.visit(node.start, frame)
- self.write(':')
- if node.stop is not None:
- self.visit(node.stop, frame)
- if node.step is not None:
- self.write(':')
- self.visit(node.step, frame)
-
- def visit_Filter(self, node, frame):
- self.write(self.filters[node.name] + '(')
- func = self.environment.filters.get(node.name)
- if func is None:
- self.fail('no filter named %r' % node.name, node.lineno)
- if getattr(func, 'contextfilter', False):
- self.write('context, ')
- elif getattr(func, 'evalcontextfilter', False):
- self.write('context.eval_ctx, ')
- elif getattr(func, 'environmentfilter', False):
- self.write('environment, ')
-
- # if the filter node is None we are inside a filter block
- # and want to write to the current buffer
- if node.node is not None:
- self.visit(node.node, frame)
- elif frame.eval_ctx.volatile:
- self.write('(context.eval_ctx.autoescape and'
- ' Markup(concat(%s)) or concat(%s))' %
- (frame.buffer, frame.buffer))
- elif frame.eval_ctx.autoescape:
- self.write('Markup(concat(%s))' % frame.buffer)
- else:
- self.write('concat(%s)' % frame.buffer)
- self.signature(node, frame)
- self.write(')')
-
- def visit_Test(self, node, frame):
- self.write(self.tests[node.name] + '(')
- if node.name not in self.environment.tests:
- self.fail('no test named %r' % node.name, node.lineno)
- self.visit(node.node, frame)
- self.signature(node, frame)
- self.write(')')
-
- def visit_CondExpr(self, node, frame):
- def write_expr2():
- if node.expr2 is not None:
- return self.visit(node.expr2, frame)
- self.write('environment.undefined(%r)' % ('the inline if-'
- 'expression on %s evaluated to false and '
- 'no else section was defined.' % self.position(node)))
-
- if not have_condexpr:
- self.write('((')
- self.visit(node.test, frame)
- self.write(') and (')
- self.visit(node.expr1, frame)
- self.write(',) or (')
- write_expr2()
- self.write(',))[0]')
- else:
- self.write('(')
- self.visit(node.expr1, frame)
- self.write(' if ')
- self.visit(node.test, frame)
- self.write(' else ')
- write_expr2()
- self.write(')')
-
- def visit_Call(self, node, frame, forward_caller=False):
- if self.environment.sandboxed:
- self.write('environment.call(context, ')
- else:
- self.write('context.call(')
- self.visit(node.node, frame)
- extra_kwargs = forward_caller and {'caller': 'caller'} or None
- self.signature(node, frame, extra_kwargs)
- self.write(')')
-
- def visit_Keyword(self, node, frame):
- self.write(node.key + '=')
- self.visit(node.value, frame)
-
- # -- Unused nodes for extensions
-
- def visit_MarkSafe(self, node, frame):
- self.write('Markup(')
- self.visit(node.expr, frame)
- self.write(')')
-
- def visit_MarkSafeIfAutoescape(self, node, frame):
- self.write('(context.eval_ctx.autoescape and Markup or identity)(')
- self.visit(node.expr, frame)
- self.write(')')
-
- def visit_EnvironmentAttribute(self, node, frame):
- self.write('environment.' + node.name)
-
- def visit_ExtensionAttribute(self, node, frame):
- self.write('environment.extensions[%r].%s' % (node.identifier, node.name))
-
- def visit_ImportedName(self, node, frame):
- self.write(self.import_aliases[node.importname])
-
- def visit_InternalName(self, node, frame):
- self.write(node.name)
-
- def visit_ContextReference(self, node, frame):
- self.write('context')
-
- def visit_Continue(self, node, frame):
- self.writeline('continue', node)
-
- def visit_Break(self, node, frame):
- self.writeline('break', node)
-
- def visit_Scope(self, node, frame):
- scope_frame = frame.inner()
- scope_frame.inspect(node.iter_child_nodes())
- aliases = self.push_scope(scope_frame)
- self.pull_locals(scope_frame)
- self.blockvisit(node.body, scope_frame)
- self.pop_scope(aliases, scope_frame)
-
- def visit_EvalContextModifier(self, node, frame):
- for keyword in node.options:
- self.writeline('context.eval_ctx.%s = ' % keyword.key)
- self.visit(keyword.value, frame)
- try:
- val = keyword.value.as_const(frame.eval_ctx)
- except nodes.Impossible:
- frame.eval_ctx.volatile = True
- else:
- setattr(frame.eval_ctx, keyword.key, val)
-
- def visit_ScopedEvalContextModifier(self, node, frame):
- old_ctx_name = self.temporary_identifier()
- safed_ctx = frame.eval_ctx.save()
- self.writeline('%s = context.eval_ctx.save()' % old_ctx_name)
- self.visit_EvalContextModifier(node, frame)
- for child in node.body:
- self.visit(child, frame)
- frame.eval_ctx.revert(safed_ctx)
- self.writeline('context.eval_ctx.revert(%s)' % old_ctx_name)
diff --git a/module/lib/jinja2/constants.py b/module/lib/jinja2/constants.py
deleted file mode 100644
index cab203cc7..000000000
--- a/module/lib/jinja2/constants.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja.constants
- ~~~~~~~~~~~~~~~
-
- Various constants.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-
-
-#: list of lorem ipsum words used by the lipsum() helper function
-LOREM_IPSUM_WORDS = u'''\
-a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at
-auctor augue bibendum blandit class commodo condimentum congue consectetuer
-consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus
-diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend
-elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames
-faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac
-hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum
-justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem
-luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie
-mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non
-nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque
-penatibus per pharetra phasellus placerat platea porta porttitor posuere
-potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus
-ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit
-sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor
-tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices
-ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus
-viverra volutpat vulputate'''
diff --git a/module/lib/jinja2/debug.py b/module/lib/jinja2/debug.py
deleted file mode 100644
index eb15456d1..000000000
--- a/module/lib/jinja2/debug.py
+++ /dev/null
@@ -1,308 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.debug
- ~~~~~~~~~~~~
-
- Implements the debug interface for Jinja. This module does some pretty
- ugly stuff with the Python traceback system in order to achieve tracebacks
- with correct line numbers, locals and contents.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import sys
-import traceback
-from jinja2.utils import CodeType, missing, internal_code
-from jinja2.exceptions import TemplateSyntaxError
-
-
-# how does the raise helper look like?
-try:
- exec "raise TypeError, 'foo'"
-except SyntaxError:
- raise_helper = 'raise __jinja_exception__[1]'
-except TypeError:
- raise_helper = 'raise __jinja_exception__[0], __jinja_exception__[1]'
-
-
-class TracebackFrameProxy(object):
- """Proxies a traceback frame."""
-
- def __init__(self, tb):
- self.tb = tb
-
- def _set_tb_next(self, next):
- if tb_set_next is not None:
- tb_set_next(self.tb, next and next.tb or None)
- self._tb_next = next
-
- def _get_tb_next(self):
- return self._tb_next
-
- tb_next = property(_get_tb_next, _set_tb_next)
- del _get_tb_next, _set_tb_next
-
- @property
- def is_jinja_frame(self):
- return '__jinja_template__' in self.tb.tb_frame.f_globals
-
- def __getattr__(self, name):
- return getattr(self.tb, name)
-
-
-class ProcessedTraceback(object):
- """Holds a Jinja preprocessed traceback for priting or reraising."""
-
- def __init__(self, exc_type, exc_value, frames):
- assert frames, 'no frames for this traceback?'
- self.exc_type = exc_type
- self.exc_value = exc_value
- self.frames = frames
-
- def chain_frames(self):
- """Chains the frames. Requires ctypes or the debugsupport extension."""
- prev_tb = None
- for tb in self.frames:
- if prev_tb is not None:
- prev_tb.tb_next = tb
- prev_tb = tb
- prev_tb.tb_next = None
-
- def render_as_text(self, limit=None):
- """Return a string with the traceback."""
- lines = traceback.format_exception(self.exc_type, self.exc_value,
- self.frames[0], limit=limit)
- return ''.join(lines).rstrip()
-
- def render_as_html(self, full=False):
- """Return a unicode string with the traceback as rendered HTML."""
- from jinja2.debugrenderer import render_traceback
- return u'%s\n\n<!--\n%s\n-->' % (
- render_traceback(self, full=full),
- self.render_as_text().decode('utf-8', 'replace')
- )
-
- @property
- def is_template_syntax_error(self):
- """`True` if this is a template syntax error."""
- return isinstance(self.exc_value, TemplateSyntaxError)
-
- @property
- def exc_info(self):
- """Exception info tuple with a proxy around the frame objects."""
- return self.exc_type, self.exc_value, self.frames[0]
-
- @property
- def standard_exc_info(self):
- """Standard python exc_info for re-raising"""
- return self.exc_type, self.exc_value, self.frames[0].tb
-
-
-def make_traceback(exc_info, source_hint=None):
- """Creates a processed traceback object from the exc_info."""
- exc_type, exc_value, tb = exc_info
- if isinstance(exc_value, TemplateSyntaxError):
- exc_info = translate_syntax_error(exc_value, source_hint)
- initial_skip = 0
- else:
- initial_skip = 1
- return translate_exception(exc_info, initial_skip)
-
-
-def translate_syntax_error(error, source=None):
- """Rewrites a syntax error to please traceback systems."""
- error.source = source
- error.translated = True
- exc_info = (error.__class__, error, None)
- filename = error.filename
- if filename is None:
- filename = '<unknown>'
- return fake_exc_info(exc_info, filename, error.lineno)
-
-
-def translate_exception(exc_info, initial_skip=0):
- """If passed an exc_info it will automatically rewrite the exceptions
- all the way down to the correct line numbers and frames.
- """
- tb = exc_info[2]
- frames = []
-
- # skip some internal frames if wanted
- for x in xrange(initial_skip):
- if tb is not None:
- tb = tb.tb_next
- initial_tb = tb
-
- while tb is not None:
- # skip frames decorated with @internalcode. These are internal
- # calls we can't avoid and that are useless in template debugging
- # output.
- if tb.tb_frame.f_code in internal_code:
- tb = tb.tb_next
- continue
-
- # save a reference to the next frame if we override the current
- # one with a faked one.
- next = tb.tb_next
-
- # fake template exceptions
- template = tb.tb_frame.f_globals.get('__jinja_template__')
- if template is not None:
- lineno = template.get_corresponding_lineno(tb.tb_lineno)
- tb = fake_exc_info(exc_info[:2] + (tb,), template.filename,
- lineno)[2]
-
- frames.append(TracebackFrameProxy(tb))
- tb = next
-
- # if we don't have any exceptions in the frames left, we have to
- # reraise it unchanged.
- # XXX: can we backup here? when could this happen?
- if not frames:
- raise exc_info[0], exc_info[1], exc_info[2]
-
- traceback = ProcessedTraceback(exc_info[0], exc_info[1], frames)
- if tb_set_next is not None:
- traceback.chain_frames()
- return traceback
-
-
-def fake_exc_info(exc_info, filename, lineno):
- """Helper for `translate_exception`."""
- exc_type, exc_value, tb = exc_info
-
- # figure the real context out
- if tb is not None:
- real_locals = tb.tb_frame.f_locals.copy()
- ctx = real_locals.get('context')
- if ctx:
- locals = ctx.get_all()
- else:
- locals = {}
- for name, value in real_locals.iteritems():
- if name.startswith('l_') and value is not missing:
- locals[name[2:]] = value
-
- # if there is a local called __jinja_exception__, we get
- # rid of it to not break the debug functionality.
- locals.pop('__jinja_exception__', None)
- else:
- locals = {}
-
- # assamble fake globals we need
- globals = {
- '__name__': filename,
- '__file__': filename,
- '__jinja_exception__': exc_info[:2],
-
- # we don't want to keep the reference to the template around
- # to not cause circular dependencies, but we mark it as Jinja
- # frame for the ProcessedTraceback
- '__jinja_template__': None
- }
-
- # and fake the exception
- code = compile('\n' * (lineno - 1) + raise_helper, filename, 'exec')
-
- # if it's possible, change the name of the code. This won't work
- # on some python environments such as google appengine
- try:
- if tb is None:
- location = 'template'
- else:
- function = tb.tb_frame.f_code.co_name
- if function == 'root':
- location = 'top-level template code'
- elif function.startswith('block_'):
- location = 'block "%s"' % function[6:]
- else:
- location = 'template'
- code = CodeType(0, code.co_nlocals, code.co_stacksize,
- code.co_flags, code.co_code, code.co_consts,
- code.co_names, code.co_varnames, filename,
- location, code.co_firstlineno,
- code.co_lnotab, (), ())
- except:
- pass
-
- # execute the code and catch the new traceback
- try:
- exec code in globals, locals
- except:
- exc_info = sys.exc_info()
- new_tb = exc_info[2].tb_next
-
- # return without this frame
- return exc_info[:2] + (new_tb,)
-
-
-def _init_ugly_crap():
- """This function implements a few ugly things so that we can patch the
- traceback objects. The function returned allows resetting `tb_next` on
- any python traceback object.
- """
- import ctypes
- from types import TracebackType
-
- # figure out side of _Py_ssize_t
- if hasattr(ctypes.pythonapi, 'Py_InitModule4_64'):
- _Py_ssize_t = ctypes.c_int64
- else:
- _Py_ssize_t = ctypes.c_int
-
- # regular python
- class _PyObject(ctypes.Structure):
- pass
- _PyObject._fields_ = [
- ('ob_refcnt', _Py_ssize_t),
- ('ob_type', ctypes.POINTER(_PyObject))
- ]
-
- # python with trace
- if hasattr(sys, 'getobjects'):
- class _PyObject(ctypes.Structure):
- pass
- _PyObject._fields_ = [
- ('_ob_next', ctypes.POINTER(_PyObject)),
- ('_ob_prev', ctypes.POINTER(_PyObject)),
- ('ob_refcnt', _Py_ssize_t),
- ('ob_type', ctypes.POINTER(_PyObject))
- ]
-
- class _Traceback(_PyObject):
- pass
- _Traceback._fields_ = [
- ('tb_next', ctypes.POINTER(_Traceback)),
- ('tb_frame', ctypes.POINTER(_PyObject)),
- ('tb_lasti', ctypes.c_int),
- ('tb_lineno', ctypes.c_int)
- ]
-
- def tb_set_next(tb, next):
- """Set the tb_next attribute of a traceback object."""
- if not (isinstance(tb, TracebackType) and
- (next is None or isinstance(next, TracebackType))):
- raise TypeError('tb_set_next arguments must be traceback objects')
- obj = _Traceback.from_address(id(tb))
- if tb.tb_next is not None:
- old = _Traceback.from_address(id(tb.tb_next))
- old.ob_refcnt -= 1
- if next is None:
- obj.tb_next = ctypes.POINTER(_Traceback)()
- else:
- next = _Traceback.from_address(id(next))
- next.ob_refcnt += 1
- obj.tb_next = ctypes.pointer(next)
-
- return tb_set_next
-
-
-# try to get a tb_set_next implementation
-try:
- from jinja2._debugsupport import tb_set_next
-except ImportError:
- try:
- tb_set_next = _init_ugly_crap()
- except:
- tb_set_next = None
-del _init_ugly_crap
diff --git a/module/lib/jinja2/defaults.py b/module/lib/jinja2/defaults.py
deleted file mode 100644
index d2d45443a..000000000
--- a/module/lib/jinja2/defaults.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.defaults
- ~~~~~~~~~~~~~~~
-
- Jinja default filters and tags.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner
-
-
-# defaults for the parser / lexer
-BLOCK_START_STRING = '{%'
-BLOCK_END_STRING = '%}'
-VARIABLE_START_STRING = '{{'
-VARIABLE_END_STRING = '}}'
-COMMENT_START_STRING = '{#'
-COMMENT_END_STRING = '#}'
-LINE_STATEMENT_PREFIX = None
-LINE_COMMENT_PREFIX = None
-TRIM_BLOCKS = False
-NEWLINE_SEQUENCE = '\n'
-
-
-# default filters, tests and namespace
-from jinja2.filters import FILTERS as DEFAULT_FILTERS
-from jinja2.tests import TESTS as DEFAULT_TESTS
-DEFAULT_NAMESPACE = {
- 'range': xrange,
- 'dict': lambda **kw: kw,
- 'lipsum': generate_lorem_ipsum,
- 'cycler': Cycler,
- 'joiner': Joiner
-}
-
-
-# export all constants
-__all__ = tuple(x for x in locals().keys() if x.isupper())
diff --git a/module/lib/jinja2/environment.py b/module/lib/jinja2/environment.py
deleted file mode 100644
index ac74a5c68..000000000
--- a/module/lib/jinja2/environment.py
+++ /dev/null
@@ -1,1118 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.environment
- ~~~~~~~~~~~~~~~~~~
-
- Provides a class that holds runtime and parsing time options.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import os
-import sys
-from jinja2 import nodes
-from jinja2.defaults import *
-from jinja2.lexer import get_lexer, TokenStream
-from jinja2.parser import Parser
-from jinja2.optimizer import optimize
-from jinja2.compiler import generate
-from jinja2.runtime import Undefined, new_context
-from jinja2.exceptions import TemplateSyntaxError, TemplateNotFound, \
- TemplatesNotFound
-from jinja2.utils import import_string, LRUCache, Markup, missing, \
- concat, consume, internalcode, _encode_filename
-
-
-# for direct template usage we have up to ten living environments
-_spontaneous_environments = LRUCache(10)
-
-# the function to create jinja traceback objects. This is dynamically
-# imported on the first exception in the exception handler.
-_make_traceback = None
-
-
-def get_spontaneous_environment(*args):
- """Return a new spontaneous environment. A spontaneous environment is an
- unnamed and unaccessible (in theory) environment that is used for
- templates generated from a string and not from the file system.
- """
- try:
- env = _spontaneous_environments.get(args)
- except TypeError:
- return Environment(*args)
- if env is not None:
- return env
- _spontaneous_environments[args] = env = Environment(*args)
- env.shared = True
- return env
-
-
-def create_cache(size):
- """Return the cache class for the given size."""
- if size == 0:
- return None
- if size < 0:
- return {}
- return LRUCache(size)
-
-
-def copy_cache(cache):
- """Create an empty copy of the given cache."""
- if cache is None:
- return None
- elif type(cache) is dict:
- return {}
- return LRUCache(cache.capacity)
-
-
-def load_extensions(environment, extensions):
- """Load the extensions from the list and bind it to the environment.
- Returns a dict of instanciated environments.
- """
- result = {}
- for extension in extensions:
- if isinstance(extension, basestring):
- extension = import_string(extension)
- result[extension.identifier] = extension(environment)
- return result
-
-
-def _environment_sanity_check(environment):
- """Perform a sanity check on the environment."""
- assert issubclass(environment.undefined, Undefined), 'undefined must ' \
- 'be a subclass of undefined because filters depend on it.'
- assert environment.block_start_string != \
- environment.variable_start_string != \
- environment.comment_start_string, 'block, variable and comment ' \
- 'start strings must be different'
- assert environment.newline_sequence in ('\r', '\r\n', '\n'), \
- 'newline_sequence set to unknown line ending string.'
- return environment
-
-
-class Environment(object):
- r"""The core component of Jinja is the `Environment`. It contains
- important shared variables like configuration, filters, tests,
- globals and others. Instances of this class may be modified if
- they are not shared and if no template was loaded so far.
- Modifications on environments after the first template was loaded
- will lead to surprising effects and undefined behavior.
-
- Here the possible initialization parameters:
-
- `block_start_string`
- The string marking the begin of a block. Defaults to ``'{%'``.
-
- `block_end_string`
- The string marking the end of a block. Defaults to ``'%}'``.
-
- `variable_start_string`
- The string marking the begin of a print statement.
- Defaults to ``'{{'``.
-
- `variable_end_string`
- The string marking the end of a print statement. Defaults to
- ``'}}'``.
-
- `comment_start_string`
- The string marking the begin of a comment. Defaults to ``'{#'``.
-
- `comment_end_string`
- The string marking the end of a comment. Defaults to ``'#}'``.
-
- `line_statement_prefix`
- If given and a string, this will be used as prefix for line based
- statements. See also :ref:`line-statements`.
-
- `line_comment_prefix`
- If given and a string, this will be used as prefix for line based
- based comments. See also :ref:`line-statements`.
-
- .. versionadded:: 2.2
-
- `trim_blocks`
- If this is set to ``True`` the first newline after a block is
- removed (block, not variable tag!). Defaults to `False`.
-
- `newline_sequence`
- The sequence that starts a newline. Must be one of ``'\r'``,
- ``'\n'`` or ``'\r\n'``. The default is ``'\n'`` which is a
- useful default for Linux and OS X systems as well as web
- applications.
-
- `extensions`
- List of Jinja extensions to use. This can either be import paths
- as strings or extension classes. For more information have a
- look at :ref:`the extensions documentation <jinja-extensions>`.
-
- `optimized`
- should the optimizer be enabled? Default is `True`.
-
- `undefined`
- :class:`Undefined` or a subclass of it that is used to represent
- undefined values in the template.
-
- `finalize`
- A callable that can be used to process the result of a variable
- expression before it is output. For example one can convert
- `None` implicitly into an empty string here.
-
- `autoescape`
- If set to true the XML/HTML autoescaping feature is enabled by
- default. For more details about auto escaping see
- :class:`~jinja2.utils.Markup`. As of Jinja 2.4 this can also
- be a callable that is passed the template name and has to
- return `True` or `False` depending on autoescape should be
- enabled by default.
-
- .. versionchanged:: 2.4
- `autoescape` can now be a function
-
- `loader`
- The template loader for this environment.
-
- `cache_size`
- The size of the cache. Per default this is ``50`` which means
- that if more than 50 templates are loaded the loader will clean
- out the least recently used template. If the cache size is set to
- ``0`` templates are recompiled all the time, if the cache size is
- ``-1`` the cache will not be cleaned.
-
- `auto_reload`
- Some loaders load templates from locations where the template
- sources may change (ie: file system or database). If
- `auto_reload` is set to `True` (default) every time a template is
- requested the loader checks if the source changed and if yes, it
- will reload the template. For higher performance it's possible to
- disable that.
-
- `bytecode_cache`
- If set to a bytecode cache object, this object will provide a
- cache for the internal Jinja bytecode so that templates don't
- have to be parsed if they were not changed.
-
- See :ref:`bytecode-cache` for more information.
- """
-
- #: if this environment is sandboxed. Modifying this variable won't make
- #: the environment sandboxed though. For a real sandboxed environment
- #: have a look at jinja2.sandbox
- sandboxed = False
-
- #: True if the environment is just an overlay
- overlayed = False
-
- #: the environment this environment is linked to if it is an overlay
- linked_to = None
-
- #: shared environments have this set to `True`. A shared environment
- #: must not be modified
- shared = False
-
- #: these are currently EXPERIMENTAL undocumented features.
- exception_handler = None
- exception_formatter = None
-
- def __init__(self,
- block_start_string=BLOCK_START_STRING,
- block_end_string=BLOCK_END_STRING,
- variable_start_string=VARIABLE_START_STRING,
- variable_end_string=VARIABLE_END_STRING,
- comment_start_string=COMMENT_START_STRING,
- comment_end_string=COMMENT_END_STRING,
- line_statement_prefix=LINE_STATEMENT_PREFIX,
- line_comment_prefix=LINE_COMMENT_PREFIX,
- trim_blocks=TRIM_BLOCKS,
- newline_sequence=NEWLINE_SEQUENCE,
- extensions=(),
- optimized=True,
- undefined=Undefined,
- finalize=None,
- autoescape=False,
- loader=None,
- cache_size=50,
- auto_reload=True,
- bytecode_cache=None):
- # !!Important notice!!
- # The constructor accepts quite a few arguments that should be
- # passed by keyword rather than position. However it's important to
- # not change the order of arguments because it's used at least
- # internally in those cases:
- # - spontaneus environments (i18n extension and Template)
- # - unittests
- # If parameter changes are required only add parameters at the end
- # and don't change the arguments (or the defaults!) of the arguments
- # existing already.
-
- # lexer / parser information
- self.block_start_string = block_start_string
- self.block_end_string = block_end_string
- self.variable_start_string = variable_start_string
- self.variable_end_string = variable_end_string
- self.comment_start_string = comment_start_string
- self.comment_end_string = comment_end_string
- self.line_statement_prefix = line_statement_prefix
- self.line_comment_prefix = line_comment_prefix
- self.trim_blocks = trim_blocks
- self.newline_sequence = newline_sequence
-
- # runtime information
- self.undefined = undefined
- self.optimized = optimized
- self.finalize = finalize
- self.autoescape = autoescape
-
- # defaults
- self.filters = DEFAULT_FILTERS.copy()
- self.tests = DEFAULT_TESTS.copy()
- self.globals = DEFAULT_NAMESPACE.copy()
-
- # set the loader provided
- self.loader = loader
- self.bytecode_cache = None
- self.cache = create_cache(cache_size)
- self.bytecode_cache = bytecode_cache
- self.auto_reload = auto_reload
-
- # load extensions
- self.extensions = load_extensions(self, extensions)
-
- _environment_sanity_check(self)
-
- def add_extension(self, extension):
- """Adds an extension after the environment was created.
-
- .. versionadded:: 2.5
- """
- self.extensions.update(load_extensions(self, [extension]))
-
- def extend(self, **attributes):
- """Add the items to the instance of the environment if they do not exist
- yet. This is used by :ref:`extensions <writing-extensions>` to register
- callbacks and configuration values without breaking inheritance.
- """
- for key, value in attributes.iteritems():
- if not hasattr(self, key):
- setattr(self, key, value)
-
- def overlay(self, block_start_string=missing, block_end_string=missing,
- variable_start_string=missing, variable_end_string=missing,
- comment_start_string=missing, comment_end_string=missing,
- line_statement_prefix=missing, line_comment_prefix=missing,
- trim_blocks=missing, extensions=missing, optimized=missing,
- undefined=missing, finalize=missing, autoescape=missing,
- loader=missing, cache_size=missing, auto_reload=missing,
- bytecode_cache=missing):
- """Create a new overlay environment that shares all the data with the
- current environment except of cache and the overridden attributes.
- Extensions cannot be removed for an overlayed environment. An overlayed
- environment automatically gets all the extensions of the environment it
- is linked to plus optional extra extensions.
-
- Creating overlays should happen after the initial environment was set
- up completely. Not all attributes are truly linked, some are just
- copied over so modifications on the original environment may not shine
- through.
- """
- args = dict(locals())
- del args['self'], args['cache_size'], args['extensions']
-
- rv = object.__new__(self.__class__)
- rv.__dict__.update(self.__dict__)
- rv.overlayed = True
- rv.linked_to = self
-
- for key, value in args.iteritems():
- if value is not missing:
- setattr(rv, key, value)
-
- if cache_size is not missing:
- rv.cache = create_cache(cache_size)
- else:
- rv.cache = copy_cache(self.cache)
-
- rv.extensions = {}
- for key, value in self.extensions.iteritems():
- rv.extensions[key] = value.bind(rv)
- if extensions is not missing:
- rv.extensions.update(load_extensions(rv, extensions))
-
- return _environment_sanity_check(rv)
-
- lexer = property(get_lexer, doc="The lexer for this environment.")
-
- def iter_extensions(self):
- """Iterates over the extensions by priority."""
- return iter(sorted(self.extensions.values(),
- key=lambda x: x.priority))
-
- def getitem(self, obj, argument):
- """Get an item or attribute of an object but prefer the item."""
- try:
- return obj[argument]
- except (TypeError, LookupError):
- if isinstance(argument, basestring):
- try:
- attr = str(argument)
- except:
- pass
- else:
- try:
- return getattr(obj, attr)
- except AttributeError:
- pass
- return self.undefined(obj=obj, name=argument)
-
- def getattr(self, obj, attribute):
- """Get an item or attribute of an object but prefer the attribute.
- Unlike :meth:`getitem` the attribute *must* be a bytestring.
- """
- try:
- return getattr(obj, attribute)
- except AttributeError:
- pass
- try:
- return obj[attribute]
- except (TypeError, LookupError, AttributeError):
- return self.undefined(obj=obj, name=attribute)
-
- @internalcode
- def parse(self, source, name=None, filename=None):
- """Parse the sourcecode and return the abstract syntax tree. This
- tree of nodes is used by the compiler to convert the template into
- executable source- or bytecode. This is useful for debugging or to
- extract information from templates.
-
- If you are :ref:`developing Jinja2 extensions <writing-extensions>`
- this gives you a good overview of the node tree generated.
- """
- try:
- return self._parse(source, name, filename)
- except TemplateSyntaxError:
- exc_info = sys.exc_info()
- self.handle_exception(exc_info, source_hint=source)
-
- def _parse(self, source, name, filename):
- """Internal parsing function used by `parse` and `compile`."""
- return Parser(self, source, name, _encode_filename(filename)).parse()
-
- def lex(self, source, name=None, filename=None):
- """Lex the given sourcecode and return a generator that yields
- tokens as tuples in the form ``(lineno, token_type, value)``.
- This can be useful for :ref:`extension development <writing-extensions>`
- and debugging templates.
-
- This does not perform preprocessing. If you want the preprocessing
- of the extensions to be applied you have to filter source through
- the :meth:`preprocess` method.
- """
- source = unicode(source)
- try:
- return self.lexer.tokeniter(source, name, filename)
- except TemplateSyntaxError:
- exc_info = sys.exc_info()
- self.handle_exception(exc_info, source_hint=source)
-
- def preprocess(self, source, name=None, filename=None):
- """Preprocesses the source with all extensions. This is automatically
- called for all parsing and compiling methods but *not* for :meth:`lex`
- because there you usually only want the actual source tokenized.
- """
- return reduce(lambda s, e: e.preprocess(s, name, filename),
- self.iter_extensions(), unicode(source))
-
- def _tokenize(self, source, name, filename=None, state=None):
- """Called by the parser to do the preprocessing and filtering
- for all the extensions. Returns a :class:`~jinja2.lexer.TokenStream`.
- """
- source = self.preprocess(source, name, filename)
- stream = self.lexer.tokenize(source, name, filename, state)
- for ext in self.iter_extensions():
- stream = ext.filter_stream(stream)
- if not isinstance(stream, TokenStream):
- stream = TokenStream(stream, name, filename)
- return stream
-
- def _generate(self, source, name, filename, defer_init=False):
- """Internal hook that can be overriden to hook a different generate
- method in.
-
- .. versionadded:: 2.5
- """
- return generate(source, self, name, filename, defer_init=defer_init)
-
- def _compile(self, source, filename):
- """Internal hook that can be overriden to hook a different compile
- method in.
-
- .. versionadded:: 2.5
- """
- return compile(source, filename, 'exec')
-
- @internalcode
- def compile(self, source, name=None, filename=None, raw=False,
- defer_init=False):
- """Compile a node or template source code. The `name` parameter is
- the load name of the template after it was joined using
- :meth:`join_path` if necessary, not the filename on the file system.
- the `filename` parameter is the estimated filename of the template on
- the file system. If the template came from a database or memory this
- can be omitted.
-
- The return value of this method is a python code object. If the `raw`
- parameter is `True` the return value will be a string with python
- code equivalent to the bytecode returned otherwise. This method is
- mainly used internally.
-
- `defer_init` is use internally to aid the module code generator. This
- causes the generated code to be able to import without the global
- environment variable to be set.
-
- .. versionadded:: 2.4
- `defer_init` parameter added.
- """
- source_hint = None
- try:
- if isinstance(source, basestring):
- source_hint = source
- source = self._parse(source, name, filename)
- if self.optimized:
- source = optimize(source, self)
- source = self._generate(source, name, filename,
- defer_init=defer_init)
- if raw:
- return source
- if filename is None:
- filename = '<template>'
- else:
- filename = _encode_filename(filename)
- return self._compile(source, filename)
- except TemplateSyntaxError:
- exc_info = sys.exc_info()
- self.handle_exception(exc_info, source_hint=source)
-
- def compile_expression(self, source, undefined_to_none=True):
- """A handy helper method that returns a callable that accepts keyword
- arguments that appear as variables in the expression. If called it
- returns the result of the expression.
-
- This is useful if applications want to use the same rules as Jinja
- in template "configuration files" or similar situations.
-
- Example usage:
-
- >>> env = Environment()
- >>> expr = env.compile_expression('foo == 42')
- >>> expr(foo=23)
- False
- >>> expr(foo=42)
- True
-
- Per default the return value is converted to `None` if the
- expression returns an undefined value. This can be changed
- by setting `undefined_to_none` to `False`.
-
- >>> env.compile_expression('var')() is None
- True
- >>> env.compile_expression('var', undefined_to_none=False)()
- Undefined
-
- .. versionadded:: 2.1
- """
- parser = Parser(self, source, state='variable')
- exc_info = None
- try:
- expr = parser.parse_expression()
- if not parser.stream.eos:
- raise TemplateSyntaxError('chunk after expression',
- parser.stream.current.lineno,
- None, None)
- expr.set_environment(self)
- except TemplateSyntaxError:
- exc_info = sys.exc_info()
- if exc_info is not None:
- self.handle_exception(exc_info, source_hint=source)
- body = [nodes.Assign(nodes.Name('result', 'store'), expr, lineno=1)]
- template = self.from_string(nodes.Template(body, lineno=1))
- return TemplateExpression(template, undefined_to_none)
-
- def compile_templates(self, target, extensions=None, filter_func=None,
- zip='deflated', log_function=None,
- ignore_errors=True, py_compile=False):
- """Compiles all the templates the loader can find, compiles them
- and stores them in `target`. If `zip` is `None`, instead of in a
- zipfile, the templates will be will be stored in a directory.
- By default a deflate zip algorithm is used, to switch to
- the stored algorithm, `zip` can be set to ``'stored'``.
-
- `extensions` and `filter_func` are passed to :meth:`list_templates`.
- Each template returned will be compiled to the target folder or
- zipfile.
-
- By default template compilation errors are ignored. In case a
- log function is provided, errors are logged. If you want template
- syntax errors to abort the compilation you can set `ignore_errors`
- to `False` and you will get an exception on syntax errors.
-
- If `py_compile` is set to `True` .pyc files will be written to the
- target instead of standard .py files.
-
- .. versionadded:: 2.4
- """
- from jinja2.loaders import ModuleLoader
-
- if log_function is None:
- log_function = lambda x: None
-
- if py_compile:
- import imp, struct, marshal
- py_header = imp.get_magic() + \
- u'\xff\xff\xff\xff'.encode('iso-8859-15')
-
- def write_file(filename, data, mode):
- if zip:
- info = ZipInfo(filename)
- info.external_attr = 0755 << 16L
- zip_file.writestr(info, data)
- else:
- f = open(os.path.join(target, filename), mode)
- try:
- f.write(data)
- finally:
- f.close()
-
- if zip is not None:
- from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED, ZIP_STORED
- zip_file = ZipFile(target, 'w', dict(deflated=ZIP_DEFLATED,
- stored=ZIP_STORED)[zip])
- log_function('Compiling into Zip archive "%s"' % target)
- else:
- if not os.path.isdir(target):
- os.makedirs(target)
- log_function('Compiling into folder "%s"' % target)
-
- try:
- for name in self.list_templates(extensions, filter_func):
- source, filename, _ = self.loader.get_source(self, name)
- try:
- code = self.compile(source, name, filename, True, True)
- except TemplateSyntaxError, e:
- if not ignore_errors:
- raise
- log_function('Could not compile "%s": %s' % (name, e))
- continue
-
- filename = ModuleLoader.get_module_filename(name)
-
- if py_compile:
- c = self._compile(code, _encode_filename(filename))
- write_file(filename + 'c', py_header +
- marshal.dumps(c), 'wb')
- log_function('Byte-compiled "%s" as %s' %
- (name, filename + 'c'))
- else:
- write_file(filename, code, 'w')
- log_function('Compiled "%s" as %s' % (name, filename))
- finally:
- if zip:
- zip_file.close()
-
- log_function('Finished compiling templates')
-
- def list_templates(self, extensions=None, filter_func=None):
- """Returns a list of templates for this environment. This requires
- that the loader supports the loader's
- :meth:`~BaseLoader.list_templates` method.
-
- If there are other files in the template folder besides the
- actual templates, the returned list can be filtered. There are two
- ways: either `extensions` is set to a list of file extensions for
- templates, or a `filter_func` can be provided which is a callable that
- is passed a template name and should return `True` if it should end up
- in the result list.
-
- If the loader does not support that, a :exc:`TypeError` is raised.
- """
- x = self.loader.list_templates()
- if extensions is not None:
- if filter_func is not None:
- raise TypeError('either extensions or filter_func '
- 'can be passed, but not both')
- filter_func = lambda x: '.' in x and \
- x.rsplit('.', 1)[1] in extensions
- if filter_func is not None:
- x = filter(filter_func, x)
- return x
-
- def handle_exception(self, exc_info=None, rendered=False, source_hint=None):
- """Exception handling helper. This is used internally to either raise
- rewritten exceptions or return a rendered traceback for the template.
- """
- global _make_traceback
- if exc_info is None:
- exc_info = sys.exc_info()
-
- # the debugging module is imported when it's used for the first time.
- # we're doing a lot of stuff there and for applications that do not
- # get any exceptions in template rendering there is no need to load
- # all of that.
- if _make_traceback is None:
- from jinja2.debug import make_traceback as _make_traceback
- traceback = _make_traceback(exc_info, source_hint)
- if rendered and self.exception_formatter is not None:
- return self.exception_formatter(traceback)
- if self.exception_handler is not None:
- self.exception_handler(traceback)
- exc_type, exc_value, tb = traceback.standard_exc_info
- raise exc_type, exc_value, tb
-
- def join_path(self, template, parent):
- """Join a template with the parent. By default all the lookups are
- relative to the loader root so this method returns the `template`
- parameter unchanged, but if the paths should be relative to the
- parent template, this function can be used to calculate the real
- template name.
-
- Subclasses may override this method and implement template path
- joining here.
- """
- return template
-
- @internalcode
- def _load_template(self, name, globals):
- if self.loader is None:
- raise TypeError('no loader for this environment specified')
- if self.cache is not None:
- template = self.cache.get(name)
- if template is not None and (not self.auto_reload or \
- template.is_up_to_date):
- return template
- template = self.loader.load(self, name, globals)
- if self.cache is not None:
- self.cache[name] = template
- return template
-
- @internalcode
- def get_template(self, name, parent=None, globals=None):
- """Load a template from the loader. If a loader is configured this
- method ask the loader for the template and returns a :class:`Template`.
- If the `parent` parameter is not `None`, :meth:`join_path` is called
- to get the real template name before loading.
-
- The `globals` parameter can be used to provide template wide globals.
- These variables are available in the context at render time.
-
- If the template does not exist a :exc:`TemplateNotFound` exception is
- raised.
-
- .. versionchanged:: 2.4
- If `name` is a :class:`Template` object it is returned from the
- function unchanged.
- """
- if isinstance(name, Template):
- return name
- if parent is not None:
- name = self.join_path(name, parent)
- return self._load_template(name, self.make_globals(globals))
-
- @internalcode
- def select_template(self, names, parent=None, globals=None):
- """Works like :meth:`get_template` but tries a number of templates
- before it fails. If it cannot find any of the templates, it will
- raise a :exc:`TemplatesNotFound` exception.
-
- .. versionadded:: 2.3
-
- .. versionchanged:: 2.4
- If `names` contains a :class:`Template` object it is returned
- from the function unchanged.
- """
- if not names:
- raise TemplatesNotFound(message=u'Tried to select from an empty list '
- u'of templates.')
- globals = self.make_globals(globals)
- for name in names:
- if isinstance(name, Template):
- return name
- if parent is not None:
- name = self.join_path(name, parent)
- try:
- return self._load_template(name, globals)
- except TemplateNotFound:
- pass
- raise TemplatesNotFound(names)
-
- @internalcode
- def get_or_select_template(self, template_name_or_list,
- parent=None, globals=None):
- """Does a typecheck and dispatches to :meth:`select_template`
- if an iterable of template names is given, otherwise to
- :meth:`get_template`.
-
- .. versionadded:: 2.3
- """
- if isinstance(template_name_or_list, basestring):
- return self.get_template(template_name_or_list, parent, globals)
- elif isinstance(template_name_or_list, Template):
- return template_name_or_list
- return self.select_template(template_name_or_list, parent, globals)
-
- def from_string(self, source, globals=None, template_class=None):
- """Load a template from a string. This parses the source given and
- returns a :class:`Template` object.
- """
- globals = self.make_globals(globals)
- cls = template_class or self.template_class
- return cls.from_code(self, self.compile(source), globals, None)
-
- def make_globals(self, d):
- """Return a dict for the globals."""
- if not d:
- return self.globals
- return dict(self.globals, **d)
-
-
-class Template(object):
- """The central template object. This class represents a compiled template
- and is used to evaluate it.
-
- Normally the template object is generated from an :class:`Environment` but
- it also has a constructor that makes it possible to create a template
- instance directly using the constructor. It takes the same arguments as
- the environment constructor but it's not possible to specify a loader.
-
- Every template object has a few methods and members that are guaranteed
- to exist. However it's important that a template object should be
- considered immutable. Modifications on the object are not supported.
-
- Template objects created from the constructor rather than an environment
- do have an `environment` attribute that points to a temporary environment
- that is probably shared with other templates created with the constructor
- and compatible settings.
-
- >>> template = Template('Hello {{ name }}!')
- >>> template.render(name='John Doe')
- u'Hello John Doe!'
-
- >>> stream = template.stream(name='John Doe')
- >>> stream.next()
- u'Hello John Doe!'
- >>> stream.next()
- Traceback (most recent call last):
- ...
- StopIteration
- """
-
- def __new__(cls, source,
- block_start_string=BLOCK_START_STRING,
- block_end_string=BLOCK_END_STRING,
- variable_start_string=VARIABLE_START_STRING,
- variable_end_string=VARIABLE_END_STRING,
- comment_start_string=COMMENT_START_STRING,
- comment_end_string=COMMENT_END_STRING,
- line_statement_prefix=LINE_STATEMENT_PREFIX,
- line_comment_prefix=LINE_COMMENT_PREFIX,
- trim_blocks=TRIM_BLOCKS,
- newline_sequence=NEWLINE_SEQUENCE,
- extensions=(),
- optimized=True,
- undefined=Undefined,
- finalize=None,
- autoescape=False):
- env = get_spontaneous_environment(
- block_start_string, block_end_string, variable_start_string,
- variable_end_string, comment_start_string, comment_end_string,
- line_statement_prefix, line_comment_prefix, trim_blocks,
- newline_sequence, frozenset(extensions), optimized, undefined,
- finalize, autoescape, None, 0, False, None)
- return env.from_string(source, template_class=cls)
-
- @classmethod
- def from_code(cls, environment, code, globals, uptodate=None):
- """Creates a template object from compiled code and the globals. This
- is used by the loaders and environment to create a template object.
- """
- namespace = {
- 'environment': environment,
- '__file__': code.co_filename
- }
- exec code in namespace
- rv = cls._from_namespace(environment, namespace, globals)
- rv._uptodate = uptodate
- return rv
-
- @classmethod
- def from_module_dict(cls, environment, module_dict, globals):
- """Creates a template object from a module. This is used by the
- module loader to create a template object.
-
- .. versionadded:: 2.4
- """
- return cls._from_namespace(environment, module_dict, globals)
-
- @classmethod
- def _from_namespace(cls, environment, namespace, globals):
- t = object.__new__(cls)
- t.environment = environment
- t.globals = globals
- t.name = namespace['name']
- t.filename = namespace['__file__']
- t.blocks = namespace['blocks']
-
- # render function and module
- t.root_render_func = namespace['root']
- t._module = None
-
- # debug and loader helpers
- t._debug_info = namespace['debug_info']
- t._uptodate = None
-
- # store the reference
- namespace['environment'] = environment
- namespace['__jinja_template__'] = t
-
- return t
-
- def render(self, *args, **kwargs):
- """This method accepts the same arguments as the `dict` constructor:
- A dict, a dict subclass or some keyword arguments. If no arguments
- are given the context will be empty. These two calls do the same::
-
- template.render(knights='that say nih')
- template.render({'knights': 'that say nih'})
-
- This will return the rendered template as unicode string.
- """
- vars = dict(*args, **kwargs)
- try:
- return concat(self.root_render_func(self.new_context(vars)))
- except:
- exc_info = sys.exc_info()
- return self.environment.handle_exception(exc_info, True)
-
- def stream(self, *args, **kwargs):
- """Works exactly like :meth:`generate` but returns a
- :class:`TemplateStream`.
- """
- return TemplateStream(self.generate(*args, **kwargs))
-
- def generate(self, *args, **kwargs):
- """For very large templates it can be useful to not render the whole
- template at once but evaluate each statement after another and yield
- piece for piece. This method basically does exactly that and returns
- a generator that yields one item after another as unicode strings.
-
- It accepts the same arguments as :meth:`render`.
- """
- vars = dict(*args, **kwargs)
- try:
- for event in self.root_render_func(self.new_context(vars)):
- yield event
- except:
- exc_info = sys.exc_info()
- else:
- return
- yield self.environment.handle_exception(exc_info, True)
-
- def new_context(self, vars=None, shared=False, locals=None):
- """Create a new :class:`Context` for this template. The vars
- provided will be passed to the template. Per default the globals
- are added to the context. If shared is set to `True` the data
- is passed as it to the context without adding the globals.
-
- `locals` can be a dict of local variables for internal usage.
- """
- return new_context(self.environment, self.name, self.blocks,
- vars, shared, self.globals, locals)
-
- def make_module(self, vars=None, shared=False, locals=None):
- """This method works like the :attr:`module` attribute when called
- without arguments but it will evaluate the template on every call
- rather than caching it. It's also possible to provide
- a dict which is then used as context. The arguments are the same
- as for the :meth:`new_context` method.
- """
- return TemplateModule(self, self.new_context(vars, shared, locals))
-
- @property
- def module(self):
- """The template as module. This is used for imports in the
- template runtime but is also useful if one wants to access
- exported template variables from the Python layer:
-
- >>> t = Template('{% macro foo() %}42{% endmacro %}23')
- >>> unicode(t.module)
- u'23'
- >>> t.module.foo()
- u'42'
- """
- if self._module is not None:
- return self._module
- self._module = rv = self.make_module()
- return rv
-
- def get_corresponding_lineno(self, lineno):
- """Return the source line number of a line number in the
- generated bytecode as they are not in sync.
- """
- for template_line, code_line in reversed(self.debug_info):
- if code_line <= lineno:
- return template_line
- return 1
-
- @property
- def is_up_to_date(self):
- """If this variable is `False` there is a newer version available."""
- if self._uptodate is None:
- return True
- return self._uptodate()
-
- @property
- def debug_info(self):
- """The debug info mapping."""
- return [tuple(map(int, x.split('='))) for x in
- self._debug_info.split('&')]
-
- def __repr__(self):
- if self.name is None:
- name = 'memory:%x' % id(self)
- else:
- name = repr(self.name)
- return '<%s %s>' % (self.__class__.__name__, name)
-
-
-class TemplateModule(object):
- """Represents an imported template. All the exported names of the
- template are available as attributes on this object. Additionally
- converting it into an unicode- or bytestrings renders the contents.
- """
-
- def __init__(self, template, context):
- self._body_stream = list(template.root_render_func(context))
- self.__dict__.update(context.get_exported())
- self.__name__ = template.name
-
- def __html__(self):
- return Markup(concat(self._body_stream))
-
- def __str__(self):
- return unicode(self).encode('utf-8')
-
- # unicode goes after __str__ because we configured 2to3 to rename
- # __unicode__ to __str__. because the 2to3 tree is not designed to
- # remove nodes from it, we leave the above __str__ around and let
- # it override at runtime.
- def __unicode__(self):
- return concat(self._body_stream)
-
- def __repr__(self):
- if self.__name__ is None:
- name = 'memory:%x' % id(self)
- else:
- name = repr(self.__name__)
- return '<%s %s>' % (self.__class__.__name__, name)
-
-
-class TemplateExpression(object):
- """The :meth:`jinja2.Environment.compile_expression` method returns an
- instance of this object. It encapsulates the expression-like access
- to the template with an expression it wraps.
- """
-
- def __init__(self, template, undefined_to_none):
- self._template = template
- self._undefined_to_none = undefined_to_none
-
- def __call__(self, *args, **kwargs):
- context = self._template.new_context(dict(*args, **kwargs))
- consume(self._template.root_render_func(context))
- rv = context.vars['result']
- if self._undefined_to_none and isinstance(rv, Undefined):
- rv = None
- return rv
-
-
-class TemplateStream(object):
- """A template stream works pretty much like an ordinary python generator
- but it can buffer multiple items to reduce the number of total iterations.
- Per default the output is unbuffered which means that for every unbuffered
- instruction in the template one unicode string is yielded.
-
- If buffering is enabled with a buffer size of 5, five items are combined
- into a new unicode string. This is mainly useful if you are streaming
- big templates to a client via WSGI which flushes after each iteration.
- """
-
- def __init__(self, gen):
- self._gen = gen
- self.disable_buffering()
-
- def dump(self, fp, encoding=None, errors='strict'):
- """Dump the complete stream into a file or file-like object.
- Per default unicode strings are written, if you want to encode
- before writing specifiy an `encoding`.
-
- Example usage::
-
- Template('Hello {{ name }}!').stream(name='foo').dump('hello.html')
- """
- close = False
- if isinstance(fp, basestring):
- fp = file(fp, 'w')
- close = True
- try:
- if encoding is not None:
- iterable = (x.encode(encoding, errors) for x in self)
- else:
- iterable = self
- if hasattr(fp, 'writelines'):
- fp.writelines(iterable)
- else:
- for item in iterable:
- fp.write(item)
- finally:
- if close:
- fp.close()
-
- def disable_buffering(self):
- """Disable the output buffering."""
- self._next = self._gen.next
- self.buffered = False
-
- def enable_buffering(self, size=5):
- """Enable buffering. Buffer `size` items before yielding them."""
- if size <= 1:
- raise ValueError('buffer size too small')
-
- def generator(next):
- buf = []
- c_size = 0
- push = buf.append
-
- while 1:
- try:
- while c_size < size:
- c = next()
- push(c)
- if c:
- c_size += 1
- except StopIteration:
- if not c_size:
- return
- yield concat(buf)
- del buf[:]
- c_size = 0
-
- self.buffered = True
- self._next = generator(self._gen.next).next
-
- def __iter__(self):
- return self
-
- def next(self):
- return self._next()
-
-
-# hook in default template class. if anyone reads this comment: ignore that
-# it's possible to use custom templates ;-)
-Environment.template_class = Template
diff --git a/module/lib/jinja2/exceptions.py b/module/lib/jinja2/exceptions.py
deleted file mode 100644
index 771f6a8d7..000000000
--- a/module/lib/jinja2/exceptions.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.exceptions
- ~~~~~~~~~~~~~~~~~
-
- Jinja exceptions.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-
-
-class TemplateError(Exception):
- """Baseclass for all template errors."""
-
- def __init__(self, message=None):
- if message is not None:
- message = unicode(message).encode('utf-8')
- Exception.__init__(self, message)
-
- @property
- def message(self):
- if self.args:
- message = self.args[0]
- if message is not None:
- return message.decode('utf-8', 'replace')
-
-
-class TemplateNotFound(IOError, LookupError, TemplateError):
- """Raised if a template does not exist."""
-
- # looks weird, but removes the warning descriptor that just
- # bogusly warns us about message being deprecated
- message = None
-
- def __init__(self, name, message=None):
- IOError.__init__(self)
- if message is None:
- message = name
- self.message = message
- self.name = name
- self.templates = [name]
-
- def __str__(self):
- return self.message.encode('utf-8')
-
- # unicode goes after __str__ because we configured 2to3 to rename
- # __unicode__ to __str__. because the 2to3 tree is not designed to
- # remove nodes from it, we leave the above __str__ around and let
- # it override at runtime.
- def __unicode__(self):
- return self.message
-
-
-class TemplatesNotFound(TemplateNotFound):
- """Like :class:`TemplateNotFound` but raised if multiple templates
- are selected. This is a subclass of :class:`TemplateNotFound`
- exception, so just catching the base exception will catch both.
-
- .. versionadded:: 2.2
- """
-
- def __init__(self, names=(), message=None):
- if message is None:
- message = u'non of the templates given were found: ' + \
- u', '.join(map(unicode, names))
- TemplateNotFound.__init__(self, names and names[-1] or None, message)
- self.templates = list(names)
-
-
-class TemplateSyntaxError(TemplateError):
- """Raised to tell the user that there is a problem with the template."""
-
- def __init__(self, message, lineno, name=None, filename=None):
- TemplateError.__init__(self, message)
- self.lineno = lineno
- self.name = name
- self.filename = filename
- self.source = None
-
- # this is set to True if the debug.translate_syntax_error
- # function translated the syntax error into a new traceback
- self.translated = False
-
- def __str__(self):
- return unicode(self).encode('utf-8')
-
- # unicode goes after __str__ because we configured 2to3 to rename
- # __unicode__ to __str__. because the 2to3 tree is not designed to
- # remove nodes from it, we leave the above __str__ around and let
- # it override at runtime.
- def __unicode__(self):
- # for translated errors we only return the message
- if self.translated:
- return self.message
-
- # otherwise attach some stuff
- location = 'line %d' % self.lineno
- name = self.filename or self.name
- if name:
- location = 'File "%s", %s' % (name, location)
- lines = [self.message, ' ' + location]
-
- # if the source is set, add the line to the output
- if self.source is not None:
- try:
- line = self.source.splitlines()[self.lineno - 1]
- except IndexError:
- line = None
- if line:
- lines.append(' ' + line.strip())
-
- return u'\n'.join(lines)
-
-
-class TemplateAssertionError(TemplateSyntaxError):
- """Like a template syntax error, but covers cases where something in the
- template caused an error at compile time that wasn't necessarily caused
- by a syntax error. However it's a direct subclass of
- :exc:`TemplateSyntaxError` and has the same attributes.
- """
-
-
-class TemplateRuntimeError(TemplateError):
- """A generic runtime error in the template engine. Under some situations
- Jinja may raise this exception.
- """
-
-
-class UndefinedError(TemplateRuntimeError):
- """Raised if a template tries to operate on :class:`Undefined`."""
-
-
-class SecurityError(TemplateRuntimeError):
- """Raised if a template tries to do something insecure if the
- sandbox is enabled.
- """
-
-
-class FilterArgumentError(TemplateRuntimeError):
- """This error is raised if a filter was called with inappropriate
- arguments
- """
diff --git a/module/lib/jinja2/ext.py b/module/lib/jinja2/ext.py
deleted file mode 100644
index ceb38953a..000000000
--- a/module/lib/jinja2/ext.py
+++ /dev/null
@@ -1,610 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.ext
- ~~~~~~~~~~
-
- Jinja extensions allow to add custom tags similar to the way django custom
- tags work. By default two example extensions exist: an i18n and a cache
- extension.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD.
-"""
-from collections import deque
-from jinja2 import nodes
-from jinja2.defaults import *
-from jinja2.environment import Environment
-from jinja2.runtime import Undefined, concat
-from jinja2.exceptions import TemplateAssertionError, TemplateSyntaxError
-from jinja2.utils import contextfunction, import_string, Markup, next
-
-
-# the only real useful gettext functions for a Jinja template. Note
-# that ugettext must be assigned to gettext as Jinja doesn't support
-# non unicode strings.
-GETTEXT_FUNCTIONS = ('_', 'gettext', 'ngettext')
-
-
-class ExtensionRegistry(type):
- """Gives the extension an unique identifier."""
-
- def __new__(cls, name, bases, d):
- rv = type.__new__(cls, name, bases, d)
- rv.identifier = rv.__module__ + '.' + rv.__name__
- return rv
-
-
-class Extension(object):
- """Extensions can be used to add extra functionality to the Jinja template
- system at the parser level. Custom extensions are bound to an environment
- but may not store environment specific data on `self`. The reason for
- this is that an extension can be bound to another environment (for
- overlays) by creating a copy and reassigning the `environment` attribute.
-
- As extensions are created by the environment they cannot accept any
- arguments for configuration. One may want to work around that by using
- a factory function, but that is not possible as extensions are identified
- by their import name. The correct way to configure the extension is
- storing the configuration values on the environment. Because this way the
- environment ends up acting as central configuration storage the
- attributes may clash which is why extensions have to ensure that the names
- they choose for configuration are not too generic. ``prefix`` for example
- is a terrible name, ``fragment_cache_prefix`` on the other hand is a good
- name as includes the name of the extension (fragment cache).
- """
- __metaclass__ = ExtensionRegistry
-
- #: if this extension parses this is the list of tags it's listening to.
- tags = set()
-
- #: the priority of that extension. This is especially useful for
- #: extensions that preprocess values. A lower value means higher
- #: priority.
- #:
- #: .. versionadded:: 2.4
- priority = 100
-
- def __init__(self, environment):
- self.environment = environment
-
- def bind(self, environment):
- """Create a copy of this extension bound to another environment."""
- rv = object.__new__(self.__class__)
- rv.__dict__.update(self.__dict__)
- rv.environment = environment
- return rv
-
- def preprocess(self, source, name, filename=None):
- """This method is called before the actual lexing and can be used to
- preprocess the source. The `filename` is optional. The return value
- must be the preprocessed source.
- """
- return source
-
- def filter_stream(self, stream):
- """It's passed a :class:`~jinja2.lexer.TokenStream` that can be used
- to filter tokens returned. This method has to return an iterable of
- :class:`~jinja2.lexer.Token`\s, but it doesn't have to return a
- :class:`~jinja2.lexer.TokenStream`.
-
- In the `ext` folder of the Jinja2 source distribution there is a file
- called `inlinegettext.py` which implements a filter that utilizes this
- method.
- """
- return stream
-
- def parse(self, parser):
- """If any of the :attr:`tags` matched this method is called with the
- parser as first argument. The token the parser stream is pointing at
- is the name token that matched. This method has to return one or a
- list of multiple nodes.
- """
- raise NotImplementedError()
-
- def attr(self, name, lineno=None):
- """Return an attribute node for the current extension. This is useful
- to pass constants on extensions to generated template code::
-
- self.attr('_my_attribute', lineno=lineno)
- """
- return nodes.ExtensionAttribute(self.identifier, name, lineno=lineno)
-
- def call_method(self, name, args=None, kwargs=None, dyn_args=None,
- dyn_kwargs=None, lineno=None):
- """Call a method of the extension. This is a shortcut for
- :meth:`attr` + :class:`jinja2.nodes.Call`.
- """
- if args is None:
- args = []
- if kwargs is None:
- kwargs = []
- return nodes.Call(self.attr(name, lineno=lineno), args, kwargs,
- dyn_args, dyn_kwargs, lineno=lineno)
-
-
-@contextfunction
-def _gettext_alias(__context, *args, **kwargs):
- return __context.call(__context.resolve('gettext'), *args, **kwargs)
-
-
-def _make_new_gettext(func):
- @contextfunction
- def gettext(__context, __string, **variables):
- rv = __context.call(func, __string)
- if __context.eval_ctx.autoescape:
- rv = Markup(rv)
- return rv % variables
- return gettext
-
-
-def _make_new_ngettext(func):
- @contextfunction
- def ngettext(__context, __singular, __plural, __num, **variables):
- variables.setdefault('num', __num)
- rv = __context.call(func, __singular, __plural, __num)
- if __context.eval_ctx.autoescape:
- rv = Markup(rv)
- return rv % variables
- return ngettext
-
-
-class InternationalizationExtension(Extension):
- """This extension adds gettext support to Jinja2."""
- tags = set(['trans'])
-
- # TODO: the i18n extension is currently reevaluating values in a few
- # situations. Take this example:
- # {% trans count=something() %}{{ count }} foo{% pluralize
- # %}{{ count }} fooss{% endtrans %}
- # something is called twice here. One time for the gettext value and
- # the other time for the n-parameter of the ngettext function.
-
- def __init__(self, environment):
- Extension.__init__(self, environment)
- environment.globals['_'] = _gettext_alias
- environment.extend(
- install_gettext_translations=self._install,
- install_null_translations=self._install_null,
- install_gettext_callables=self._install_callables,
- uninstall_gettext_translations=self._uninstall,
- extract_translations=self._extract,
- newstyle_gettext=False
- )
-
- def _install(self, translations, newstyle=None):
- gettext = getattr(translations, 'ugettext', None)
- if gettext is None:
- gettext = translations.gettext
- ngettext = getattr(translations, 'ungettext', None)
- if ngettext is None:
- ngettext = translations.ngettext
- self._install_callables(gettext, ngettext, newstyle)
-
- def _install_null(self, newstyle=None):
- self._install_callables(
- lambda x: x,
- lambda s, p, n: (n != 1 and (p,) or (s,))[0],
- newstyle
- )
-
- def _install_callables(self, gettext, ngettext, newstyle=None):
- if newstyle is not None:
- self.environment.newstyle_gettext = newstyle
- if self.environment.newstyle_gettext:
- gettext = _make_new_gettext(gettext)
- ngettext = _make_new_ngettext(ngettext)
- self.environment.globals.update(
- gettext=gettext,
- ngettext=ngettext
- )
-
- def _uninstall(self, translations):
- for key in 'gettext', 'ngettext':
- self.environment.globals.pop(key, None)
-
- def _extract(self, source, gettext_functions=GETTEXT_FUNCTIONS):
- if isinstance(source, basestring):
- source = self.environment.parse(source)
- return extract_from_ast(source, gettext_functions)
-
- def parse(self, parser):
- """Parse a translatable tag."""
- lineno = next(parser.stream).lineno
- num_called_num = False
-
- # find all the variables referenced. Additionally a variable can be
- # defined in the body of the trans block too, but this is checked at
- # a later state.
- plural_expr = None
- variables = {}
- while parser.stream.current.type != 'block_end':
- if variables:
- parser.stream.expect('comma')
-
- # skip colon for python compatibility
- if parser.stream.skip_if('colon'):
- break
-
- name = parser.stream.expect('name')
- if name.value in variables:
- parser.fail('translatable variable %r defined twice.' %
- name.value, name.lineno,
- exc=TemplateAssertionError)
-
- # expressions
- if parser.stream.current.type == 'assign':
- next(parser.stream)
- variables[name.value] = var = parser.parse_expression()
- else:
- variables[name.value] = var = nodes.Name(name.value, 'load')
-
- if plural_expr is None:
- plural_expr = var
- num_called_num = name.value == 'num'
-
- parser.stream.expect('block_end')
-
- plural = plural_names = None
- have_plural = False
- referenced = set()
-
- # now parse until endtrans or pluralize
- singular_names, singular = self._parse_block(parser, True)
- if singular_names:
- referenced.update(singular_names)
- if plural_expr is None:
- plural_expr = nodes.Name(singular_names[0], 'load')
- num_called_num = singular_names[0] == 'num'
-
- # if we have a pluralize block, we parse that too
- if parser.stream.current.test('name:pluralize'):
- have_plural = True
- next(parser.stream)
- if parser.stream.current.type != 'block_end':
- name = parser.stream.expect('name')
- if name.value not in variables:
- parser.fail('unknown variable %r for pluralization' %
- name.value, name.lineno,
- exc=TemplateAssertionError)
- plural_expr = variables[name.value]
- num_called_num = name.value == 'num'
- parser.stream.expect('block_end')
- plural_names, plural = self._parse_block(parser, False)
- next(parser.stream)
- referenced.update(plural_names)
- else:
- next(parser.stream)
-
- # register free names as simple name expressions
- for var in referenced:
- if var not in variables:
- variables[var] = nodes.Name(var, 'load')
-
- if not have_plural:
- plural_expr = None
- elif plural_expr is None:
- parser.fail('pluralize without variables', lineno)
-
- node = self._make_node(singular, plural, variables, plural_expr,
- bool(referenced),
- num_called_num and have_plural)
- node.set_lineno(lineno)
- return node
-
- def _parse_block(self, parser, allow_pluralize):
- """Parse until the next block tag with a given name."""
- referenced = []
- buf = []
- while 1:
- if parser.stream.current.type == 'data':
- buf.append(parser.stream.current.value.replace('%', '%%'))
- next(parser.stream)
- elif parser.stream.current.type == 'variable_begin':
- next(parser.stream)
- name = parser.stream.expect('name').value
- referenced.append(name)
- buf.append('%%(%s)s' % name)
- parser.stream.expect('variable_end')
- elif parser.stream.current.type == 'block_begin':
- next(parser.stream)
- if parser.stream.current.test('name:endtrans'):
- break
- elif parser.stream.current.test('name:pluralize'):
- if allow_pluralize:
- break
- parser.fail('a translatable section can have only one '
- 'pluralize section')
- parser.fail('control structures in translatable sections are '
- 'not allowed')
- elif parser.stream.eos:
- parser.fail('unclosed translation block')
- else:
- assert False, 'internal parser error'
-
- return referenced, concat(buf)
-
- def _make_node(self, singular, plural, variables, plural_expr,
- vars_referenced, num_called_num):
- """Generates a useful node from the data provided."""
- # no variables referenced? no need to escape for old style
- # gettext invocations only if there are vars.
- if not vars_referenced and not self.environment.newstyle_gettext:
- singular = singular.replace('%%', '%')
- if plural:
- plural = plural.replace('%%', '%')
-
- # singular only:
- if plural_expr is None:
- gettext = nodes.Name('gettext', 'load')
- node = nodes.Call(gettext, [nodes.Const(singular)],
- [], None, None)
-
- # singular and plural
- else:
- ngettext = nodes.Name('ngettext', 'load')
- node = nodes.Call(ngettext, [
- nodes.Const(singular),
- nodes.Const(plural),
- plural_expr
- ], [], None, None)
-
- # in case newstyle gettext is used, the method is powerful
- # enough to handle the variable expansion and autoescape
- # handling itself
- if self.environment.newstyle_gettext:
- for key, value in variables.iteritems():
- # the function adds that later anyways in case num was
- # called num, so just skip it.
- if num_called_num and key == 'num':
- continue
- node.kwargs.append(nodes.Keyword(key, value))
-
- # otherwise do that here
- else:
- # mark the return value as safe if we are in an
- # environment with autoescaping turned on
- node = nodes.MarkSafeIfAutoescape(node)
- if variables:
- node = nodes.Mod(node, nodes.Dict([
- nodes.Pair(nodes.Const(key), value)
- for key, value in variables.items()
- ]))
- return nodes.Output([node])
-
-
-class ExprStmtExtension(Extension):
- """Adds a `do` tag to Jinja2 that works like the print statement just
- that it doesn't print the return value.
- """
- tags = set(['do'])
-
- def parse(self, parser):
- node = nodes.ExprStmt(lineno=next(parser.stream).lineno)
- node.node = parser.parse_tuple()
- return node
-
-
-class LoopControlExtension(Extension):
- """Adds break and continue to the template engine."""
- tags = set(['break', 'continue'])
-
- def parse(self, parser):
- token = next(parser.stream)
- if token.value == 'break':
- return nodes.Break(lineno=token.lineno)
- return nodes.Continue(lineno=token.lineno)
-
-
-class WithExtension(Extension):
- """Adds support for a django-like with block."""
- tags = set(['with'])
-
- def parse(self, parser):
- node = nodes.Scope(lineno=next(parser.stream).lineno)
- assignments = []
- while parser.stream.current.type != 'block_end':
- lineno = parser.stream.current.lineno
- if assignments:
- parser.stream.expect('comma')
- target = parser.parse_assign_target()
- parser.stream.expect('assign')
- expr = parser.parse_expression()
- assignments.append(nodes.Assign(target, expr, lineno=lineno))
- node.body = assignments + \
- list(parser.parse_statements(('name:endwith',),
- drop_needle=True))
- return node
-
-
-class AutoEscapeExtension(Extension):
- """Changes auto escape rules for a scope."""
- tags = set(['autoescape'])
-
- def parse(self, parser):
- node = nodes.ScopedEvalContextModifier(lineno=next(parser.stream).lineno)
- node.options = [
- nodes.Keyword('autoescape', parser.parse_expression())
- ]
- node.body = parser.parse_statements(('name:endautoescape',),
- drop_needle=True)
- return nodes.Scope([node])
-
-
-def extract_from_ast(node, gettext_functions=GETTEXT_FUNCTIONS,
- babel_style=True):
- """Extract localizable strings from the given template node. Per
- default this function returns matches in babel style that means non string
- parameters as well as keyword arguments are returned as `None`. This
- allows Babel to figure out what you really meant if you are using
- gettext functions that allow keyword arguments for placeholder expansion.
- If you don't want that behavior set the `babel_style` parameter to `False`
- which causes only strings to be returned and parameters are always stored
- in tuples. As a consequence invalid gettext calls (calls without a single
- string parameter or string parameters after non-string parameters) are
- skipped.
-
- This example explains the behavior:
-
- >>> from jinja2 import Environment
- >>> env = Environment()
- >>> node = env.parse('{{ (_("foo"), _(), ngettext("foo", "bar", 42)) }}')
- >>> list(extract_from_ast(node))
- [(1, '_', 'foo'), (1, '_', ()), (1, 'ngettext', ('foo', 'bar', None))]
- >>> list(extract_from_ast(node, babel_style=False))
- [(1, '_', ('foo',)), (1, 'ngettext', ('foo', 'bar'))]
-
- For every string found this function yields a ``(lineno, function,
- message)`` tuple, where:
-
- * ``lineno`` is the number of the line on which the string was found,
- * ``function`` is the name of the ``gettext`` function used (if the
- string was extracted from embedded Python code), and
- * ``message`` is the string itself (a ``unicode`` object, or a tuple
- of ``unicode`` objects for functions with multiple string arguments).
-
- This extraction function operates on the AST and is because of that unable
- to extract any comments. For comment support you have to use the babel
- extraction interface or extract comments yourself.
- """
- for node in node.find_all(nodes.Call):
- if not isinstance(node.node, nodes.Name) or \
- node.node.name not in gettext_functions:
- continue
-
- strings = []
- for arg in node.args:
- if isinstance(arg, nodes.Const) and \
- isinstance(arg.value, basestring):
- strings.append(arg.value)
- else:
- strings.append(None)
-
- for arg in node.kwargs:
- strings.append(None)
- if node.dyn_args is not None:
- strings.append(None)
- if node.dyn_kwargs is not None:
- strings.append(None)
-
- if not babel_style:
- strings = tuple(x for x in strings if x is not None)
- if not strings:
- continue
- else:
- if len(strings) == 1:
- strings = strings[0]
- else:
- strings = tuple(strings)
- yield node.lineno, node.node.name, strings
-
-
-class _CommentFinder(object):
- """Helper class to find comments in a token stream. Can only
- find comments for gettext calls forwards. Once the comment
- from line 4 is found, a comment for line 1 will not return a
- usable value.
- """
-
- def __init__(self, tokens, comment_tags):
- self.tokens = tokens
- self.comment_tags = comment_tags
- self.offset = 0
- self.last_lineno = 0
-
- def find_backwards(self, offset):
- try:
- for _, token_type, token_value in \
- reversed(self.tokens[self.offset:offset]):
- if token_type in ('comment', 'linecomment'):
- try:
- prefix, comment = token_value.split(None, 1)
- except ValueError:
- continue
- if prefix in self.comment_tags:
- return [comment.rstrip()]
- return []
- finally:
- self.offset = offset
-
- def find_comments(self, lineno):
- if not self.comment_tags or self.last_lineno > lineno:
- return []
- for idx, (token_lineno, _, _) in enumerate(self.tokens[self.offset:]):
- if token_lineno > lineno:
- return self.find_backwards(self.offset + idx)
- return self.find_backwards(len(self.tokens))
-
-
-def babel_extract(fileobj, keywords, comment_tags, options):
- """Babel extraction method for Jinja templates.
-
- .. versionchanged:: 2.3
- Basic support for translation comments was added. If `comment_tags`
- is now set to a list of keywords for extraction, the extractor will
- try to find the best preceeding comment that begins with one of the
- keywords. For best results, make sure to not have more than one
- gettext call in one line of code and the matching comment in the
- same line or the line before.
-
- .. versionchanged:: 2.5.1
- The `newstyle_gettext` flag can be set to `True` to enable newstyle
- gettext calls.
-
- :param fileobj: the file-like object the messages should be extracted from
- :param keywords: a list of keywords (i.e. function names) that should be
- recognized as translation functions
- :param comment_tags: a list of translator tags to search for and include
- in the results.
- :param options: a dictionary of additional options (optional)
- :return: an iterator over ``(lineno, funcname, message, comments)`` tuples.
- (comments will be empty currently)
- """
- extensions = set()
- for extension in options.get('extensions', '').split(','):
- extension = extension.strip()
- if not extension:
- continue
- extensions.add(import_string(extension))
- if InternationalizationExtension not in extensions:
- extensions.add(InternationalizationExtension)
-
- def getbool(options, key, default=False):
- options.get(key, str(default)).lower() in ('1', 'on', 'yes', 'true')
-
- environment = Environment(
- options.get('block_start_string', BLOCK_START_STRING),
- options.get('block_end_string', BLOCK_END_STRING),
- options.get('variable_start_string', VARIABLE_START_STRING),
- options.get('variable_end_string', VARIABLE_END_STRING),
- options.get('comment_start_string', COMMENT_START_STRING),
- options.get('comment_end_string', COMMENT_END_STRING),
- options.get('line_statement_prefix') or LINE_STATEMENT_PREFIX,
- options.get('line_comment_prefix') or LINE_COMMENT_PREFIX,
- getbool(options, 'trim_blocks', TRIM_BLOCKS),
- NEWLINE_SEQUENCE, frozenset(extensions),
- cache_size=0,
- auto_reload=False
- )
-
- if getbool(options, 'newstyle_gettext'):
- environment.newstyle_gettext = True
-
- source = fileobj.read().decode(options.get('encoding', 'utf-8'))
- try:
- node = environment.parse(source)
- tokens = list(environment.lex(environment.preprocess(source)))
- except TemplateSyntaxError, e:
- # skip templates with syntax errors
- return
-
- finder = _CommentFinder(tokens, comment_tags)
- for lineno, func, message in extract_from_ast(node, keywords):
- yield lineno, func, message, finder.find_comments(lineno)
-
-
-#: nicer import names
-i18n = InternationalizationExtension
-do = ExprStmtExtension
-loopcontrols = LoopControlExtension
-with_ = WithExtension
-autoescape = AutoEscapeExtension
diff --git a/module/lib/jinja2/filters.py b/module/lib/jinja2/filters.py
deleted file mode 100644
index d1848e434..000000000
--- a/module/lib/jinja2/filters.py
+++ /dev/null
@@ -1,719 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.filters
- ~~~~~~~~~~~~~~
-
- Bundled jinja filters.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import re
-import math
-from random import choice
-from operator import itemgetter
-from itertools import imap, groupby
-from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode
-from jinja2.runtime import Undefined
-from jinja2.exceptions import FilterArgumentError, SecurityError
-
-
-_word_re = re.compile(r'\w+(?u)')
-
-
-def contextfilter(f):
- """Decorator for marking context dependent filters. The current
- :class:`Context` will be passed as first argument.
- """
- f.contextfilter = True
- return f
-
-
-def evalcontextfilter(f):
- """Decorator for marking eval-context dependent filters. An eval
- context object is passed as first argument. For more information
- about the eval context, see :ref:`eval-context`.
-
- .. versionadded:: 2.4
- """
- f.evalcontextfilter = True
- return f
-
-
-def environmentfilter(f):
- """Decorator for marking evironment dependent filters. The current
- :class:`Environment` is passed to the filter as first argument.
- """
- f.environmentfilter = True
- return f
-
-
-def do_forceescape(value):
- """Enforce HTML escaping. This will probably double escape variables."""
- if hasattr(value, '__html__'):
- value = value.__html__()
- return escape(unicode(value))
-
-
-@evalcontextfilter
-def do_replace(eval_ctx, s, old, new, count=None):
- """Return a copy of the value with all occurrences of a substring
- replaced with a new one. The first argument is the substring
- that should be replaced, the second is the replacement string.
- If the optional third argument ``count`` is given, only the first
- ``count`` occurrences are replaced:
-
- .. sourcecode:: jinja
-
- {{ "Hello World"|replace("Hello", "Goodbye") }}
- -> Goodbye World
-
- {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
- -> d'oh, d'oh, aaargh
- """
- if count is None:
- count = -1
- if not eval_ctx.autoescape:
- return unicode(s).replace(unicode(old), unicode(new), count)
- if hasattr(old, '__html__') or hasattr(new, '__html__') and \
- not hasattr(s, '__html__'):
- s = escape(s)
- else:
- s = soft_unicode(s)
- return s.replace(soft_unicode(old), soft_unicode(new), count)
-
-
-def do_upper(s):
- """Convert a value to uppercase."""
- return soft_unicode(s).upper()
-
-
-def do_lower(s):
- """Convert a value to lowercase."""
- return soft_unicode(s).lower()
-
-
-@evalcontextfilter
-def do_xmlattr(_eval_ctx, d, autospace=True):
- """Create an SGML/XML attribute string based on the items in a dict.
- All values that are neither `none` nor `undefined` are automatically
- escaped:
-
- .. sourcecode:: html+jinja
-
- <ul{{ {'class': 'my_list', 'missing': none,
- 'id': 'list-%d'|format(variable)}|xmlattr }}>
- ...
- </ul>
-
- Results in something like this:
-
- .. sourcecode:: html
-
- <ul class="my_list" id="list-42">
- ...
- </ul>
-
- As you can see it automatically prepends a space in front of the item
- if the filter returned something unless the second parameter is false.
- """
- rv = u' '.join(
- u'%s="%s"' % (escape(key), escape(value))
- for key, value in d.iteritems()
- if value is not None and not isinstance(value, Undefined)
- )
- if autospace and rv:
- rv = u' ' + rv
- if _eval_ctx.autoescape:
- rv = Markup(rv)
- return rv
-
-
-def do_capitalize(s):
- """Capitalize a value. The first character will be uppercase, all others
- lowercase.
- """
- return soft_unicode(s).capitalize()
-
-
-def do_title(s):
- """Return a titlecased version of the value. I.e. words will start with
- uppercase letters, all remaining characters are lowercase.
- """
- return soft_unicode(s).title()
-
-
-def do_dictsort(value, case_sensitive=False, by='key'):
- """Sort a dict and yield (key, value) pairs. Because python dicts are
- unsorted you may want to use this function to order them by either
- key or value:
-
- .. sourcecode:: jinja
-
- {% for item in mydict|dictsort %}
- sort the dict by key, case insensitive
-
- {% for item in mydict|dicsort(true) %}
- sort the dict by key, case sensitive
-
- {% for item in mydict|dictsort(false, 'value') %}
- sort the dict by key, case insensitive, sorted
- normally and ordered by value.
- """
- if by == 'key':
- pos = 0
- elif by == 'value':
- pos = 1
- else:
- raise FilterArgumentError('You can only sort by either '
- '"key" or "value"')
- def sort_func(item):
- value = item[pos]
- if isinstance(value, basestring) and not case_sensitive:
- value = value.lower()
- return value
-
- return sorted(value.items(), key=sort_func)
-
-
-def do_sort(value, reverse=False, case_sensitive=False):
- """Sort an iterable. Per default it sorts ascending, if you pass it
- true as first argument it will reverse the sorting.
-
- If the iterable is made of strings the third parameter can be used to
- control the case sensitiveness of the comparison which is disabled by
- default.
-
- .. sourcecode:: jinja
-
- {% for item in iterable|sort %}
- ...
- {% endfor %}
- """
- if not case_sensitive:
- def sort_func(item):
- if isinstance(item, basestring):
- item = item.lower()
- return item
- else:
- sort_func = None
- return sorted(value, key=sort_func, reverse=reverse)
-
-
-def do_default(value, default_value=u'', boolean=False):
- """If the value is undefined it will return the passed default value,
- otherwise the value of the variable:
-
- .. sourcecode:: jinja
-
- {{ my_variable|default('my_variable is not defined') }}
-
- This will output the value of ``my_variable`` if the variable was
- defined, otherwise ``'my_variable is not defined'``. If you want
- to use default with variables that evaluate to false you have to
- set the second parameter to `true`:
-
- .. sourcecode:: jinja
-
- {{ ''|default('the string was empty', true) }}
- """
- if (boolean and not value) or isinstance(value, Undefined):
- return default_value
- return value
-
-
-@evalcontextfilter
-def do_join(eval_ctx, value, d=u''):
- """Return a string which is the concatenation of the strings in the
- sequence. The separator between elements is an empty string per
- default, you can define it with the optional parameter:
-
- .. sourcecode:: jinja
-
- {{ [1, 2, 3]|join('|') }}
- -> 1|2|3
-
- {{ [1, 2, 3]|join }}
- -> 123
- """
- # no automatic escaping? joining is a lot eaiser then
- if not eval_ctx.autoescape:
- return unicode(d).join(imap(unicode, value))
-
- # if the delimiter doesn't have an html representation we check
- # if any of the items has. If yes we do a coercion to Markup
- if not hasattr(d, '__html__'):
- value = list(value)
- do_escape = False
- for idx, item in enumerate(value):
- if hasattr(item, '__html__'):
- do_escape = True
- else:
- value[idx] = unicode(item)
- if do_escape:
- d = escape(d)
- else:
- d = unicode(d)
- return d.join(value)
-
- # no html involved, to normal joining
- return soft_unicode(d).join(imap(soft_unicode, value))
-
-
-def do_center(value, width=80):
- """Centers the value in a field of a given width."""
- return unicode(value).center(width)
-
-
-@environmentfilter
-def do_first(environment, seq):
- """Return the first item of a sequence."""
- try:
- return iter(seq).next()
- except StopIteration:
- return environment.undefined('No first item, sequence was empty.')
-
-
-@environmentfilter
-def do_last(environment, seq):
- """Return the last item of a sequence."""
- try:
- return iter(reversed(seq)).next()
- except StopIteration:
- return environment.undefined('No last item, sequence was empty.')
-
-
-@environmentfilter
-def do_random(environment, seq):
- """Return a random item from the sequence."""
- try:
- return choice(seq)
- except IndexError:
- return environment.undefined('No random item, sequence was empty.')
-
-
-def do_filesizeformat(value, binary=False):
- """Format the value like a 'human-readable' file size (i.e. 13 KB,
- 4.1 MB, 102 bytes, etc). Per default decimal prefixes are used (mega,
- giga, etc.), if the second parameter is set to `True` the binary
- prefixes are used (mebi, gibi).
- """
- bytes = float(value)
- base = binary and 1024 or 1000
- middle = binary and 'i' or ''
- if bytes < base:
- return "%d Byte%s" % (bytes, bytes != 1 and 's' or '')
- elif bytes < base * base:
- return "%.1f K%sB" % (bytes / base, middle)
- elif bytes < base * base * base:
- return "%.1f M%sB" % (bytes / (base * base), middle)
- return "%.1f G%sB" % (bytes / (base * base * base), middle)
-
-
-def do_pprint(value, verbose=False):
- """Pretty print a variable. Useful for debugging.
-
- With Jinja 1.2 onwards you can pass it a parameter. If this parameter
- is truthy the output will be more verbose (this requires `pretty`)
- """
- return pformat(value, verbose=verbose)
-
-
-@evalcontextfilter
-def do_urlize(eval_ctx, value, trim_url_limit=None, nofollow=False):
- """Converts URLs in plain text into clickable links.
-
- If you pass the filter an additional integer it will shorten the urls
- to that number. Also a third argument exists that makes the urls
- "nofollow":
-
- .. sourcecode:: jinja
-
- {{ mytext|urlize(40, true) }}
- links are shortened to 40 chars and defined with rel="nofollow"
- """
- rv = urlize(value, trim_url_limit, nofollow)
- if eval_ctx.autoescape:
- rv = Markup(rv)
- return rv
-
-
-def do_indent(s, width=4, indentfirst=False):
- """Return a copy of the passed string, each line indented by
- 4 spaces. The first line is not indented. If you want to
- change the number of spaces or indent the first line too
- you can pass additional parameters to the filter:
-
- .. sourcecode:: jinja
-
- {{ mytext|indent(2, true) }}
- indent by two spaces and indent the first line too.
- """
- indention = u' ' * width
- rv = (u'\n' + indention).join(s.splitlines())
- if indentfirst:
- rv = indention + rv
- return rv
-
-
-def do_truncate(s, length=255, killwords=False, end='...'):
- """Return a truncated copy of the string. The length is specified
- with the first parameter which defaults to ``255``. If the second
- parameter is ``true`` the filter will cut the text at length. Otherwise
- it will try to save the last word. If the text was in fact
- truncated it will append an ellipsis sign (``"..."``). If you want a
- different ellipsis sign than ``"..."`` you can specify it using the
- third parameter.
-
- .. sourcecode jinja::
-
- {{ mytext|truncate(300, false, '&raquo;') }}
- truncate mytext to 300 chars, don't split up words, use a
- right pointing double arrow as ellipsis sign.
- """
- if len(s) <= length:
- return s
- elif killwords:
- return s[:length] + end
- words = s.split(' ')
- result = []
- m = 0
- for word in words:
- m += len(word) + 1
- if m > length:
- break
- result.append(word)
- result.append(end)
- return u' '.join(result)
-
-
-def do_wordwrap(s, width=79, break_long_words=True):
- """
- Return a copy of the string passed to the filter wrapped after
- ``79`` characters. You can override this default using the first
- parameter. If you set the second parameter to `false` Jinja will not
- split words apart if they are longer than `width`.
- """
- import textwrap
- return u'\n'.join(textwrap.wrap(s, width=width, expand_tabs=False,
- replace_whitespace=False,
- break_long_words=break_long_words))
-
-
-def do_wordcount(s):
- """Count the words in that string."""
- return len(_word_re.findall(s))
-
-
-def do_int(value, default=0):
- """Convert the value into an integer. If the
- conversion doesn't work it will return ``0``. You can
- override this default using the first parameter.
- """
- try:
- return int(value)
- except (TypeError, ValueError):
- # this quirk is necessary so that "42.23"|int gives 42.
- try:
- return int(float(value))
- except (TypeError, ValueError):
- return default
-
-
-def do_float(value, default=0.0):
- """Convert the value into a floating point number. If the
- conversion doesn't work it will return ``0.0``. You can
- override this default using the first parameter.
- """
- try:
- return float(value)
- except (TypeError, ValueError):
- return default
-
-
-def do_format(value, *args, **kwargs):
- """
- Apply python string formatting on an object:
-
- .. sourcecode:: jinja
-
- {{ "%s - %s"|format("Hello?", "Foo!") }}
- -> Hello? - Foo!
- """
- if args and kwargs:
- raise FilterArgumentError('can\'t handle positional and keyword '
- 'arguments at the same time')
- return soft_unicode(value) % (kwargs or args)
-
-
-def do_trim(value):
- """Strip leading and trailing whitespace."""
- return soft_unicode(value).strip()
-
-
-def do_striptags(value):
- """Strip SGML/XML tags and replace adjacent whitespace by one space.
- """
- if hasattr(value, '__html__'):
- value = value.__html__()
- return Markup(unicode(value)).striptags()
-
-
-def do_slice(value, slices, fill_with=None):
- """Slice an iterator and return a list of lists containing
- those items. Useful if you want to create a div containing
- three ul tags that represent columns:
-
- .. sourcecode:: html+jinja
-
- <div class="columwrapper">
- {%- for column in items|slice(3) %}
- <ul class="column-{{ loop.index }}">
- {%- for item in column %}
- <li>{{ item }}</li>
- {%- endfor %}
- </ul>
- {%- endfor %}
- </div>
-
- If you pass it a second argument it's used to fill missing
- values on the last iteration.
- """
- seq = list(value)
- length = len(seq)
- items_per_slice = length // slices
- slices_with_extra = length % slices
- offset = 0
- for slice_number in xrange(slices):
- start = offset + slice_number * items_per_slice
- if slice_number < slices_with_extra:
- offset += 1
- end = offset + (slice_number + 1) * items_per_slice
- tmp = seq[start:end]
- if fill_with is not None and slice_number >= slices_with_extra:
- tmp.append(fill_with)
- yield tmp
-
-
-def do_batch(value, linecount, fill_with=None):
- """
- A filter that batches items. It works pretty much like `slice`
- just the other way round. It returns a list of lists with the
- given number of items. If you provide a second parameter this
- is used to fill missing items. See this example:
-
- .. sourcecode:: html+jinja
-
- <table>
- {%- for row in items|batch(3, '&nbsp;') %}
- <tr>
- {%- for column in row %}
- <td>{{ column }}</td>
- {%- endfor %}
- </tr>
- {%- endfor %}
- </table>
- """
- result = []
- tmp = []
- for item in value:
- if len(tmp) == linecount:
- yield tmp
- tmp = []
- tmp.append(item)
- if tmp:
- if fill_with is not None and len(tmp) < linecount:
- tmp += [fill_with] * (linecount - len(tmp))
- yield tmp
-
-
-def do_round(value, precision=0, method='common'):
- """Round the number to a given precision. The first
- parameter specifies the precision (default is ``0``), the
- second the rounding method:
-
- - ``'common'`` rounds either up or down
- - ``'ceil'`` always rounds up
- - ``'floor'`` always rounds down
-
- If you don't specify a method ``'common'`` is used.
-
- .. sourcecode:: jinja
-
- {{ 42.55|round }}
- -> 43.0
- {{ 42.55|round(1, 'floor') }}
- -> 42.5
-
- Note that even if rounded to 0 precision, a float is returned. If
- you need a real integer, pipe it through `int`:
-
- .. sourcecode:: jinja
-
- {{ 42.55|round|int }}
- -> 43
- """
- if not method in ('common', 'ceil', 'floor'):
- raise FilterArgumentError('method must be common, ceil or floor')
- if method == 'common':
- return round(value, precision)
- func = getattr(math, method)
- return func(value * (10 ** precision)) / (10 ** precision)
-
-
-@environmentfilter
-def do_groupby(environment, value, attribute):
- """Group a sequence of objects by a common attribute.
-
- If you for example have a list of dicts or objects that represent persons
- with `gender`, `first_name` and `last_name` attributes and you want to
- group all users by genders you can do something like the following
- snippet:
-
- .. sourcecode:: html+jinja
-
- <ul>
- {% for group in persons|groupby('gender') %}
- <li>{{ group.grouper }}<ul>
- {% for person in group.list %}
- <li>{{ person.first_name }} {{ person.last_name }}</li>
- {% endfor %}</ul></li>
- {% endfor %}
- </ul>
-
- Additionally it's possible to use tuple unpacking for the grouper and
- list:
-
- .. sourcecode:: html+jinja
-
- <ul>
- {% for grouper, list in persons|groupby('gender') %}
- ...
- {% endfor %}
- </ul>
-
- As you can see the item we're grouping by is stored in the `grouper`
- attribute and the `list` contains all the objects that have this grouper
- in common.
- """
- expr = lambda x: environment.getitem(x, attribute)
- return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr)))
-
-
-class _GroupTuple(tuple):
- __slots__ = ()
- grouper = property(itemgetter(0))
- list = property(itemgetter(1))
-
- def __new__(cls, (key, value)):
- return tuple.__new__(cls, (key, list(value)))
-
-
-def do_list(value):
- """Convert the value into a list. If it was a string the returned list
- will be a list of characters.
- """
- return list(value)
-
-
-def do_mark_safe(value):
- """Mark the value as safe which means that in an environment with automatic
- escaping enabled this variable will not be escaped.
- """
- return Markup(value)
-
-
-def do_mark_unsafe(value):
- """Mark a value as unsafe. This is the reverse operation for :func:`safe`."""
- return unicode(value)
-
-
-def do_reverse(value):
- """Reverse the object or return an iterator the iterates over it the other
- way round.
- """
- if isinstance(value, basestring):
- return value[::-1]
- try:
- return reversed(value)
- except TypeError:
- try:
- rv = list(value)
- rv.reverse()
- return rv
- except TypeError:
- raise FilterArgumentError('argument must be iterable')
-
-
-@environmentfilter
-def do_attr(environment, obj, name):
- """Get an attribute of an object. ``foo|attr("bar")`` works like
- ``foo["bar"]`` just that always an attribute is returned and items are not
- looked up.
-
- See :ref:`Notes on subscriptions <notes-on-subscriptions>` for more details.
- """
- try:
- name = str(name)
- except UnicodeError:
- pass
- else:
- try:
- value = getattr(obj, name)
- except AttributeError:
- pass
- else:
- if environment.sandboxed and not \
- environment.is_safe_attribute(obj, name, value):
- return environment.unsafe_undefined(obj, name)
- return value
- return environment.undefined(obj=obj, name=name)
-
-
-FILTERS = {
- 'attr': do_attr,
- 'replace': do_replace,
- 'upper': do_upper,
- 'lower': do_lower,
- 'escape': escape,
- 'e': escape,
- 'forceescape': do_forceescape,
- 'capitalize': do_capitalize,
- 'title': do_title,
- 'default': do_default,
- 'd': do_default,
- 'join': do_join,
- 'count': len,
- 'dictsort': do_dictsort,
- 'sort': do_sort,
- 'length': len,
- 'reverse': do_reverse,
- 'center': do_center,
- 'indent': do_indent,
- 'title': do_title,
- 'capitalize': do_capitalize,
- 'first': do_first,
- 'last': do_last,
- 'random': do_random,
- 'filesizeformat': do_filesizeformat,
- 'pprint': do_pprint,
- 'truncate': do_truncate,
- 'wordwrap': do_wordwrap,
- 'wordcount': do_wordcount,
- 'int': do_int,
- 'float': do_float,
- 'string': soft_unicode,
- 'list': do_list,
- 'urlize': do_urlize,
- 'format': do_format,
- 'trim': do_trim,
- 'striptags': do_striptags,
- 'slice': do_slice,
- 'batch': do_batch,
- 'sum': sum,
- 'abs': abs,
- 'round': do_round,
- 'groupby': do_groupby,
- 'safe': do_mark_safe,
- 'xmlattr': do_xmlattr
-}
diff --git a/module/lib/jinja2/lexer.py b/module/lib/jinja2/lexer.py
deleted file mode 100644
index 0d3f69617..000000000
--- a/module/lib/jinja2/lexer.py
+++ /dev/null
@@ -1,681 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.lexer
- ~~~~~~~~~~~~
-
- This module implements a Jinja / Python combination lexer. The
- `Lexer` class provided by this module is used to do some preprocessing
- for Jinja.
-
- On the one hand it filters out invalid operators like the bitshift
- operators we don't allow in templates. On the other hand it separates
- template code and python code in expressions.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import re
-from operator import itemgetter
-from collections import deque
-from jinja2.exceptions import TemplateSyntaxError
-from jinja2.utils import LRUCache, next
-
-
-# cache for the lexers. Exists in order to be able to have multiple
-# environments with the same lexer
-_lexer_cache = LRUCache(50)
-
-# static regular expressions
-whitespace_re = re.compile(r'\s+', re.U)
-string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
- r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
-integer_re = re.compile(r'\d+')
-
-# we use the unicode identifier rule if this python version is able
-# to handle unicode identifiers, otherwise the standard ASCII one.
-try:
- compile('föö', '<unknown>', 'eval')
-except SyntaxError:
- name_re = re.compile(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b')
-else:
- from jinja2 import _stringdefs
- name_re = re.compile(r'[%s][%s]*' % (_stringdefs.xid_start,
- _stringdefs.xid_continue))
-
-float_re = re.compile(r'(?<!\.)\d+\.\d+')
-newline_re = re.compile(r'(\r\n|\r|\n)')
-
-# internal the tokens and keep references to them
-TOKEN_ADD = intern('add')
-TOKEN_ASSIGN = intern('assign')
-TOKEN_COLON = intern('colon')
-TOKEN_COMMA = intern('comma')
-TOKEN_DIV = intern('div')
-TOKEN_DOT = intern('dot')
-TOKEN_EQ = intern('eq')
-TOKEN_FLOORDIV = intern('floordiv')
-TOKEN_GT = intern('gt')
-TOKEN_GTEQ = intern('gteq')
-TOKEN_LBRACE = intern('lbrace')
-TOKEN_LBRACKET = intern('lbracket')
-TOKEN_LPAREN = intern('lparen')
-TOKEN_LT = intern('lt')
-TOKEN_LTEQ = intern('lteq')
-TOKEN_MOD = intern('mod')
-TOKEN_MUL = intern('mul')
-TOKEN_NE = intern('ne')
-TOKEN_PIPE = intern('pipe')
-TOKEN_POW = intern('pow')
-TOKEN_RBRACE = intern('rbrace')
-TOKEN_RBRACKET = intern('rbracket')
-TOKEN_RPAREN = intern('rparen')
-TOKEN_SEMICOLON = intern('semicolon')
-TOKEN_SUB = intern('sub')
-TOKEN_TILDE = intern('tilde')
-TOKEN_WHITESPACE = intern('whitespace')
-TOKEN_FLOAT = intern('float')
-TOKEN_INTEGER = intern('integer')
-TOKEN_NAME = intern('name')
-TOKEN_STRING = intern('string')
-TOKEN_OPERATOR = intern('operator')
-TOKEN_BLOCK_BEGIN = intern('block_begin')
-TOKEN_BLOCK_END = intern('block_end')
-TOKEN_VARIABLE_BEGIN = intern('variable_begin')
-TOKEN_VARIABLE_END = intern('variable_end')
-TOKEN_RAW_BEGIN = intern('raw_begin')
-TOKEN_RAW_END = intern('raw_end')
-TOKEN_COMMENT_BEGIN = intern('comment_begin')
-TOKEN_COMMENT_END = intern('comment_end')
-TOKEN_COMMENT = intern('comment')
-TOKEN_LINESTATEMENT_BEGIN = intern('linestatement_begin')
-TOKEN_LINESTATEMENT_END = intern('linestatement_end')
-TOKEN_LINECOMMENT_BEGIN = intern('linecomment_begin')
-TOKEN_LINECOMMENT_END = intern('linecomment_end')
-TOKEN_LINECOMMENT = intern('linecomment')
-TOKEN_DATA = intern('data')
-TOKEN_INITIAL = intern('initial')
-TOKEN_EOF = intern('eof')
-
-# bind operators to token types
-operators = {
- '+': TOKEN_ADD,
- '-': TOKEN_SUB,
- '/': TOKEN_DIV,
- '//': TOKEN_FLOORDIV,
- '*': TOKEN_MUL,
- '%': TOKEN_MOD,
- '**': TOKEN_POW,
- '~': TOKEN_TILDE,
- '[': TOKEN_LBRACKET,
- ']': TOKEN_RBRACKET,
- '(': TOKEN_LPAREN,
- ')': TOKEN_RPAREN,
- '{': TOKEN_LBRACE,
- '}': TOKEN_RBRACE,
- '==': TOKEN_EQ,
- '!=': TOKEN_NE,
- '>': TOKEN_GT,
- '>=': TOKEN_GTEQ,
- '<': TOKEN_LT,
- '<=': TOKEN_LTEQ,
- '=': TOKEN_ASSIGN,
- '.': TOKEN_DOT,
- ':': TOKEN_COLON,
- '|': TOKEN_PIPE,
- ',': TOKEN_COMMA,
- ';': TOKEN_SEMICOLON
-}
-
-reverse_operators = dict([(v, k) for k, v in operators.iteritems()])
-assert len(operators) == len(reverse_operators), 'operators dropped'
-operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in
- sorted(operators, key=lambda x: -len(x))))
-
-ignored_tokens = frozenset([TOKEN_COMMENT_BEGIN, TOKEN_COMMENT,
- TOKEN_COMMENT_END, TOKEN_WHITESPACE,
- TOKEN_WHITESPACE, TOKEN_LINECOMMENT_BEGIN,
- TOKEN_LINECOMMENT_END, TOKEN_LINECOMMENT])
-ignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA,
- TOKEN_COMMENT, TOKEN_LINECOMMENT])
-
-
-def _describe_token_type(token_type):
- if token_type in reverse_operators:
- return reverse_operators[token_type]
- return {
- TOKEN_COMMENT_BEGIN: 'begin of comment',
- TOKEN_COMMENT_END: 'end of comment',
- TOKEN_COMMENT: 'comment',
- TOKEN_LINECOMMENT: 'comment',
- TOKEN_BLOCK_BEGIN: 'begin of statement block',
- TOKEN_BLOCK_END: 'end of statement block',
- TOKEN_VARIABLE_BEGIN: 'begin of print statement',
- TOKEN_VARIABLE_END: 'end of print statement',
- TOKEN_LINESTATEMENT_BEGIN: 'begin of line statement',
- TOKEN_LINESTATEMENT_END: 'end of line statement',
- TOKEN_DATA: 'template data / text',
- TOKEN_EOF: 'end of template'
- }.get(token_type, token_type)
-
-
-def describe_token(token):
- """Returns a description of the token."""
- if token.type == 'name':
- return token.value
- return _describe_token_type(token.type)
-
-
-def describe_token_expr(expr):
- """Like `describe_token` but for token expressions."""
- if ':' in expr:
- type, value = expr.split(':', 1)
- if type == 'name':
- return value
- else:
- type = expr
- return _describe_token_type(type)
-
-
-def count_newlines(value):
- """Count the number of newline characters in the string. This is
- useful for extensions that filter a stream.
- """
- return len(newline_re.findall(value))
-
-
-def compile_rules(environment):
- """Compiles all the rules from the environment into a list of rules."""
- e = re.escape
- rules = [
- (len(environment.comment_start_string), 'comment',
- e(environment.comment_start_string)),
- (len(environment.block_start_string), 'block',
- e(environment.block_start_string)),
- (len(environment.variable_start_string), 'variable',
- e(environment.variable_start_string))
- ]
-
- if environment.line_statement_prefix is not None:
- rules.append((len(environment.line_statement_prefix), 'linestatement',
- r'^\s*' + e(environment.line_statement_prefix)))
- if environment.line_comment_prefix is not None:
- rules.append((len(environment.line_comment_prefix), 'linecomment',
- r'(?:^|(?<=\S))[^\S\r\n]*' +
- e(environment.line_comment_prefix)))
-
- return [x[1:] for x in sorted(rules, reverse=True)]
-
-
-class Failure(object):
- """Class that raises a `TemplateSyntaxError` if called.
- Used by the `Lexer` to specify known errors.
- """
-
- def __init__(self, message, cls=TemplateSyntaxError):
- self.message = message
- self.error_class = cls
-
- def __call__(self, lineno, filename):
- raise self.error_class(self.message, lineno, filename)
-
-
-class Token(tuple):
- """Token class."""
- __slots__ = ()
- lineno, type, value = (property(itemgetter(x)) for x in range(3))
-
- def __new__(cls, lineno, type, value):
- return tuple.__new__(cls, (lineno, intern(str(type)), value))
-
- def __str__(self):
- if self.type in reverse_operators:
- return reverse_operators[self.type]
- elif self.type == 'name':
- return self.value
- return self.type
-
- def test(self, expr):
- """Test a token against a token expression. This can either be a
- token type or ``'token_type:token_value'``. This can only test
- against string values and types.
- """
- # here we do a regular string equality check as test_any is usually
- # passed an iterable of not interned strings.
- if self.type == expr:
- return True
- elif ':' in expr:
- return expr.split(':', 1) == [self.type, self.value]
- return False
-
- def test_any(self, *iterable):
- """Test against multiple token expressions."""
- for expr in iterable:
- if self.test(expr):
- return True
- return False
-
- def __repr__(self):
- return 'Token(%r, %r, %r)' % (
- self.lineno,
- self.type,
- self.value
- )
-
-
-class TokenStreamIterator(object):
- """The iterator for tokenstreams. Iterate over the stream
- until the eof token is reached.
- """
-
- def __init__(self, stream):
- self.stream = stream
-
- def __iter__(self):
- return self
-
- def next(self):
- token = self.stream.current
- if token.type is TOKEN_EOF:
- self.stream.close()
- raise StopIteration()
- next(self.stream)
- return token
-
-
-class TokenStream(object):
- """A token stream is an iterable that yields :class:`Token`\s. The
- parser however does not iterate over it but calls :meth:`next` to go
- one token ahead. The current active token is stored as :attr:`current`.
- """
-
- def __init__(self, generator, name, filename):
- self._next = iter(generator).next
- self._pushed = deque()
- self.name = name
- self.filename = filename
- self.closed = False
- self.current = Token(1, TOKEN_INITIAL, '')
- next(self)
-
- def __iter__(self):
- return TokenStreamIterator(self)
-
- def __nonzero__(self):
- return bool(self._pushed) or self.current.type is not TOKEN_EOF
-
- eos = property(lambda x: not x, doc="Are we at the end of the stream?")
-
- def push(self, token):
- """Push a token back to the stream."""
- self._pushed.append(token)
-
- def look(self):
- """Look at the next token."""
- old_token = next(self)
- result = self.current
- self.push(result)
- self.current = old_token
- return result
-
- def skip(self, n=1):
- """Got n tokens ahead."""
- for x in xrange(n):
- next(self)
-
- def next_if(self, expr):
- """Perform the token test and return the token if it matched.
- Otherwise the return value is `None`.
- """
- if self.current.test(expr):
- return next(self)
-
- def skip_if(self, expr):
- """Like :meth:`next_if` but only returns `True` or `False`."""
- return self.next_if(expr) is not None
-
- def next(self):
- """Go one token ahead and return the old one"""
- rv = self.current
- if self._pushed:
- self.current = self._pushed.popleft()
- elif self.current.type is not TOKEN_EOF:
- try:
- self.current = self._next()
- except StopIteration:
- self.close()
- return rv
-
- def close(self):
- """Close the stream."""
- self.current = Token(self.current.lineno, TOKEN_EOF, '')
- self._next = None
- self.closed = True
-
- def expect(self, expr):
- """Expect a given token type and return it. This accepts the same
- argument as :meth:`jinja2.lexer.Token.test`.
- """
- if not self.current.test(expr):
- expr = describe_token_expr(expr)
- if self.current.type is TOKEN_EOF:
- raise TemplateSyntaxError('unexpected end of template, '
- 'expected %r.' % expr,
- self.current.lineno,
- self.name, self.filename)
- raise TemplateSyntaxError("expected token %r, got %r" %
- (expr, describe_token(self.current)),
- self.current.lineno,
- self.name, self.filename)
- try:
- return self.current
- finally:
- next(self)
-
-
-def get_lexer(environment):
- """Return a lexer which is probably cached."""
- key = (environment.block_start_string,
- environment.block_end_string,
- environment.variable_start_string,
- environment.variable_end_string,
- environment.comment_start_string,
- environment.comment_end_string,
- environment.line_statement_prefix,
- environment.line_comment_prefix,
- environment.trim_blocks,
- environment.newline_sequence)
- lexer = _lexer_cache.get(key)
- if lexer is None:
- lexer = Lexer(environment)
- _lexer_cache[key] = lexer
- return lexer
-
-
-class Lexer(object):
- """Class that implements a lexer for a given environment. Automatically
- created by the environment class, usually you don't have to do that.
-
- Note that the lexer is not automatically bound to an environment.
- Multiple environments can share the same lexer.
- """
-
- def __init__(self, environment):
- # shortcuts
- c = lambda x: re.compile(x, re.M | re.S)
- e = re.escape
-
- # lexing rules for tags
- tag_rules = [
- (whitespace_re, TOKEN_WHITESPACE, None),
- (float_re, TOKEN_FLOAT, None),
- (integer_re, TOKEN_INTEGER, None),
- (name_re, TOKEN_NAME, None),
- (string_re, TOKEN_STRING, None),
- (operator_re, TOKEN_OPERATOR, None)
- ]
-
- # assamble the root lexing rule. because "|" is ungreedy
- # we have to sort by length so that the lexer continues working
- # as expected when we have parsing rules like <% for block and
- # <%= for variables. (if someone wants asp like syntax)
- # variables are just part of the rules if variable processing
- # is required.
- root_tag_rules = compile_rules(environment)
-
- # block suffix if trimming is enabled
- block_suffix_re = environment.trim_blocks and '\\n?' or ''
-
- self.newline_sequence = environment.newline_sequence
-
- # global lexing rules
- self.rules = {
- 'root': [
- # directives
- (c('(.*?)(?:%s)' % '|'.join(
- [r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % (
- e(environment.block_start_string),
- e(environment.block_start_string),
- e(environment.block_end_string),
- e(environment.block_end_string)
- )] + [
- r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, r)
- for n, r in root_tag_rules
- ])), (TOKEN_DATA, '#bygroup'), '#bygroup'),
- # data
- (c('.+'), TOKEN_DATA, None)
- ],
- # comments
- TOKEN_COMMENT_BEGIN: [
- (c(r'(.*?)((?:\-%s\s*|%s)%s)' % (
- e(environment.comment_end_string),
- e(environment.comment_end_string),
- block_suffix_re
- )), (TOKEN_COMMENT, TOKEN_COMMENT_END), '#pop'),
- (c('(.)'), (Failure('Missing end of comment tag'),), None)
- ],
- # blocks
- TOKEN_BLOCK_BEGIN: [
- (c('(?:\-%s\s*|%s)%s' % (
- e(environment.block_end_string),
- e(environment.block_end_string),
- block_suffix_re
- )), TOKEN_BLOCK_END, '#pop'),
- ] + tag_rules,
- # variables
- TOKEN_VARIABLE_BEGIN: [
- (c('\-%s\s*|%s' % (
- e(environment.variable_end_string),
- e(environment.variable_end_string)
- )), TOKEN_VARIABLE_END, '#pop')
- ] + tag_rules,
- # raw block
- TOKEN_RAW_BEGIN: [
- (c('(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % (
- e(environment.block_start_string),
- e(environment.block_start_string),
- e(environment.block_end_string),
- e(environment.block_end_string),
- block_suffix_re
- )), (TOKEN_DATA, TOKEN_RAW_END), '#pop'),
- (c('(.)'), (Failure('Missing end of raw directive'),), None)
- ],
- # line statements
- TOKEN_LINESTATEMENT_BEGIN: [
- (c(r'\s*(\n|$)'), TOKEN_LINESTATEMENT_END, '#pop')
- ] + tag_rules,
- # line comments
- TOKEN_LINECOMMENT_BEGIN: [
- (c(r'(.*?)()(?=\n|$)'), (TOKEN_LINECOMMENT,
- TOKEN_LINECOMMENT_END), '#pop')
- ]
- }
-
- def _normalize_newlines(self, value):
- """Called for strings and template data to normlize it to unicode."""
- return newline_re.sub(self.newline_sequence, value)
-
- def tokenize(self, source, name=None, filename=None, state=None):
- """Calls tokeniter + tokenize and wraps it in a token stream.
- """
- stream = self.tokeniter(source, name, filename, state)
- return TokenStream(self.wrap(stream, name, filename), name, filename)
-
- def wrap(self, stream, name=None, filename=None):
- """This is called with the stream as returned by `tokenize` and wraps
- every token in a :class:`Token` and converts the value.
- """
- for lineno, token, value in stream:
- if token in ignored_tokens:
- continue
- elif token == 'linestatement_begin':
- token = 'block_begin'
- elif token == 'linestatement_end':
- token = 'block_end'
- # we are not interested in those tokens in the parser
- elif token in ('raw_begin', 'raw_end'):
- continue
- elif token == 'data':
- value = self._normalize_newlines(value)
- elif token == 'keyword':
- token = value
- elif token == 'name':
- value = str(value)
- elif token == 'string':
- # try to unescape string
- try:
- value = self._normalize_newlines(value[1:-1]) \
- .encode('ascii', 'backslashreplace') \
- .decode('unicode-escape')
- except Exception, e:
- msg = str(e).split(':')[-1].strip()
- raise TemplateSyntaxError(msg, lineno, name, filename)
- # if we can express it as bytestring (ascii only)
- # we do that for support of semi broken APIs
- # as datetime.datetime.strftime. On python 3 this
- # call becomes a noop thanks to 2to3
- try:
- value = str(value)
- except UnicodeError:
- pass
- elif token == 'integer':
- value = int(value)
- elif token == 'float':
- value = float(value)
- elif token == 'operator':
- token = operators[value]
- yield Token(lineno, token, value)
-
- def tokeniter(self, source, name, filename=None, state=None):
- """This method tokenizes the text and returns the tokens in a
- generator. Use this method if you just want to tokenize a template.
- """
- source = '\n'.join(unicode(source).splitlines())
- pos = 0
- lineno = 1
- stack = ['root']
- if state is not None and state != 'root':
- assert state in ('variable', 'block'), 'invalid state'
- stack.append(state + '_begin')
- else:
- state = 'root'
- statetokens = self.rules[stack[-1]]
- source_length = len(source)
-
- balancing_stack = []
-
- while 1:
- # tokenizer loop
- for regex, tokens, new_state in statetokens:
- m = regex.match(source, pos)
- # if no match we try again with the next rule
- if m is None:
- continue
-
- # we only match blocks and variables if brances / parentheses
- # are balanced. continue parsing with the lower rule which
- # is the operator rule. do this only if the end tags look
- # like operators
- if balancing_stack and \
- tokens in ('variable_end', 'block_end',
- 'linestatement_end'):
- continue
-
- # tuples support more options
- if isinstance(tokens, tuple):
- for idx, token in enumerate(tokens):
- # failure group
- if token.__class__ is Failure:
- raise token(lineno, filename)
- # bygroup is a bit more complex, in that case we
- # yield for the current token the first named
- # group that matched
- elif token == '#bygroup':
- for key, value in m.groupdict().iteritems():
- if value is not None:
- yield lineno, key, value
- lineno += value.count('\n')
- break
- else:
- raise RuntimeError('%r wanted to resolve '
- 'the token dynamically'
- ' but no group matched'
- % regex)
- # normal group
- else:
- data = m.group(idx + 1)
- if data or token not in ignore_if_empty:
- yield lineno, token, data
- lineno += data.count('\n')
-
- # strings as token just are yielded as it.
- else:
- data = m.group()
- # update brace/parentheses balance
- if tokens == 'operator':
- if data == '{':
- balancing_stack.append('}')
- elif data == '(':
- balancing_stack.append(')')
- elif data == '[':
- balancing_stack.append(']')
- elif data in ('}', ')', ']'):
- if not balancing_stack:
- raise TemplateSyntaxError('unexpected \'%s\'' %
- data, lineno, name,
- filename)
- expected_op = balancing_stack.pop()
- if expected_op != data:
- raise TemplateSyntaxError('unexpected \'%s\', '
- 'expected \'%s\'' %
- (data, expected_op),
- lineno, name,
- filename)
- # yield items
- if data or tokens not in ignore_if_empty:
- yield lineno, tokens, data
- lineno += data.count('\n')
-
- # fetch new position into new variable so that we can check
- # if there is a internal parsing error which would result
- # in an infinite loop
- pos2 = m.end()
-
- # handle state changes
- if new_state is not None:
- # remove the uppermost state
- if new_state == '#pop':
- stack.pop()
- # resolve the new state by group checking
- elif new_state == '#bygroup':
- for key, value in m.groupdict().iteritems():
- if value is not None:
- stack.append(key)
- break
- else:
- raise RuntimeError('%r wanted to resolve the '
- 'new state dynamically but'
- ' no group matched' %
- regex)
- # direct state name given
- else:
- stack.append(new_state)
- statetokens = self.rules[stack[-1]]
- # we are still at the same position and no stack change.
- # this means a loop without break condition, avoid that and
- # raise error
- elif pos2 == pos:
- raise RuntimeError('%r yielded empty string without '
- 'stack change' % regex)
- # publish new function and start again
- pos = pos2
- break
- # if loop terminated without break we havn't found a single match
- # either we are at the end of the file or we have a problem
- else:
- # end of text
- if pos >= source_length:
- return
- # something went wrong
- raise TemplateSyntaxError('unexpected char %r at %d' %
- (source[pos], pos), lineno,
- name, filename)
diff --git a/module/lib/jinja2/loaders.py b/module/lib/jinja2/loaders.py
deleted file mode 100644
index bd435e8b0..000000000
--- a/module/lib/jinja2/loaders.py
+++ /dev/null
@@ -1,449 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.loaders
- ~~~~~~~~~~~~~~
-
- Jinja loader classes.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import os
-import sys
-import weakref
-from types import ModuleType
-from os import path
-try:
- from hashlib import sha1
-except ImportError:
- from sha import new as sha1
-from jinja2.exceptions import TemplateNotFound
-from jinja2.utils import LRUCache, open_if_exists, internalcode
-
-
-def split_template_path(template):
- """Split a path into segments and perform a sanity check. If it detects
- '..' in the path it will raise a `TemplateNotFound` error.
- """
- pieces = []
- for piece in template.split('/'):
- if path.sep in piece \
- or (path.altsep and path.altsep in piece) or \
- piece == path.pardir:
- raise TemplateNotFound(template)
- elif piece and piece != '.':
- pieces.append(piece)
- return pieces
-
-
-class BaseLoader(object):
- """Baseclass for all loaders. Subclass this and override `get_source` to
- implement a custom loading mechanism. The environment provides a
- `get_template` method that calls the loader's `load` method to get the
- :class:`Template` object.
-
- A very basic example for a loader that looks up templates on the file
- system could look like this::
-
- from jinja2 import BaseLoader, TemplateNotFound
- from os.path import join, exists, getmtime
-
- class MyLoader(BaseLoader):
-
- def __init__(self, path):
- self.path = path
-
- def get_source(self, environment, template):
- path = join(self.path, template)
- if not exists(path):
- raise TemplateNotFound(template)
- mtime = getmtime(path)
- with file(path) as f:
- source = f.read().decode('utf-8')
- return source, path, lambda: mtime == getmtime(path)
- """
-
- #: if set to `False` it indicates that the loader cannot provide access
- #: to the source of templates.
- #:
- #: .. versionadded:: 2.4
- has_source_access = True
-
- def get_source(self, environment, template):
- """Get the template source, filename and reload helper for a template.
- It's passed the environment and template name and has to return a
- tuple in the form ``(source, filename, uptodate)`` or raise a
- `TemplateNotFound` error if it can't locate the template.
-
- The source part of the returned tuple must be the source of the
- template as unicode string or a ASCII bytestring. The filename should
- be the name of the file on the filesystem if it was loaded from there,
- otherwise `None`. The filename is used by python for the tracebacks
- if no loader extension is used.
-
- The last item in the tuple is the `uptodate` function. If auto
- reloading is enabled it's always called to check if the template
- changed. No arguments are passed so the function must store the
- old state somewhere (for example in a closure). If it returns `False`
- the template will be reloaded.
- """
- if not self.has_source_access:
- raise RuntimeError('%s cannot provide access to the source' %
- self.__class__.__name__)
- raise TemplateNotFound(template)
-
- def list_templates(self):
- """Iterates over all templates. If the loader does not support that
- it should raise a :exc:`TypeError` which is the default behavior.
- """
- raise TypeError('this loader cannot iterate over all templates')
-
- @internalcode
- def load(self, environment, name, globals=None):
- """Loads a template. This method looks up the template in the cache
- or loads one by calling :meth:`get_source`. Subclasses should not
- override this method as loaders working on collections of other
- loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`)
- will not call this method but `get_source` directly.
- """
- code = None
- if globals is None:
- globals = {}
-
- # first we try to get the source for this template together
- # with the filename and the uptodate function.
- source, filename, uptodate = self.get_source(environment, name)
-
- # try to load the code from the bytecode cache if there is a
- # bytecode cache configured.
- bcc = environment.bytecode_cache
- if bcc is not None:
- bucket = bcc.get_bucket(environment, name, filename, source)
- code = bucket.code
-
- # if we don't have code so far (not cached, no longer up to
- # date) etc. we compile the template
- if code is None:
- code = environment.compile(source, name, filename)
-
- # if the bytecode cache is available and the bucket doesn't
- # have a code so far, we give the bucket the new code and put
- # it back to the bytecode cache.
- if bcc is not None and bucket.code is None:
- bucket.code = code
- bcc.set_bucket(bucket)
-
- return environment.template_class.from_code(environment, code,
- globals, uptodate)
-
-
-class FileSystemLoader(BaseLoader):
- """Loads templates from the file system. This loader can find templates
- in folders on the file system and is the preferred way to load them.
-
- The loader takes the path to the templates as string, or if multiple
- locations are wanted a list of them which is then looked up in the
- given order:
-
- >>> loader = FileSystemLoader('/path/to/templates')
- >>> loader = FileSystemLoader(['/path/to/templates', '/other/path'])
-
- Per default the template encoding is ``'utf-8'`` which can be changed
- by setting the `encoding` parameter to something else.
- """
-
- def __init__(self, searchpath, encoding='utf-8'):
- if isinstance(searchpath, basestring):
- searchpath = [searchpath]
- self.searchpath = list(searchpath)
- self.encoding = encoding
-
- def get_source(self, environment, template):
- pieces = split_template_path(template)
- for searchpath in self.searchpath:
- filename = path.join(searchpath, *pieces)
- f = open_if_exists(filename)
- if f is None:
- continue
- try:
- contents = f.read().decode(self.encoding)
- finally:
- f.close()
-
- mtime = path.getmtime(filename)
- def uptodate():
- try:
- return path.getmtime(filename) == mtime
- except OSError:
- return False
- return contents, filename, uptodate
- raise TemplateNotFound(template)
-
- def list_templates(self):
- found = set()
- for searchpath in self.searchpath:
- for dirpath, dirnames, filenames in os.walk(searchpath):
- for filename in filenames:
- template = os.path.join(dirpath, filename) \
- [len(searchpath):].strip(os.path.sep) \
- .replace(os.path.sep, '/')
- if template[:2] == './':
- template = template[2:]
- if template not in found:
- found.add(template)
- return sorted(found)
-
-
-class PackageLoader(BaseLoader):
- """Load templates from python eggs or packages. It is constructed with
- the name of the python package and the path to the templates in that
- package::
-
- loader = PackageLoader('mypackage', 'views')
-
- If the package path is not given, ``'templates'`` is assumed.
-
- Per default the template encoding is ``'utf-8'`` which can be changed
- by setting the `encoding` parameter to something else. Due to the nature
- of eggs it's only possible to reload templates if the package was loaded
- from the file system and not a zip file.
- """
-
- def __init__(self, package_name, package_path='templates',
- encoding='utf-8'):
- from pkg_resources import DefaultProvider, ResourceManager, \
- get_provider
- provider = get_provider(package_name)
- self.encoding = encoding
- self.manager = ResourceManager()
- self.filesystem_bound = isinstance(provider, DefaultProvider)
- self.provider = provider
- self.package_path = package_path
-
- def get_source(self, environment, template):
- pieces = split_template_path(template)
- p = '/'.join((self.package_path,) + tuple(pieces))
- if not self.provider.has_resource(p):
- raise TemplateNotFound(template)
-
- filename = uptodate = None
- if self.filesystem_bound:
- filename = self.provider.get_resource_filename(self.manager, p)
- mtime = path.getmtime(filename)
- def uptodate():
- try:
- return path.getmtime(filename) == mtime
- except OSError:
- return False
-
- source = self.provider.get_resource_string(self.manager, p)
- return source.decode(self.encoding), filename, uptodate
-
- def list_templates(self):
- path = self.package_path
- if path[:2] == './':
- path = path[2:]
- elif path == '.':
- path = ''
- offset = len(path)
- results = []
- def _walk(path):
- for filename in self.provider.resource_listdir(path):
- fullname = path + '/' + filename
- if self.provider.resource_isdir(fullname):
- for item in _walk(fullname):
- results.append(item)
- else:
- results.append(fullname[offset:].lstrip('/'))
- _walk(path)
- results.sort()
- return results
-
-
-class DictLoader(BaseLoader):
- """Loads a template from a python dict. It's passed a dict of unicode
- strings bound to template names. This loader is useful for unittesting:
-
- >>> loader = DictLoader({'index.html': 'source here'})
-
- Because auto reloading is rarely useful this is disabled per default.
- """
-
- def __init__(self, mapping):
- self.mapping = mapping
-
- def get_source(self, environment, template):
- if template in self.mapping:
- source = self.mapping[template]
- return source, None, lambda: source != self.mapping.get(template)
- raise TemplateNotFound(template)
-
- def list_templates(self):
- return sorted(self.mapping)
-
-
-class FunctionLoader(BaseLoader):
- """A loader that is passed a function which does the loading. The
- function becomes the name of the template passed and has to return either
- an unicode string with the template source, a tuple in the form ``(source,
- filename, uptodatefunc)`` or `None` if the template does not exist.
-
- >>> def load_template(name):
- ... if name == 'index.html':
- ... return '...'
- ...
- >>> loader = FunctionLoader(load_template)
-
- The `uptodatefunc` is a function that is called if autoreload is enabled
- and has to return `True` if the template is still up to date. For more
- details have a look at :meth:`BaseLoader.get_source` which has the same
- return value.
- """
-
- def __init__(self, load_func):
- self.load_func = load_func
-
- def get_source(self, environment, template):
- rv = self.load_func(template)
- if rv is None:
- raise TemplateNotFound(template)
- elif isinstance(rv, basestring):
- return rv, None, None
- return rv
-
-
-class PrefixLoader(BaseLoader):
- """A loader that is passed a dict of loaders where each loader is bound
- to a prefix. The prefix is delimited from the template by a slash per
- default, which can be changed by setting the `delimiter` argument to
- something else::
-
- loader = PrefixLoader({
- 'app1': PackageLoader('mypackage.app1'),
- 'app2': PackageLoader('mypackage.app2')
- })
-
- By loading ``'app1/index.html'`` the file from the app1 package is loaded,
- by loading ``'app2/index.html'`` the file from the second.
- """
-
- def __init__(self, mapping, delimiter='/'):
- self.mapping = mapping
- self.delimiter = delimiter
-
- def get_source(self, environment, template):
- try:
- prefix, name = template.split(self.delimiter, 1)
- loader = self.mapping[prefix]
- except (ValueError, KeyError):
- raise TemplateNotFound(template)
- try:
- return loader.get_source(environment, name)
- except TemplateNotFound:
- # re-raise the exception with the correct fileame here.
- # (the one that includes the prefix)
- raise TemplateNotFound(template)
-
- def list_templates(self):
- result = []
- for prefix, loader in self.mapping.iteritems():
- for template in loader.list_templates():
- result.append(prefix + self.delimiter + template)
- return result
-
-
-class ChoiceLoader(BaseLoader):
- """This loader works like the `PrefixLoader` just that no prefix is
- specified. If a template could not be found by one loader the next one
- is tried.
-
- >>> loader = ChoiceLoader([
- ... FileSystemLoader('/path/to/user/templates'),
- ... FileSystemLoader('/path/to/system/templates')
- ... ])
-
- This is useful if you want to allow users to override builtin templates
- from a different location.
- """
-
- def __init__(self, loaders):
- self.loaders = loaders
-
- def get_source(self, environment, template):
- for loader in self.loaders:
- try:
- return loader.get_source(environment, template)
- except TemplateNotFound:
- pass
- raise TemplateNotFound(template)
-
- def list_templates(self):
- found = set()
- for loader in self.loaders:
- found.update(loader.list_templates())
- return sorted(found)
-
-
-class _TemplateModule(ModuleType):
- """Like a normal module but with support for weak references"""
-
-
-class ModuleLoader(BaseLoader):
- """This loader loads templates from precompiled templates.
-
- Example usage:
-
- >>> loader = ChoiceLoader([
- ... ModuleLoader('/path/to/compiled/templates'),
- ... FileSystemLoader('/path/to/templates')
- ... ])
- """
-
- has_source_access = False
-
- def __init__(self, path):
- package_name = '_jinja2_module_templates_%x' % id(self)
-
- # create a fake module that looks for the templates in the
- # path given.
- mod = _TemplateModule(package_name)
- if isinstance(path, basestring):
- path = [path]
- else:
- path = list(path)
- mod.__path__ = path
-
- sys.modules[package_name] = weakref.proxy(mod,
- lambda x: sys.modules.pop(package_name, None))
-
- # the only strong reference, the sys.modules entry is weak
- # so that the garbage collector can remove it once the
- # loader that created it goes out of business.
- self.module = mod
- self.package_name = package_name
-
- @staticmethod
- def get_template_key(name):
- return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest()
-
- @staticmethod
- def get_module_filename(name):
- return ModuleLoader.get_template_key(name) + '.py'
-
- @internalcode
- def load(self, environment, name, globals=None):
- key = self.get_template_key(name)
- module = '%s.%s' % (self.package_name, key)
- mod = getattr(self.module, module, None)
- if mod is None:
- try:
- mod = __import__(module, None, None, ['root'])
- except ImportError:
- raise TemplateNotFound(name)
-
- # remove the entry from sys.modules, we only want the attribute
- # on the module object we have stored on the loader.
- sys.modules.pop(module, None)
-
- return environment.template_class.from_module_dict(
- environment, mod.__dict__, globals)
diff --git a/module/lib/jinja2/meta.py b/module/lib/jinja2/meta.py
deleted file mode 100644
index 3a779a5e9..000000000
--- a/module/lib/jinja2/meta.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.meta
- ~~~~~~~~~~~
-
- This module implements various functions that exposes information about
- templates that might be interesting for various kinds of applications.
-
- :copyright: (c) 2010 by the Jinja Team, see AUTHORS for more details.
- :license: BSD, see LICENSE for more details.
-"""
-from jinja2 import nodes
-from jinja2.compiler import CodeGenerator
-
-
-class TrackingCodeGenerator(CodeGenerator):
- """We abuse the code generator for introspection."""
-
- def __init__(self, environment):
- CodeGenerator.__init__(self, environment, '<introspection>',
- '<introspection>')
- self.undeclared_identifiers = set()
-
- def write(self, x):
- """Don't write."""
-
- def pull_locals(self, frame):
- """Remember all undeclared identifiers."""
- self.undeclared_identifiers.update(frame.identifiers.undeclared)
-
-
-def find_undeclared_variables(ast):
- """Returns a set of all variables in the AST that will be looked up from
- the context at runtime. Because at compile time it's not known which
- variables will be used depending on the path the execution takes at
- runtime, all variables are returned.
-
- >>> from jinja2 import Environment, meta
- >>> env = Environment()
- >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}')
- >>> meta.find_undeclared_variables(ast)
- set(['bar'])
-
- .. admonition:: Implementation
-
- Internally the code generator is used for finding undeclared variables.
- This is good to know because the code generator might raise a
- :exc:`TemplateAssertionError` during compilation and as a matter of
- fact this function can currently raise that exception as well.
- """
- codegen = TrackingCodeGenerator(ast.environment)
- codegen.visit(ast)
- return codegen.undeclared_identifiers
-
-
-def find_referenced_templates(ast):
- """Finds all the referenced templates from the AST. This will return an
- iterator over all the hardcoded template extensions, inclusions and
- imports. If dynamic inheritance or inclusion is used, `None` will be
- yielded.
-
- >>> from jinja2 import Environment, meta
- >>> env = Environment()
- >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}')
- >>> list(meta.find_referenced_templates(ast))
- ['layout.html', None]
-
- This function is useful for dependency tracking. For example if you want
- to rebuild parts of the website after a layout template has changed.
- """
- for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import,
- nodes.Include)):
- if not isinstance(node.template, nodes.Const):
- # a tuple with some non consts in there
- if isinstance(node.template, (nodes.Tuple, nodes.List)):
- for template_name in node.template.items:
- # something const, only yield the strings and ignore
- # non-string consts that really just make no sense
- if isinstance(template_name, nodes.Const):
- if isinstance(template_name.value, basestring):
- yield template_name.value
- # something dynamic in there
- else:
- yield None
- # something dynamic we don't know about here
- else:
- yield None
- continue
- # constant is a basestring, direct template name
- if isinstance(node.template.value, basestring):
- yield node.template.value
- # a tuple or list (latter *should* not happen) made of consts,
- # yield the consts that are strings. We could warn here for
- # non string values
- elif isinstance(node, nodes.Include) and \
- isinstance(node.template.value, (tuple, list)):
- for template_name in node.template.value:
- if isinstance(template_name, basestring):
- yield template_name
- # something else we don't care about, we could warn here
- else:
- yield None
diff --git a/module/lib/jinja2/nodes.py b/module/lib/jinja2/nodes.py
deleted file mode 100644
index 6446c70ea..000000000
--- a/module/lib/jinja2/nodes.py
+++ /dev/null
@@ -1,901 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.nodes
- ~~~~~~~~~~~~
-
- This module implements additional nodes derived from the ast base node.
-
- It also provides some node tree helper functions like `in_lineno` and
- `get_nodes` used by the parser and translator in order to normalize
- python and jinja nodes.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import operator
-from itertools import chain, izip
-from collections import deque
-from jinja2.utils import Markup, MethodType, FunctionType
-
-
-#: the types we support for context functions
-_context_function_types = (FunctionType, MethodType)
-
-
-_binop_to_func = {
- '*': operator.mul,
- '/': operator.truediv,
- '//': operator.floordiv,
- '**': operator.pow,
- '%': operator.mod,
- '+': operator.add,
- '-': operator.sub
-}
-
-_uaop_to_func = {
- 'not': operator.not_,
- '+': operator.pos,
- '-': operator.neg
-}
-
-_cmpop_to_func = {
- 'eq': operator.eq,
- 'ne': operator.ne,
- 'gt': operator.gt,
- 'gteq': operator.ge,
- 'lt': operator.lt,
- 'lteq': operator.le,
- 'in': lambda a, b: a in b,
- 'notin': lambda a, b: a not in b
-}
-
-
-class Impossible(Exception):
- """Raised if the node could not perform a requested action."""
-
-
-class NodeType(type):
- """A metaclass for nodes that handles the field and attribute
- inheritance. fields and attributes from the parent class are
- automatically forwarded to the child."""
-
- def __new__(cls, name, bases, d):
- for attr in 'fields', 'attributes':
- storage = []
- storage.extend(getattr(bases[0], attr, ()))
- storage.extend(d.get(attr, ()))
- assert len(bases) == 1, 'multiple inheritance not allowed'
- assert len(storage) == len(set(storage)), 'layout conflict'
- d[attr] = tuple(storage)
- d.setdefault('abstract', False)
- return type.__new__(cls, name, bases, d)
-
-
-class EvalContext(object):
- """Holds evaluation time information. Custom attributes can be attached
- to it in extensions.
- """
-
- def __init__(self, environment, template_name=None):
- if callable(environment.autoescape):
- self.autoescape = environment.autoescape(template_name)
- else:
- self.autoescape = environment.autoescape
- self.volatile = False
-
- def save(self):
- return self.__dict__.copy()
-
- def revert(self, old):
- self.__dict__.clear()
- self.__dict__.update(old)
-
-
-def get_eval_context(node, ctx):
- if ctx is None:
- if node.environment is None:
- raise RuntimeError('if no eval context is passed, the '
- 'node must have an attached '
- 'environment.')
- return EvalContext(node.environment)
- return ctx
-
-
-class Node(object):
- """Baseclass for all Jinja2 nodes. There are a number of nodes available
- of different types. There are three major types:
-
- - :class:`Stmt`: statements
- - :class:`Expr`: expressions
- - :class:`Helper`: helper nodes
- - :class:`Template`: the outermost wrapper node
-
- All nodes have fields and attributes. Fields may be other nodes, lists,
- or arbitrary values. Fields are passed to the constructor as regular
- positional arguments, attributes as keyword arguments. Each node has
- two attributes: `lineno` (the line number of the node) and `environment`.
- The `environment` attribute is set at the end of the parsing process for
- all nodes automatically.
- """
- __metaclass__ = NodeType
- fields = ()
- attributes = ('lineno', 'environment')
- abstract = True
-
- def __init__(self, *fields, **attributes):
- if self.abstract:
- raise TypeError('abstract nodes are not instanciable')
- if fields:
- if len(fields) != len(self.fields):
- if not self.fields:
- raise TypeError('%r takes 0 arguments' %
- self.__class__.__name__)
- raise TypeError('%r takes 0 or %d argument%s' % (
- self.__class__.__name__,
- len(self.fields),
- len(self.fields) != 1 and 's' or ''
- ))
- for name, arg in izip(self.fields, fields):
- setattr(self, name, arg)
- for attr in self.attributes:
- setattr(self, attr, attributes.pop(attr, None))
- if attributes:
- raise TypeError('unknown attribute %r' %
- iter(attributes).next())
-
- def iter_fields(self, exclude=None, only=None):
- """This method iterates over all fields that are defined and yields
- ``(key, value)`` tuples. Per default all fields are returned, but
- it's possible to limit that to some fields by providing the `only`
- parameter or to exclude some using the `exclude` parameter. Both
- should be sets or tuples of field names.
- """
- for name in self.fields:
- if (exclude is only is None) or \
- (exclude is not None and name not in exclude) or \
- (only is not None and name in only):
- try:
- yield name, getattr(self, name)
- except AttributeError:
- pass
-
- def iter_child_nodes(self, exclude=None, only=None):
- """Iterates over all direct child nodes of the node. This iterates
- over all fields and yields the values of they are nodes. If the value
- of a field is a list all the nodes in that list are returned.
- """
- for field, item in self.iter_fields(exclude, only):
- if isinstance(item, list):
- for n in item:
- if isinstance(n, Node):
- yield n
- elif isinstance(item, Node):
- yield item
-
- def find(self, node_type):
- """Find the first node of a given type. If no such node exists the
- return value is `None`.
- """
- for result in self.find_all(node_type):
- return result
-
- def find_all(self, node_type):
- """Find all the nodes of a given type. If the type is a tuple,
- the check is performed for any of the tuple items.
- """
- for child in self.iter_child_nodes():
- if isinstance(child, node_type):
- yield child
- for result in child.find_all(node_type):
- yield result
-
- def set_ctx(self, ctx):
- """Reset the context of a node and all child nodes. Per default the
- parser will all generate nodes that have a 'load' context as it's the
- most common one. This method is used in the parser to set assignment
- targets and other nodes to a store context.
- """
- todo = deque([self])
- while todo:
- node = todo.popleft()
- if 'ctx' in node.fields:
- node.ctx = ctx
- todo.extend(node.iter_child_nodes())
- return self
-
- def set_lineno(self, lineno, override=False):
- """Set the line numbers of the node and children."""
- todo = deque([self])
- while todo:
- node = todo.popleft()
- if 'lineno' in node.attributes:
- if node.lineno is None or override:
- node.lineno = lineno
- todo.extend(node.iter_child_nodes())
- return self
-
- def set_environment(self, environment):
- """Set the environment for all nodes."""
- todo = deque([self])
- while todo:
- node = todo.popleft()
- node.environment = environment
- todo.extend(node.iter_child_nodes())
- return self
-
- def __eq__(self, other):
- return type(self) is type(other) and \
- tuple(self.iter_fields()) == tuple(other.iter_fields())
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def __repr__(self):
- return '%s(%s)' % (
- self.__class__.__name__,
- ', '.join('%s=%r' % (arg, getattr(self, arg, None)) for
- arg in self.fields)
- )
-
-
-class Stmt(Node):
- """Base node for all statements."""
- abstract = True
-
-
-class Helper(Node):
- """Nodes that exist in a specific context only."""
- abstract = True
-
-
-class Template(Node):
- """Node that represents a template. This must be the outermost node that
- is passed to the compiler.
- """
- fields = ('body',)
-
-
-class Output(Stmt):
- """A node that holds multiple expressions which are then printed out.
- This is used both for the `print` statement and the regular template data.
- """
- fields = ('nodes',)
-
-
-class Extends(Stmt):
- """Represents an extends statement."""
- fields = ('template',)
-
-
-class For(Stmt):
- """The for loop. `target` is the target for the iteration (usually a
- :class:`Name` or :class:`Tuple`), `iter` the iterable. `body` is a list
- of nodes that are used as loop-body, and `else_` a list of nodes for the
- `else` block. If no else node exists it has to be an empty list.
-
- For filtered nodes an expression can be stored as `test`, otherwise `None`.
- """
- fields = ('target', 'iter', 'body', 'else_', 'test', 'recursive')
-
-
-class If(Stmt):
- """If `test` is true, `body` is rendered, else `else_`."""
- fields = ('test', 'body', 'else_')
-
-
-class Macro(Stmt):
- """A macro definition. `name` is the name of the macro, `args` a list of
- arguments and `defaults` a list of defaults if there are any. `body` is
- a list of nodes for the macro body.
- """
- fields = ('name', 'args', 'defaults', 'body')
-
-
-class CallBlock(Stmt):
- """Like a macro without a name but a call instead. `call` is called with
- the unnamed macro as `caller` argument this node holds.
- """
- fields = ('call', 'args', 'defaults', 'body')
-
-
-class FilterBlock(Stmt):
- """Node for filter sections."""
- fields = ('body', 'filter')
-
-
-class Block(Stmt):
- """A node that represents a block."""
- fields = ('name', 'body', 'scoped')
-
-
-class Include(Stmt):
- """A node that represents the include tag."""
- fields = ('template', 'with_context', 'ignore_missing')
-
-
-class Import(Stmt):
- """A node that represents the import tag."""
- fields = ('template', 'target', 'with_context')
-
-
-class FromImport(Stmt):
- """A node that represents the from import tag. It's important to not
- pass unsafe names to the name attribute. The compiler translates the
- attribute lookups directly into getattr calls and does *not* use the
- subscript callback of the interface. As exported variables may not
- start with double underscores (which the parser asserts) this is not a
- problem for regular Jinja code, but if this node is used in an extension
- extra care must be taken.
-
- The list of names may contain tuples if aliases are wanted.
- """
- fields = ('template', 'names', 'with_context')
-
-
-class ExprStmt(Stmt):
- """A statement that evaluates an expression and discards the result."""
- fields = ('node',)
-
-
-class Assign(Stmt):
- """Assigns an expression to a target."""
- fields = ('target', 'node')
-
-
-class Expr(Node):
- """Baseclass for all expressions."""
- abstract = True
-
- def as_const(self, eval_ctx=None):
- """Return the value of the expression as constant or raise
- :exc:`Impossible` if this was not possible.
-
- An :class:`EvalContext` can be provided, if none is given
- a default context is created which requires the nodes to have
- an attached environment.
-
- .. versionchanged:: 2.4
- the `eval_ctx` parameter was added.
- """
- raise Impossible()
-
- def can_assign(self):
- """Check if it's possible to assign something to this node."""
- return False
-
-
-class BinExpr(Expr):
- """Baseclass for all binary expressions."""
- fields = ('left', 'right')
- operator = None
- abstract = True
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- f = _binop_to_func[self.operator]
- try:
- return f(self.left.as_const(eval_ctx), self.right.as_const(eval_ctx))
- except:
- raise Impossible()
-
-
-class UnaryExpr(Expr):
- """Baseclass for all unary expressions."""
- fields = ('node',)
- operator = None
- abstract = True
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- f = _uaop_to_func[self.operator]
- try:
- return f(self.node.as_const(eval_ctx))
- except:
- raise Impossible()
-
-
-class Name(Expr):
- """Looks up a name or stores a value in a name.
- The `ctx` of the node can be one of the following values:
-
- - `store`: store a value in the name
- - `load`: load that name
- - `param`: like `store` but if the name was defined as function parameter.
- """
- fields = ('name', 'ctx')
-
- def can_assign(self):
- return self.name not in ('true', 'false', 'none',
- 'True', 'False', 'None')
-
-
-class Literal(Expr):
- """Baseclass for literals."""
- abstract = True
-
-
-class Const(Literal):
- """All constant values. The parser will return this node for simple
- constants such as ``42`` or ``"foo"`` but it can be used to store more
- complex values such as lists too. Only constants with a safe
- representation (objects where ``eval(repr(x)) == x`` is true).
- """
- fields = ('value',)
-
- def as_const(self, eval_ctx=None):
- return self.value
-
- @classmethod
- def from_untrusted(cls, value, lineno=None, environment=None):
- """Return a const object if the value is representable as
- constant value in the generated code, otherwise it will raise
- an `Impossible` exception.
- """
- from compiler import has_safe_repr
- if not has_safe_repr(value):
- raise Impossible()
- return cls(value, lineno=lineno, environment=environment)
-
-
-class TemplateData(Literal):
- """A constant template string."""
- fields = ('data',)
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- if eval_ctx.volatile:
- raise Impossible()
- if eval_ctx.autoescape:
- return Markup(self.data)
- return self.data
-
-
-class Tuple(Literal):
- """For loop unpacking and some other things like multiple arguments
- for subscripts. Like for :class:`Name` `ctx` specifies if the tuple
- is used for loading the names or storing.
- """
- fields = ('items', 'ctx')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return tuple(x.as_const(eval_ctx) for x in self.items)
-
- def can_assign(self):
- for item in self.items:
- if not item.can_assign():
- return False
- return True
-
-
-class List(Literal):
- """Any list literal such as ``[1, 2, 3]``"""
- fields = ('items',)
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return [x.as_const(eval_ctx) for x in self.items]
-
-
-class Dict(Literal):
- """Any dict literal such as ``{1: 2, 3: 4}``. The items must be a list of
- :class:`Pair` nodes.
- """
- fields = ('items',)
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return dict(x.as_const(eval_ctx) for x in self.items)
-
-
-class Pair(Helper):
- """A key, value pair for dicts."""
- fields = ('key', 'value')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.key.as_const(eval_ctx), self.value.as_const(eval_ctx)
-
-
-class Keyword(Helper):
- """A key, value pair for keyword arguments where key is a string."""
- fields = ('key', 'value')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.key, self.value.as_const(eval_ctx)
-
-
-class CondExpr(Expr):
- """A conditional expression (inline if expression). (``{{
- foo if bar else baz }}``)
- """
- fields = ('test', 'expr1', 'expr2')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- if self.test.as_const(eval_ctx):
- return self.expr1.as_const(eval_ctx)
-
- # if we evaluate to an undefined object, we better do that at runtime
- if self.expr2 is None:
- raise Impossible()
-
- return self.expr2.as_const(eval_ctx)
-
-
-class Filter(Expr):
- """This node applies a filter on an expression. `name` is the name of
- the filter, the rest of the fields are the same as for :class:`Call`.
-
- If the `node` of a filter is `None` the contents of the last buffer are
- filtered. Buffers are created by macros and filter blocks.
- """
- fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- if eval_ctx.volatile or self.node is None:
- raise Impossible()
- # we have to be careful here because we call filter_ below.
- # if this variable would be called filter, 2to3 would wrap the
- # call in a list beause it is assuming we are talking about the
- # builtin filter function here which no longer returns a list in
- # python 3. because of that, do not rename filter_ to filter!
- filter_ = self.environment.filters.get(self.name)
- if filter_ is None or getattr(filter_, 'contextfilter', False):
- raise Impossible()
- obj = self.node.as_const(eval_ctx)
- args = [x.as_const(eval_ctx) for x in self.args]
- if getattr(filter_, 'evalcontextfilter', False):
- args.insert(0, eval_ctx)
- elif getattr(filter_, 'environmentfilter', False):
- args.insert(0, self.environment)
- kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs)
- if self.dyn_args is not None:
- try:
- args.extend(self.dyn_args.as_const(eval_ctx))
- except:
- raise Impossible()
- if self.dyn_kwargs is not None:
- try:
- kwargs.update(self.dyn_kwargs.as_const(eval_ctx))
- except:
- raise Impossible()
- try:
- return filter_(obj, *args, **kwargs)
- except:
- raise Impossible()
-
-
-class Test(Expr):
- """Applies a test on an expression. `name` is the name of the test, the
- rest of the fields are the same as for :class:`Call`.
- """
- fields = ('node', 'name', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
-
-
-class Call(Expr):
- """Calls an expression. `args` is a list of arguments, `kwargs` a list
- of keyword arguments (list of :class:`Keyword` nodes), and `dyn_args`
- and `dyn_kwargs` has to be either `None` or a node that is used as
- node for dynamic positional (``*args``) or keyword (``**kwargs``)
- arguments.
- """
- fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- if eval_ctx.volatile:
- raise Impossible()
- obj = self.node.as_const(eval_ctx)
-
- # don't evaluate context functions
- args = [x.as_const(eval_ctx) for x in self.args]
- if isinstance(obj, _context_function_types):
- if getattr(obj, 'contextfunction', False):
- raise Impossible()
- elif getattr(obj, 'evalcontextfunction', False):
- args.insert(0, eval_ctx)
- elif getattr(obj, 'environmentfunction', False):
- args.insert(0, self.environment)
-
- kwargs = dict(x.as_const(eval_ctx) for x in self.kwargs)
- if self.dyn_args is not None:
- try:
- args.extend(self.dyn_args.as_const(eval_ctx))
- except:
- raise Impossible()
- if self.dyn_kwargs is not None:
- try:
- kwargs.update(self.dyn_kwargs.as_const(eval_ctx))
- except:
- raise Impossible()
- try:
- return obj(*args, **kwargs)
- except:
- raise Impossible()
-
-
-class Getitem(Expr):
- """Get an attribute or item from an expression and prefer the item."""
- fields = ('node', 'arg', 'ctx')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- if self.ctx != 'load':
- raise Impossible()
- try:
- return self.environment.getitem(self.node.as_const(eval_ctx),
- self.arg.as_const(eval_ctx))
- except:
- raise Impossible()
-
- def can_assign(self):
- return False
-
-
-class Getattr(Expr):
- """Get an attribute or item from an expression that is a ascii-only
- bytestring and prefer the attribute.
- """
- fields = ('node', 'attr', 'ctx')
-
- def as_const(self, eval_ctx=None):
- if self.ctx != 'load':
- raise Impossible()
- try:
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.environment.getattr(self.node.as_const(eval_ctx),
- self.attr)
- except:
- raise Impossible()
-
- def can_assign(self):
- return False
-
-
-class Slice(Expr):
- """Represents a slice object. This must only be used as argument for
- :class:`Subscript`.
- """
- fields = ('start', 'stop', 'step')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- def const(obj):
- if obj is None:
- return None
- return obj.as_const(eval_ctx)
- return slice(const(self.start), const(self.stop), const(self.step))
-
-
-class Concat(Expr):
- """Concatenates the list of expressions provided after converting them to
- unicode.
- """
- fields = ('nodes',)
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return ''.join(unicode(x.as_const(eval_ctx)) for x in self.nodes)
-
-
-class Compare(Expr):
- """Compares an expression with some other expressions. `ops` must be a
- list of :class:`Operand`\s.
- """
- fields = ('expr', 'ops')
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- result = value = self.expr.as_const(eval_ctx)
- try:
- for op in self.ops:
- new_value = op.expr.as_const(eval_ctx)
- result = _cmpop_to_func[op.op](value, new_value)
- value = new_value
- except:
- raise Impossible()
- return result
-
-
-class Operand(Helper):
- """Holds an operator and an expression."""
- fields = ('op', 'expr')
-
-if __debug__:
- Operand.__doc__ += '\nThe following operators are available: ' + \
- ', '.join(sorted('``%s``' % x for x in set(_binop_to_func) |
- set(_uaop_to_func) | set(_cmpop_to_func)))
-
-
-class Mul(BinExpr):
- """Multiplies the left with the right node."""
- operator = '*'
-
-
-class Div(BinExpr):
- """Divides the left by the right node."""
- operator = '/'
-
-
-class FloorDiv(BinExpr):
- """Divides the left by the right node and truncates conver the
- result into an integer by truncating.
- """
- operator = '//'
-
-
-class Add(BinExpr):
- """Add the left to the right node."""
- operator = '+'
-
-
-class Sub(BinExpr):
- """Substract the right from the left node."""
- operator = '-'
-
-
-class Mod(BinExpr):
- """Left modulo right."""
- operator = '%'
-
-
-class Pow(BinExpr):
- """Left to the power of right."""
- operator = '**'
-
-
-class And(BinExpr):
- """Short circuited AND."""
- operator = 'and'
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.left.as_const(eval_ctx) and self.right.as_const(eval_ctx)
-
-
-class Or(BinExpr):
- """Short circuited OR."""
- operator = 'or'
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return self.left.as_const(eval_ctx) or self.right.as_const(eval_ctx)
-
-
-class Not(UnaryExpr):
- """Negate the expression."""
- operator = 'not'
-
-
-class Neg(UnaryExpr):
- """Make the expression negative."""
- operator = '-'
-
-
-class Pos(UnaryExpr):
- """Make the expression positive (noop for most expressions)"""
- operator = '+'
-
-
-# Helpers for extensions
-
-
-class EnvironmentAttribute(Expr):
- """Loads an attribute from the environment object. This is useful for
- extensions that want to call a callback stored on the environment.
- """
- fields = ('name',)
-
-
-class ExtensionAttribute(Expr):
- """Returns the attribute of an extension bound to the environment.
- The identifier is the identifier of the :class:`Extension`.
-
- This node is usually constructed by calling the
- :meth:`~jinja2.ext.Extension.attr` method on an extension.
- """
- fields = ('identifier', 'name')
-
-
-class ImportedName(Expr):
- """If created with an import name the import name is returned on node
- access. For example ``ImportedName('cgi.escape')`` returns the `escape`
- function from the cgi module on evaluation. Imports are optimized by the
- compiler so there is no need to assign them to local variables.
- """
- fields = ('importname',)
-
-
-class InternalName(Expr):
- """An internal name in the compiler. You cannot create these nodes
- yourself but the parser provides a
- :meth:`~jinja2.parser.Parser.free_identifier` method that creates
- a new identifier for you. This identifier is not available from the
- template and is not threated specially by the compiler.
- """
- fields = ('name',)
-
- def __init__(self):
- raise TypeError('Can\'t create internal names. Use the '
- '`free_identifier` method on a parser.')
-
-
-class MarkSafe(Expr):
- """Mark the wrapped expression as safe (wrap it as `Markup`)."""
- fields = ('expr',)
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- return Markup(self.expr.as_const(eval_ctx))
-
-
-class MarkSafeIfAutoescape(Expr):
- """Mark the wrapped expression as safe (wrap it as `Markup`) but
- only if autoescaping is active.
-
- .. versionadded:: 2.5
- """
- fields = ('expr',)
-
- def as_const(self, eval_ctx=None):
- eval_ctx = get_eval_context(self, eval_ctx)
- if eval_ctx.volatile:
- raise Impossible()
- expr = self.expr.as_const(eval_ctx)
- if eval_ctx.autoescape:
- return Markup(expr)
- return expr
-
-
-class ContextReference(Expr):
- """Returns the current template context. It can be used like a
- :class:`Name` node, with a ``'load'`` ctx and will return the
- current :class:`~jinja2.runtime.Context` object.
-
- Here an example that assigns the current template name to a
- variable named `foo`::
-
- Assign(Name('foo', ctx='store'),
- Getattr(ContextReference(), 'name'))
- """
-
-
-class Continue(Stmt):
- """Continue a loop."""
-
-
-class Break(Stmt):
- """Break a loop."""
-
-
-class Scope(Stmt):
- """An artificial scope."""
- fields = ('body',)
-
-
-class EvalContextModifier(Stmt):
- """Modifies the eval context. For each option that should be modified,
- a :class:`Keyword` has to be added to the :attr:`options` list.
-
- Example to change the `autoescape` setting::
-
- EvalContextModifier(options=[Keyword('autoescape', Const(True))])
- """
- fields = ('options',)
-
-
-class ScopedEvalContextModifier(EvalContextModifier):
- """Modifies the eval context and reverts it later. Works exactly like
- :class:`EvalContextModifier` but will only modify the
- :class:`~jinja2.nodes.EvalContext` for nodes in the :attr:`body`.
- """
- fields = ('body',)
-
-
-# make sure nobody creates custom nodes
-def _failing_new(*args, **kwargs):
- raise TypeError('can\'t create custom node types')
-NodeType.__new__ = staticmethod(_failing_new); del _failing_new
diff --git a/module/lib/jinja2/optimizer.py b/module/lib/jinja2/optimizer.py
deleted file mode 100644
index 00eab115e..000000000
--- a/module/lib/jinja2/optimizer.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.optimizer
- ~~~~~~~~~~~~~~~~
-
- The jinja optimizer is currently trying to constant fold a few expressions
- and modify the AST in place so that it should be easier to evaluate it.
-
- Because the AST does not contain all the scoping information and the
- compiler has to find that out, we cannot do all the optimizations we
- want. For example loop unrolling doesn't work because unrolled loops would
- have a different scoping.
-
- The solution would be a second syntax tree that has the scoping rules stored.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD.
-"""
-from jinja2 import nodes
-from jinja2.visitor import NodeTransformer
-
-
-def optimize(node, environment):
- """The context hint can be used to perform an static optimization
- based on the context given."""
- optimizer = Optimizer(environment)
- return optimizer.visit(node)
-
-
-class Optimizer(NodeTransformer):
-
- def __init__(self, environment):
- self.environment = environment
-
- def visit_If(self, node):
- """Eliminate dead code."""
- # do not optimize ifs that have a block inside so that it doesn't
- # break super().
- if node.find(nodes.Block) is not None:
- return self.generic_visit(node)
- try:
- val = self.visit(node.test).as_const()
- except nodes.Impossible:
- return self.generic_visit(node)
- if val:
- body = node.body
- else:
- body = node.else_
- result = []
- for node in body:
- result.extend(self.visit_list(node))
- return result
-
- def fold(self, node):
- """Do constant folding."""
- node = self.generic_visit(node)
- try:
- return nodes.Const.from_untrusted(node.as_const(),
- lineno=node.lineno,
- environment=self.environment)
- except nodes.Impossible:
- return node
-
- visit_Add = visit_Sub = visit_Mul = visit_Div = visit_FloorDiv = \
- visit_Pow = visit_Mod = visit_And = visit_Or = visit_Pos = visit_Neg = \
- visit_Not = visit_Compare = visit_Getitem = visit_Getattr = visit_Call = \
- visit_Filter = visit_Test = visit_CondExpr = fold
- del fold
diff --git a/module/lib/jinja2/parser.py b/module/lib/jinja2/parser.py
deleted file mode 100644
index d44229ad0..000000000
--- a/module/lib/jinja2/parser.py
+++ /dev/null
@@ -1,896 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.parser
- ~~~~~~~~~~~~~
-
- Implements the template parser.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-from jinja2 import nodes
-from jinja2.exceptions import TemplateSyntaxError, TemplateAssertionError
-from jinja2.utils import next
-from jinja2.lexer import describe_token, describe_token_expr
-
-
-#: statements that callinto
-_statement_keywords = frozenset(['for', 'if', 'block', 'extends', 'print',
- 'macro', 'include', 'from', 'import',
- 'set'])
-_compare_operators = frozenset(['eq', 'ne', 'lt', 'lteq', 'gt', 'gteq'])
-
-
-class Parser(object):
- """This is the central parsing class Jinja2 uses. It's passed to
- extensions and can be used to parse expressions or statements.
- """
-
- def __init__(self, environment, source, name=None, filename=None,
- state=None):
- self.environment = environment
- self.stream = environment._tokenize(source, name, filename, state)
- self.name = name
- self.filename = filename
- self.closed = False
- self.extensions = {}
- for extension in environment.iter_extensions():
- for tag in extension.tags:
- self.extensions[tag] = extension.parse
- self._last_identifier = 0
- self._tag_stack = []
- self._end_token_stack = []
-
- def fail(self, msg, lineno=None, exc=TemplateSyntaxError):
- """Convenience method that raises `exc` with the message, passed
- line number or last line number as well as the current name and
- filename.
- """
- if lineno is None:
- lineno = self.stream.current.lineno
- raise exc(msg, lineno, self.name, self.filename)
-
- def _fail_ut_eof(self, name, end_token_stack, lineno):
- expected = []
- for exprs in end_token_stack:
- expected.extend(map(describe_token_expr, exprs))
- if end_token_stack:
- currently_looking = ' or '.join(
- "'%s'" % describe_token_expr(expr)
- for expr in end_token_stack[-1])
- else:
- currently_looking = None
-
- if name is None:
- message = ['Unexpected end of template.']
- else:
- message = ['Encountered unknown tag \'%s\'.' % name]
-
- if currently_looking:
- if name is not None and name in expected:
- message.append('You probably made a nesting mistake. Jinja '
- 'is expecting this tag, but currently looking '
- 'for %s.' % currently_looking)
- else:
- message.append('Jinja was looking for the following tags: '
- '%s.' % currently_looking)
-
- if self._tag_stack:
- message.append('The innermost block that needs to be '
- 'closed is \'%s\'.' % self._tag_stack[-1])
-
- self.fail(' '.join(message), lineno)
-
- def fail_unknown_tag(self, name, lineno=None):
- """Called if the parser encounters an unknown tag. Tries to fail
- with a human readable error message that could help to identify
- the problem.
- """
- return self._fail_ut_eof(name, self._end_token_stack, lineno)
-
- def fail_eof(self, end_tokens=None, lineno=None):
- """Like fail_unknown_tag but for end of template situations."""
- stack = list(self._end_token_stack)
- if end_tokens is not None:
- stack.append(end_tokens)
- return self._fail_ut_eof(None, stack, lineno)
-
- def is_tuple_end(self, extra_end_rules=None):
- """Are we at the end of a tuple?"""
- if self.stream.current.type in ('variable_end', 'block_end', 'rparen'):
- return True
- elif extra_end_rules is not None:
- return self.stream.current.test_any(extra_end_rules)
- return False
-
- def free_identifier(self, lineno=None):
- """Return a new free identifier as :class:`~jinja2.nodes.InternalName`."""
- self._last_identifier += 1
- rv = object.__new__(nodes.InternalName)
- nodes.Node.__init__(rv, 'fi%d' % self._last_identifier, lineno=lineno)
- return rv
-
- def parse_statement(self):
- """Parse a single statement."""
- token = self.stream.current
- if token.type != 'name':
- self.fail('tag name expected', token.lineno)
- self._tag_stack.append(token.value)
- pop_tag = True
- try:
- if token.value in _statement_keywords:
- return getattr(self, 'parse_' + self.stream.current.value)()
- if token.value == 'call':
- return self.parse_call_block()
- if token.value == 'filter':
- return self.parse_filter_block()
- ext = self.extensions.get(token.value)
- if ext is not None:
- return ext(self)
-
- # did not work out, remove the token we pushed by accident
- # from the stack so that the unknown tag fail function can
- # produce a proper error message.
- self._tag_stack.pop()
- pop_tag = False
- self.fail_unknown_tag(token.value, token.lineno)
- finally:
- if pop_tag:
- self._tag_stack.pop()
-
- def parse_statements(self, end_tokens, drop_needle=False):
- """Parse multiple statements into a list until one of the end tokens
- is reached. This is used to parse the body of statements as it also
- parses template data if appropriate. The parser checks first if the
- current token is a colon and skips it if there is one. Then it checks
- for the block end and parses until if one of the `end_tokens` is
- reached. Per default the active token in the stream at the end of
- the call is the matched end token. If this is not wanted `drop_needle`
- can be set to `True` and the end token is removed.
- """
- # the first token may be a colon for python compatibility
- self.stream.skip_if('colon')
-
- # in the future it would be possible to add whole code sections
- # by adding some sort of end of statement token and parsing those here.
- self.stream.expect('block_end')
- result = self.subparse(end_tokens)
-
- # we reached the end of the template too early, the subparser
- # does not check for this, so we do that now
- if self.stream.current.type == 'eof':
- self.fail_eof(end_tokens)
-
- if drop_needle:
- next(self.stream)
- return result
-
- def parse_set(self):
- """Parse an assign statement."""
- lineno = next(self.stream).lineno
- target = self.parse_assign_target()
- self.stream.expect('assign')
- expr = self.parse_tuple()
- return nodes.Assign(target, expr, lineno=lineno)
-
- def parse_for(self):
- """Parse a for loop."""
- lineno = self.stream.expect('name:for').lineno
- target = self.parse_assign_target(extra_end_rules=('name:in',))
- self.stream.expect('name:in')
- iter = self.parse_tuple(with_condexpr=False,
- extra_end_rules=('name:recursive',))
- test = None
- if self.stream.skip_if('name:if'):
- test = self.parse_expression()
- recursive = self.stream.skip_if('name:recursive')
- body = self.parse_statements(('name:endfor', 'name:else'))
- if next(self.stream).value == 'endfor':
- else_ = []
- else:
- else_ = self.parse_statements(('name:endfor',), drop_needle=True)
- return nodes.For(target, iter, body, else_, test,
- recursive, lineno=lineno)
-
- def parse_if(self):
- """Parse an if construct."""
- node = result = nodes.If(lineno=self.stream.expect('name:if').lineno)
- while 1:
- node.test = self.parse_tuple(with_condexpr=False)
- node.body = self.parse_statements(('name:elif', 'name:else',
- 'name:endif'))
- token = next(self.stream)
- if token.test('name:elif'):
- new_node = nodes.If(lineno=self.stream.current.lineno)
- node.else_ = [new_node]
- node = new_node
- continue
- elif token.test('name:else'):
- node.else_ = self.parse_statements(('name:endif',),
- drop_needle=True)
- else:
- node.else_ = []
- break
- return result
-
- def parse_block(self):
- node = nodes.Block(lineno=next(self.stream).lineno)
- node.name = self.stream.expect('name').value
- node.scoped = self.stream.skip_if('name:scoped')
-
- # common problem people encounter when switching from django
- # to jinja. we do not support hyphens in block names, so let's
- # raise a nicer error message in that case.
- if self.stream.current.type == 'sub':
- self.fail('Block names in Jinja have to be valid Python '
- 'identifiers and may not contain hypens, use an '
- 'underscore instead.')
-
- node.body = self.parse_statements(('name:endblock',), drop_needle=True)
- self.stream.skip_if('name:' + node.name)
- return node
-
- def parse_extends(self):
- node = nodes.Extends(lineno=next(self.stream).lineno)
- node.template = self.parse_expression()
- return node
-
- def parse_import_context(self, node, default):
- if self.stream.current.test_any('name:with', 'name:without') and \
- self.stream.look().test('name:context'):
- node.with_context = next(self.stream).value == 'with'
- self.stream.skip()
- else:
- node.with_context = default
- return node
-
- def parse_include(self):
- node = nodes.Include(lineno=next(self.stream).lineno)
- node.template = self.parse_expression()
- if self.stream.current.test('name:ignore') and \
- self.stream.look().test('name:missing'):
- node.ignore_missing = True
- self.stream.skip(2)
- else:
- node.ignore_missing = False
- return self.parse_import_context(node, True)
-
- def parse_import(self):
- node = nodes.Import(lineno=next(self.stream).lineno)
- node.template = self.parse_expression()
- self.stream.expect('name:as')
- node.target = self.parse_assign_target(name_only=True).name
- return self.parse_import_context(node, False)
-
- def parse_from(self):
- node = nodes.FromImport(lineno=next(self.stream).lineno)
- node.template = self.parse_expression()
- self.stream.expect('name:import')
- node.names = []
-
- def parse_context():
- if self.stream.current.value in ('with', 'without') and \
- self.stream.look().test('name:context'):
- node.with_context = next(self.stream).value == 'with'
- self.stream.skip()
- return True
- return False
-
- while 1:
- if node.names:
- self.stream.expect('comma')
- if self.stream.current.type == 'name':
- if parse_context():
- break
- target = self.parse_assign_target(name_only=True)
- if target.name.startswith('_'):
- self.fail('names starting with an underline can not '
- 'be imported', target.lineno,
- exc=TemplateAssertionError)
- if self.stream.skip_if('name:as'):
- alias = self.parse_assign_target(name_only=True)
- node.names.append((target.name, alias.name))
- else:
- node.names.append(target.name)
- if parse_context() or self.stream.current.type != 'comma':
- break
- else:
- break
- if not hasattr(node, 'with_context'):
- node.with_context = False
- self.stream.skip_if('comma')
- return node
-
- def parse_signature(self, node):
- node.args = args = []
- node.defaults = defaults = []
- self.stream.expect('lparen')
- while self.stream.current.type != 'rparen':
- if args:
- self.stream.expect('comma')
- arg = self.parse_assign_target(name_only=True)
- arg.set_ctx('param')
- if self.stream.skip_if('assign'):
- defaults.append(self.parse_expression())
- args.append(arg)
- self.stream.expect('rparen')
-
- def parse_call_block(self):
- node = nodes.CallBlock(lineno=next(self.stream).lineno)
- if self.stream.current.type == 'lparen':
- self.parse_signature(node)
- else:
- node.args = []
- node.defaults = []
-
- node.call = self.parse_expression()
- if not isinstance(node.call, nodes.Call):
- self.fail('expected call', node.lineno)
- node.body = self.parse_statements(('name:endcall',), drop_needle=True)
- return node
-
- def parse_filter_block(self):
- node = nodes.FilterBlock(lineno=next(self.stream).lineno)
- node.filter = self.parse_filter(None, start_inline=True)
- node.body = self.parse_statements(('name:endfilter',),
- drop_needle=True)
- return node
-
- def parse_macro(self):
- node = nodes.Macro(lineno=next(self.stream).lineno)
- node.name = self.parse_assign_target(name_only=True).name
- self.parse_signature(node)
- node.body = self.parse_statements(('name:endmacro',),
- drop_needle=True)
- return node
-
- def parse_print(self):
- node = nodes.Output(lineno=next(self.stream).lineno)
- node.nodes = []
- while self.stream.current.type != 'block_end':
- if node.nodes:
- self.stream.expect('comma')
- node.nodes.append(self.parse_expression())
- return node
-
- def parse_assign_target(self, with_tuple=True, name_only=False,
- extra_end_rules=None):
- """Parse an assignment target. As Jinja2 allows assignments to
- tuples, this function can parse all allowed assignment targets. Per
- default assignments to tuples are parsed, that can be disable however
- by setting `with_tuple` to `False`. If only assignments to names are
- wanted `name_only` can be set to `True`. The `extra_end_rules`
- parameter is forwarded to the tuple parsing function.
- """
- if name_only:
- token = self.stream.expect('name')
- target = nodes.Name(token.value, 'store', lineno=token.lineno)
- else:
- if with_tuple:
- target = self.parse_tuple(simplified=True,
- extra_end_rules=extra_end_rules)
- else:
- target = self.parse_primary()
- target.set_ctx('store')
- if not target.can_assign():
- self.fail('can\'t assign to %r' % target.__class__.
- __name__.lower(), target.lineno)
- return target
-
- def parse_expression(self, with_condexpr=True):
- """Parse an expression. Per default all expressions are parsed, if
- the optional `with_condexpr` parameter is set to `False` conditional
- expressions are not parsed.
- """
- if with_condexpr:
- return self.parse_condexpr()
- return self.parse_or()
-
- def parse_condexpr(self):
- lineno = self.stream.current.lineno
- expr1 = self.parse_or()
- while self.stream.skip_if('name:if'):
- expr2 = self.parse_or()
- if self.stream.skip_if('name:else'):
- expr3 = self.parse_condexpr()
- else:
- expr3 = None
- expr1 = nodes.CondExpr(expr2, expr1, expr3, lineno=lineno)
- lineno = self.stream.current.lineno
- return expr1
-
- def parse_or(self):
- lineno = self.stream.current.lineno
- left = self.parse_and()
- while self.stream.skip_if('name:or'):
- right = self.parse_and()
- left = nodes.Or(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_and(self):
- lineno = self.stream.current.lineno
- left = self.parse_not()
- while self.stream.skip_if('name:and'):
- right = self.parse_not()
- left = nodes.And(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_not(self):
- if self.stream.current.test('name:not'):
- lineno = next(self.stream).lineno
- return nodes.Not(self.parse_not(), lineno=lineno)
- return self.parse_compare()
-
- def parse_compare(self):
- lineno = self.stream.current.lineno
- expr = self.parse_add()
- ops = []
- while 1:
- token_type = self.stream.current.type
- if token_type in _compare_operators:
- next(self.stream)
- ops.append(nodes.Operand(token_type, self.parse_add()))
- elif self.stream.skip_if('name:in'):
- ops.append(nodes.Operand('in', self.parse_add()))
- elif self.stream.current.test('name:not') and \
- self.stream.look().test('name:in'):
- self.stream.skip(2)
- ops.append(nodes.Operand('notin', self.parse_add()))
- else:
- break
- lineno = self.stream.current.lineno
- if not ops:
- return expr
- return nodes.Compare(expr, ops, lineno=lineno)
-
- def parse_add(self):
- lineno = self.stream.current.lineno
- left = self.parse_sub()
- while self.stream.current.type == 'add':
- next(self.stream)
- right = self.parse_sub()
- left = nodes.Add(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_sub(self):
- lineno = self.stream.current.lineno
- left = self.parse_concat()
- while self.stream.current.type == 'sub':
- next(self.stream)
- right = self.parse_concat()
- left = nodes.Sub(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_concat(self):
- lineno = self.stream.current.lineno
- args = [self.parse_mul()]
- while self.stream.current.type == 'tilde':
- next(self.stream)
- args.append(self.parse_mul())
- if len(args) == 1:
- return args[0]
- return nodes.Concat(args, lineno=lineno)
-
- def parse_mul(self):
- lineno = self.stream.current.lineno
- left = self.parse_div()
- while self.stream.current.type == 'mul':
- next(self.stream)
- right = self.parse_div()
- left = nodes.Mul(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_div(self):
- lineno = self.stream.current.lineno
- left = self.parse_floordiv()
- while self.stream.current.type == 'div':
- next(self.stream)
- right = self.parse_floordiv()
- left = nodes.Div(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_floordiv(self):
- lineno = self.stream.current.lineno
- left = self.parse_mod()
- while self.stream.current.type == 'floordiv':
- next(self.stream)
- right = self.parse_mod()
- left = nodes.FloorDiv(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_mod(self):
- lineno = self.stream.current.lineno
- left = self.parse_pow()
- while self.stream.current.type == 'mod':
- next(self.stream)
- right = self.parse_pow()
- left = nodes.Mod(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_pow(self):
- lineno = self.stream.current.lineno
- left = self.parse_unary()
- while self.stream.current.type == 'pow':
- next(self.stream)
- right = self.parse_unary()
- left = nodes.Pow(left, right, lineno=lineno)
- lineno = self.stream.current.lineno
- return left
-
- def parse_unary(self, with_filter=True):
- token_type = self.stream.current.type
- lineno = self.stream.current.lineno
- if token_type == 'sub':
- next(self.stream)
- node = nodes.Neg(self.parse_unary(False), lineno=lineno)
- elif token_type == 'add':
- next(self.stream)
- node = nodes.Pos(self.parse_unary(False), lineno=lineno)
- else:
- node = self.parse_primary()
- node = self.parse_postfix(node)
- if with_filter:
- node = self.parse_filter_expr(node)
- return node
-
- def parse_primary(self):
- token = self.stream.current
- if token.type == 'name':
- if token.value in ('true', 'false', 'True', 'False'):
- node = nodes.Const(token.value in ('true', 'True'),
- lineno=token.lineno)
- elif token.value in ('none', 'None'):
- node = nodes.Const(None, lineno=token.lineno)
- else:
- node = nodes.Name(token.value, 'load', lineno=token.lineno)
- next(self.stream)
- elif token.type == 'string':
- next(self.stream)
- buf = [token.value]
- lineno = token.lineno
- while self.stream.current.type == 'string':
- buf.append(self.stream.current.value)
- next(self.stream)
- node = nodes.Const(''.join(buf), lineno=lineno)
- elif token.type in ('integer', 'float'):
- next(self.stream)
- node = nodes.Const(token.value, lineno=token.lineno)
- elif token.type == 'lparen':
- next(self.stream)
- node = self.parse_tuple(explicit_parentheses=True)
- self.stream.expect('rparen')
- elif token.type == 'lbracket':
- node = self.parse_list()
- elif token.type == 'lbrace':
- node = self.parse_dict()
- else:
- self.fail("unexpected '%s'" % describe_token(token), token.lineno)
- return node
-
- def parse_tuple(self, simplified=False, with_condexpr=True,
- extra_end_rules=None, explicit_parentheses=False):
- """Works like `parse_expression` but if multiple expressions are
- delimited by a comma a :class:`~jinja2.nodes.Tuple` node is created.
- This method could also return a regular expression instead of a tuple
- if no commas where found.
-
- The default parsing mode is a full tuple. If `simplified` is `True`
- only names and literals are parsed. The `no_condexpr` parameter is
- forwarded to :meth:`parse_expression`.
-
- Because tuples do not require delimiters and may end in a bogus comma
- an extra hint is needed that marks the end of a tuple. For example
- for loops support tuples between `for` and `in`. In that case the
- `extra_end_rules` is set to ``['name:in']``.
-
- `explicit_parentheses` is true if the parsing was triggered by an
- expression in parentheses. This is used to figure out if an empty
- tuple is a valid expression or not.
- """
- lineno = self.stream.current.lineno
- if simplified:
- parse = self.parse_primary
- elif with_condexpr:
- parse = self.parse_expression
- else:
- parse = lambda: self.parse_expression(with_condexpr=False)
- args = []
- is_tuple = False
- while 1:
- if args:
- self.stream.expect('comma')
- if self.is_tuple_end(extra_end_rules):
- break
- args.append(parse())
- if self.stream.current.type == 'comma':
- is_tuple = True
- else:
- break
- lineno = self.stream.current.lineno
-
- if not is_tuple:
- if args:
- return args[0]
-
- # if we don't have explicit parentheses, an empty tuple is
- # not a valid expression. This would mean nothing (literally
- # nothing) in the spot of an expression would be an empty
- # tuple.
- if not explicit_parentheses:
- self.fail('Expected an expression, got \'%s\'' %
- describe_token(self.stream.current))
-
- return nodes.Tuple(args, 'load', lineno=lineno)
-
- def parse_list(self):
- token = self.stream.expect('lbracket')
- items = []
- while self.stream.current.type != 'rbracket':
- if items:
- self.stream.expect('comma')
- if self.stream.current.type == 'rbracket':
- break
- items.append(self.parse_expression())
- self.stream.expect('rbracket')
- return nodes.List(items, lineno=token.lineno)
-
- def parse_dict(self):
- token = self.stream.expect('lbrace')
- items = []
- while self.stream.current.type != 'rbrace':
- if items:
- self.stream.expect('comma')
- if self.stream.current.type == 'rbrace':
- break
- key = self.parse_expression()
- self.stream.expect('colon')
- value = self.parse_expression()
- items.append(nodes.Pair(key, value, lineno=key.lineno))
- self.stream.expect('rbrace')
- return nodes.Dict(items, lineno=token.lineno)
-
- def parse_postfix(self, node):
- while 1:
- token_type = self.stream.current.type
- if token_type == 'dot' or token_type == 'lbracket':
- node = self.parse_subscript(node)
- # calls are valid both after postfix expressions (getattr
- # and getitem) as well as filters and tests
- elif token_type == 'lparen':
- node = self.parse_call(node)
- else:
- break
- return node
-
- def parse_filter_expr(self, node):
- while 1:
- token_type = self.stream.current.type
- if token_type == 'pipe':
- node = self.parse_filter(node)
- elif token_type == 'name' and self.stream.current.value == 'is':
- node = self.parse_test(node)
- # calls are valid both after postfix expressions (getattr
- # and getitem) as well as filters and tests
- elif token_type == 'lparen':
- node = self.parse_call(node)
- else:
- break
- return node
-
- def parse_subscript(self, node):
- token = next(self.stream)
- if token.type == 'dot':
- attr_token = self.stream.current
- next(self.stream)
- if attr_token.type == 'name':
- return nodes.Getattr(node, attr_token.value, 'load',
- lineno=token.lineno)
- elif attr_token.type != 'integer':
- self.fail('expected name or number', attr_token.lineno)
- arg = nodes.Const(attr_token.value, lineno=attr_token.lineno)
- return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
- if token.type == 'lbracket':
- priority_on_attribute = False
- args = []
- while self.stream.current.type != 'rbracket':
- if args:
- self.stream.expect('comma')
- args.append(self.parse_subscribed())
- self.stream.expect('rbracket')
- if len(args) == 1:
- arg = args[0]
- else:
- arg = nodes.Tuple(args, 'load', lineno=token.lineno)
- return nodes.Getitem(node, arg, 'load', lineno=token.lineno)
- self.fail('expected subscript expression', self.lineno)
-
- def parse_subscribed(self):
- lineno = self.stream.current.lineno
-
- if self.stream.current.type == 'colon':
- next(self.stream)
- args = [None]
- else:
- node = self.parse_expression()
- if self.stream.current.type != 'colon':
- return node
- next(self.stream)
- args = [node]
-
- if self.stream.current.type == 'colon':
- args.append(None)
- elif self.stream.current.type not in ('rbracket', 'comma'):
- args.append(self.parse_expression())
- else:
- args.append(None)
-
- if self.stream.current.type == 'colon':
- next(self.stream)
- if self.stream.current.type not in ('rbracket', 'comma'):
- args.append(self.parse_expression())
- else:
- args.append(None)
- else:
- args.append(None)
-
- return nodes.Slice(lineno=lineno, *args)
-
- def parse_call(self, node):
- token = self.stream.expect('lparen')
- args = []
- kwargs = []
- dyn_args = dyn_kwargs = None
- require_comma = False
-
- def ensure(expr):
- if not expr:
- self.fail('invalid syntax for function call expression',
- token.lineno)
-
- while self.stream.current.type != 'rparen':
- if require_comma:
- self.stream.expect('comma')
- # support for trailing comma
- if self.stream.current.type == 'rparen':
- break
- if self.stream.current.type == 'mul':
- ensure(dyn_args is None and dyn_kwargs is None)
- next(self.stream)
- dyn_args = self.parse_expression()
- elif self.stream.current.type == 'pow':
- ensure(dyn_kwargs is None)
- next(self.stream)
- dyn_kwargs = self.parse_expression()
- else:
- ensure(dyn_args is None and dyn_kwargs is None)
- if self.stream.current.type == 'name' and \
- self.stream.look().type == 'assign':
- key = self.stream.current.value
- self.stream.skip(2)
- value = self.parse_expression()
- kwargs.append(nodes.Keyword(key, value,
- lineno=value.lineno))
- else:
- ensure(not kwargs)
- args.append(self.parse_expression())
-
- require_comma = True
- self.stream.expect('rparen')
-
- if node is None:
- return args, kwargs, dyn_args, dyn_kwargs
- return nodes.Call(node, args, kwargs, dyn_args, dyn_kwargs,
- lineno=token.lineno)
-
- def parse_filter(self, node, start_inline=False):
- while self.stream.current.type == 'pipe' or start_inline:
- if not start_inline:
- next(self.stream)
- token = self.stream.expect('name')
- name = token.value
- while self.stream.current.type == 'dot':
- next(self.stream)
- name += '.' + self.stream.expect('name').value
- if self.stream.current.type == 'lparen':
- args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
- else:
- args = []
- kwargs = []
- dyn_args = dyn_kwargs = None
- node = nodes.Filter(node, name, args, kwargs, dyn_args,
- dyn_kwargs, lineno=token.lineno)
- start_inline = False
- return node
-
- def parse_test(self, node):
- token = next(self.stream)
- if self.stream.current.test('name:not'):
- next(self.stream)
- negated = True
- else:
- negated = False
- name = self.stream.expect('name').value
- while self.stream.current.type == 'dot':
- next(self.stream)
- name += '.' + self.stream.expect('name').value
- dyn_args = dyn_kwargs = None
- kwargs = []
- if self.stream.current.type == 'lparen':
- args, kwargs, dyn_args, dyn_kwargs = self.parse_call(None)
- elif self.stream.current.type in ('name', 'string', 'integer',
- 'float', 'lparen', 'lbracket',
- 'lbrace') and not \
- self.stream.current.test_any('name:else', 'name:or',
- 'name:and'):
- if self.stream.current.test('name:is'):
- self.fail('You cannot chain multiple tests with is')
- args = [self.parse_expression()]
- else:
- args = []
- node = nodes.Test(node, name, args, kwargs, dyn_args,
- dyn_kwargs, lineno=token.lineno)
- if negated:
- node = nodes.Not(node, lineno=token.lineno)
- return node
-
- def subparse(self, end_tokens=None):
- body = []
- data_buffer = []
- add_data = data_buffer.append
-
- if end_tokens is not None:
- self._end_token_stack.append(end_tokens)
-
- def flush_data():
- if data_buffer:
- lineno = data_buffer[0].lineno
- body.append(nodes.Output(data_buffer[:], lineno=lineno))
- del data_buffer[:]
-
- try:
- while self.stream:
- token = self.stream.current
- if token.type == 'data':
- if token.value:
- add_data(nodes.TemplateData(token.value,
- lineno=token.lineno))
- next(self.stream)
- elif token.type == 'variable_begin':
- next(self.stream)
- add_data(self.parse_tuple(with_condexpr=True))
- self.stream.expect('variable_end')
- elif token.type == 'block_begin':
- flush_data()
- next(self.stream)
- if end_tokens is not None and \
- self.stream.current.test_any(*end_tokens):
- return body
- rv = self.parse_statement()
- if isinstance(rv, list):
- body.extend(rv)
- else:
- body.append(rv)
- self.stream.expect('block_end')
- else:
- raise AssertionError('internal parsing error')
-
- flush_data()
- finally:
- if end_tokens is not None:
- self._end_token_stack.pop()
-
- return body
-
- def parse(self):
- """Parse the whole template into a `Template` node."""
- result = nodes.Template(self.subparse(), lineno=1)
- result.set_environment(self.environment)
- return result
diff --git a/module/lib/jinja2/runtime.py b/module/lib/jinja2/runtime.py
deleted file mode 100644
index 6fea3aa4f..000000000
--- a/module/lib/jinja2/runtime.py
+++ /dev/null
@@ -1,544 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.runtime
- ~~~~~~~~~~~~~~
-
- Runtime helpers.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD.
-"""
-import sys
-from itertools import chain, imap
-from jinja2.nodes import EvalContext, _context_function_types
-from jinja2.utils import Markup, partial, soft_unicode, escape, missing, \
- concat, internalcode, next, object_type_repr
-from jinja2.exceptions import UndefinedError, TemplateRuntimeError, \
- TemplateNotFound
-
-
-# these variables are exported to the template runtime
-__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
- 'TemplateRuntimeError', 'missing', 'concat', 'escape',
- 'markup_join', 'unicode_join', 'to_string', 'identity',
- 'TemplateNotFound']
-
-#: the name of the function that is used to convert something into
-#: a string. 2to3 will adopt that automatically and the generated
-#: code can take advantage of it.
-to_string = unicode
-
-#: the identity function. Useful for certain things in the environment
-identity = lambda x: x
-
-
-def markup_join(seq):
- """Concatenation that escapes if necessary and converts to unicode."""
- buf = []
- iterator = imap(soft_unicode, seq)
- for arg in iterator:
- buf.append(arg)
- if hasattr(arg, '__html__'):
- return Markup(u'').join(chain(buf, iterator))
- return concat(buf)
-
-
-def unicode_join(seq):
- """Simple args to unicode conversion and concatenation."""
- return concat(imap(unicode, seq))
-
-
-def new_context(environment, template_name, blocks, vars=None,
- shared=None, globals=None, locals=None):
- """Internal helper to for context creation."""
- if vars is None:
- vars = {}
- if shared:
- parent = vars
- else:
- parent = dict(globals or (), **vars)
- if locals:
- # if the parent is shared a copy should be created because
- # we don't want to modify the dict passed
- if shared:
- parent = dict(parent)
- for key, value in locals.iteritems():
- if key[:2] == 'l_' and value is not missing:
- parent[key[2:]] = value
- return Context(environment, parent, template_name, blocks)
-
-
-class TemplateReference(object):
- """The `self` in templates."""
-
- def __init__(self, context):
- self.__context = context
-
- def __getitem__(self, name):
- blocks = self.__context.blocks[name]
- wrap = self.__context.eval_ctx.autoescape and \
- Markup or (lambda x: x)
- return BlockReference(name, self.__context, blocks, 0)
-
- def __repr__(self):
- return '<%s %r>' % (
- self.__class__.__name__,
- self.__context.name
- )
-
-
-class Context(object):
- """The template context holds the variables of a template. It stores the
- values passed to the template and also the names the template exports.
- Creating instances is neither supported nor useful as it's created
- automatically at various stages of the template evaluation and should not
- be created by hand.
-
- The context is immutable. Modifications on :attr:`parent` **must not**
- happen and modifications on :attr:`vars` are allowed from generated
- template code only. Template filters and global functions marked as
- :func:`contextfunction`\s get the active context passed as first argument
- and are allowed to access the context read-only.
-
- The template context supports read only dict operations (`get`,
- `keys`, `values`, `items`, `iterkeys`, `itervalues`, `iteritems`,
- `__getitem__`, `__contains__`). Additionally there is a :meth:`resolve`
- method that doesn't fail with a `KeyError` but returns an
- :class:`Undefined` object for missing variables.
- """
- __slots__ = ('parent', 'vars', 'environment', 'eval_ctx', 'exported_vars',
- 'name', 'blocks', '__weakref__')
-
- def __init__(self, environment, parent, name, blocks):
- self.parent = parent
- self.vars = {}
- self.environment = environment
- self.eval_ctx = EvalContext(self.environment, name)
- self.exported_vars = set()
- self.name = name
-
- # create the initial mapping of blocks. Whenever template inheritance
- # takes place the runtime will update this mapping with the new blocks
- # from the template.
- self.blocks = dict((k, [v]) for k, v in blocks.iteritems())
-
- def super(self, name, current):
- """Render a parent block."""
- try:
- blocks = self.blocks[name]
- index = blocks.index(current) + 1
- blocks[index]
- except LookupError:
- return self.environment.undefined('there is no parent block '
- 'called %r.' % name,
- name='super')
- return BlockReference(name, self, blocks, index)
-
- def get(self, key, default=None):
- """Returns an item from the template context, if it doesn't exist
- `default` is returned.
- """
- try:
- return self[key]
- except KeyError:
- return default
-
- def resolve(self, key):
- """Looks up a variable like `__getitem__` or `get` but returns an
- :class:`Undefined` object with the name of the name looked up.
- """
- if key in self.vars:
- return self.vars[key]
- if key in self.parent:
- return self.parent[key]
- return self.environment.undefined(name=key)
-
- def get_exported(self):
- """Get a new dict with the exported variables."""
- return dict((k, self.vars[k]) for k in self.exported_vars)
-
- def get_all(self):
- """Return a copy of the complete context as dict including the
- exported variables.
- """
- return dict(self.parent, **self.vars)
-
- @internalcode
- def call(__self, __obj, *args, **kwargs):
- """Call the callable with the arguments and keyword arguments
- provided but inject the active context or environment as first
- argument if the callable is a :func:`contextfunction` or
- :func:`environmentfunction`.
- """
- if __debug__:
- __traceback_hide__ = True
- if isinstance(__obj, _context_function_types):
- if getattr(__obj, 'contextfunction', 0):
- args = (__self,) + args
- elif getattr(__obj, 'evalcontextfunction', 0):
- args = (__self.eval_ctx,) + args
- elif getattr(__obj, 'environmentfunction', 0):
- args = (__self.environment,) + args
- try:
- return __obj(*args, **kwargs)
- except StopIteration:
- return __self.environment.undefined('value was undefined because '
- 'a callable raised a '
- 'StopIteration exception')
-
- def derived(self, locals=None):
- """Internal helper function to create a derived context."""
- context = new_context(self.environment, self.name, {},
- self.parent, True, None, locals)
- context.eval_ctx = self.eval_ctx
- context.blocks.update((k, list(v)) for k, v in self.blocks.iteritems())
- return context
-
- def _all(meth):
- proxy = lambda self: getattr(self.get_all(), meth)()
- proxy.__doc__ = getattr(dict, meth).__doc__
- proxy.__name__ = meth
- return proxy
-
- keys = _all('keys')
- values = _all('values')
- items = _all('items')
-
- # not available on python 3
- if hasattr(dict, 'iterkeys'):
- iterkeys = _all('iterkeys')
- itervalues = _all('itervalues')
- iteritems = _all('iteritems')
- del _all
-
- def __contains__(self, name):
- return name in self.vars or name in self.parent
-
- def __getitem__(self, key):
- """Lookup a variable or raise `KeyError` if the variable is
- undefined.
- """
- item = self.resolve(key)
- if isinstance(item, Undefined):
- raise KeyError(key)
- return item
-
- def __repr__(self):
- return '<%s %s of %r>' % (
- self.__class__.__name__,
- repr(self.get_all()),
- self.name
- )
-
-
-# register the context as mapping if possible
-try:
- from collections import Mapping
- Mapping.register(Context)
-except ImportError:
- pass
-
-
-class BlockReference(object):
- """One block on a template reference."""
-
- def __init__(self, name, context, stack, depth):
- self.name = name
- self._context = context
- self._stack = stack
- self._depth = depth
-
- @property
- def super(self):
- """Super the block."""
- if self._depth + 1 >= len(self._stack):
- return self._context.environment. \
- undefined('there is no parent block called %r.' %
- self.name, name='super')
- return BlockReference(self.name, self._context, self._stack,
- self._depth + 1)
-
- @internalcode
- def __call__(self):
- rv = concat(self._stack[self._depth](self._context))
- if self._context.eval_ctx.autoescape:
- rv = Markup(rv)
- return rv
-
-
-class LoopContext(object):
- """A loop context for dynamic iteration."""
-
- def __init__(self, iterable, recurse=None):
- self._iterator = iter(iterable)
- self._recurse = recurse
- self.index0 = -1
-
- # try to get the length of the iterable early. This must be done
- # here because there are some broken iterators around where there
- # __len__ is the number of iterations left (i'm looking at your
- # listreverseiterator!).
- try:
- self._length = len(iterable)
- except (TypeError, AttributeError):
- self._length = None
-
- def cycle(self, *args):
- """Cycles among the arguments with the current loop index."""
- if not args:
- raise TypeError('no items for cycling given')
- return args[self.index0 % len(args)]
-
- first = property(lambda x: x.index0 == 0)
- last = property(lambda x: x.index0 + 1 == x.length)
- index = property(lambda x: x.index0 + 1)
- revindex = property(lambda x: x.length - x.index0)
- revindex0 = property(lambda x: x.length - x.index)
-
- def __len__(self):
- return self.length
-
- def __iter__(self):
- return LoopContextIterator(self)
-
- @internalcode
- def loop(self, iterable):
- if self._recurse is None:
- raise TypeError('Tried to call non recursive loop. Maybe you '
- "forgot the 'recursive' modifier.")
- return self._recurse(iterable, self._recurse)
-
- # a nifty trick to enhance the error message if someone tried to call
- # the the loop without or with too many arguments.
- __call__ = loop
- del loop
-
- @property
- def length(self):
- if self._length is None:
- # if was not possible to get the length of the iterator when
- # the loop context was created (ie: iterating over a generator)
- # we have to convert the iterable into a sequence and use the
- # length of that.
- iterable = tuple(self._iterator)
- self._iterator = iter(iterable)
- self._length = len(iterable) + self.index0 + 1
- return self._length
-
- def __repr__(self):
- return '<%s %r/%r>' % (
- self.__class__.__name__,
- self.index,
- self.length
- )
-
-
-class LoopContextIterator(object):
- """The iterator for a loop context."""
- __slots__ = ('context',)
-
- def __init__(self, context):
- self.context = context
-
- def __iter__(self):
- return self
-
- def next(self):
- ctx = self.context
- ctx.index0 += 1
- return next(ctx._iterator), ctx
-
-
-class Macro(object):
- """Wraps a macro function."""
-
- def __init__(self, environment, func, name, arguments, defaults,
- catch_kwargs, catch_varargs, caller):
- self._environment = environment
- self._func = func
- self._argument_count = len(arguments)
- self.name = name
- self.arguments = arguments
- self.defaults = defaults
- self.catch_kwargs = catch_kwargs
- self.catch_varargs = catch_varargs
- self.caller = caller
-
- @internalcode
- def __call__(self, *args, **kwargs):
- # try to consume the positional arguments
- arguments = list(args[:self._argument_count])
- off = len(arguments)
-
- # if the number of arguments consumed is not the number of
- # arguments expected we start filling in keyword arguments
- # and defaults.
- if off != self._argument_count:
- for idx, name in enumerate(self.arguments[len(arguments):]):
- try:
- value = kwargs.pop(name)
- except KeyError:
- try:
- value = self.defaults[idx - self._argument_count + off]
- except IndexError:
- value = self._environment.undefined(
- 'parameter %r was not provided' % name, name=name)
- arguments.append(value)
-
- # it's important that the order of these arguments does not change
- # if not also changed in the compiler's `function_scoping` method.
- # the order is caller, keyword arguments, positional arguments!
- if self.caller:
- caller = kwargs.pop('caller', None)
- if caller is None:
- caller = self._environment.undefined('No caller defined',
- name='caller')
- arguments.append(caller)
- if self.catch_kwargs:
- arguments.append(kwargs)
- elif kwargs:
- raise TypeError('macro %r takes no keyword argument %r' %
- (self.name, next(iter(kwargs))))
- if self.catch_varargs:
- arguments.append(args[self._argument_count:])
- elif len(args) > self._argument_count:
- raise TypeError('macro %r takes not more than %d argument(s)' %
- (self.name, len(self.arguments)))
- return self._func(*arguments)
-
- def __repr__(self):
- return '<%s %s>' % (
- self.__class__.__name__,
- self.name is None and 'anonymous' or repr(self.name)
- )
-
-
-class Undefined(object):
- """The default undefined type. This undefined type can be printed and
- iterated over, but every other access will raise an :exc:`UndefinedError`:
-
- >>> foo = Undefined(name='foo')
- >>> str(foo)
- ''
- >>> not foo
- True
- >>> foo + 42
- Traceback (most recent call last):
- ...
- UndefinedError: 'foo' is undefined
- """
- __slots__ = ('_undefined_hint', '_undefined_obj', '_undefined_name',
- '_undefined_exception')
-
- def __init__(self, hint=None, obj=missing, name=None, exc=UndefinedError):
- self._undefined_hint = hint
- self._undefined_obj = obj
- self._undefined_name = name
- self._undefined_exception = exc
-
- @internalcode
- def _fail_with_undefined_error(self, *args, **kwargs):
- """Regular callback function for undefined objects that raises an
- `UndefinedError` on call.
- """
- if self._undefined_hint is None:
- if self._undefined_obj is missing:
- hint = '%r is undefined' % self._undefined_name
- elif not isinstance(self._undefined_name, basestring):
- hint = '%s has no element %r' % (
- object_type_repr(self._undefined_obj),
- self._undefined_name
- )
- else:
- hint = '%r has no attribute %r' % (
- object_type_repr(self._undefined_obj),
- self._undefined_name
- )
- else:
- hint = self._undefined_hint
- raise self._undefined_exception(hint)
-
- __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \
- __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \
- __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \
- __getattr__ = __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \
- __int__ = __float__ = __complex__ = __pow__ = __rpow__ = \
- _fail_with_undefined_error
-
- def __str__(self):
- return unicode(self).encode('utf-8')
-
- # unicode goes after __str__ because we configured 2to3 to rename
- # __unicode__ to __str__. because the 2to3 tree is not designed to
- # remove nodes from it, we leave the above __str__ around and let
- # it override at runtime.
- def __unicode__(self):
- return u''
-
- def __len__(self):
- return 0
-
- def __iter__(self):
- if 0:
- yield None
-
- def __nonzero__(self):
- return False
-
- def __repr__(self):
- return 'Undefined'
-
-
-class DebugUndefined(Undefined):
- """An undefined that returns the debug info when printed.
-
- >>> foo = DebugUndefined(name='foo')
- >>> str(foo)
- '{{ foo }}'
- >>> not foo
- True
- >>> foo + 42
- Traceback (most recent call last):
- ...
- UndefinedError: 'foo' is undefined
- """
- __slots__ = ()
-
- def __unicode__(self):
- if self._undefined_hint is None:
- if self._undefined_obj is missing:
- return u'{{ %s }}' % self._undefined_name
- return '{{ no such element: %s[%r] }}' % (
- object_type_repr(self._undefined_obj),
- self._undefined_name
- )
- return u'{{ undefined value printed: %s }}' % self._undefined_hint
-
-
-class StrictUndefined(Undefined):
- """An undefined that barks on print and iteration as well as boolean
- tests and all kinds of comparisons. In other words: you can do nothing
- with it except checking if it's defined using the `defined` test.
-
- >>> foo = StrictUndefined(name='foo')
- >>> str(foo)
- Traceback (most recent call last):
- ...
- UndefinedError: 'foo' is undefined
- >>> not foo
- Traceback (most recent call last):
- ...
- UndefinedError: 'foo' is undefined
- >>> foo + 42
- Traceback (most recent call last):
- ...
- UndefinedError: 'foo' is undefined
- """
- __slots__ = ()
- __iter__ = __unicode__ = __str__ = __len__ = __nonzero__ = __eq__ = \
- __ne__ = __bool__ = Undefined._fail_with_undefined_error
-
-
-# remove remaining slots attributes, after the metaclass did the magic they
-# are unneeded and irritating as they contain wrong data for the subclasses.
-del Undefined.__slots__, DebugUndefined.__slots__, StrictUndefined.__slots__
diff --git a/module/lib/jinja2/sandbox.py b/module/lib/jinja2/sandbox.py
deleted file mode 100644
index 749719548..000000000
--- a/module/lib/jinja2/sandbox.py
+++ /dev/null
@@ -1,271 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.sandbox
- ~~~~~~~~~~~~~~
-
- Adds a sandbox layer to Jinja as it was the default behavior in the old
- Jinja 1 releases. This sandbox is slightly different from Jinja 1 as the
- default behavior is easier to use.
-
- The behavior can be changed by subclassing the environment.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD.
-"""
-import operator
-from jinja2.runtime import Undefined
-from jinja2.environment import Environment
-from jinja2.exceptions import SecurityError
-from jinja2.utils import FunctionType, MethodType, TracebackType, CodeType, \
- FrameType, GeneratorType
-
-
-#: maximum number of items a range may produce
-MAX_RANGE = 100000
-
-#: attributes of function objects that are considered unsafe.
-UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
- 'func_defaults', 'func_globals'])
-
-#: unsafe method attributes. function attributes are unsafe for methods too
-UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
-
-
-import warnings
-
-# make sure we don't warn in python 2.6 about stuff we don't care about
-warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning,
- module='jinja2.sandbox')
-
-from collections import deque
-
-_mutable_set_types = (set,)
-_mutable_mapping_types = (dict,)
-_mutable_sequence_types = (list,)
-
-
-# on python 2.x we can register the user collection types
-try:
- from UserDict import UserDict, DictMixin
- from UserList import UserList
- _mutable_mapping_types += (UserDict, DictMixin)
- _mutable_set_types += (UserList,)
-except ImportError:
- pass
-
-# if sets is still available, register the mutable set from there as well
-try:
- from sets import Set
- _mutable_set_types += (Set,)
-except ImportError:
- pass
-
-#: register Python 2.6 abstract base classes
-try:
- from collections import MutableSet, MutableMapping, MutableSequence
- _mutable_set_types += (MutableSet,)
- _mutable_mapping_types += (MutableMapping,)
- _mutable_sequence_types += (MutableSequence,)
-except ImportError:
- pass
-
-_mutable_spec = (
- (_mutable_set_types, frozenset([
- 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
- 'symmetric_difference_update', 'update'
- ])),
- (_mutable_mapping_types, frozenset([
- 'clear', 'pop', 'popitem', 'setdefault', 'update'
- ])),
- (_mutable_sequence_types, frozenset([
- 'append', 'reverse', 'insert', 'sort', 'extend', 'remove'
- ])),
- (deque, frozenset([
- 'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop',
- 'popleft', 'remove', 'rotate'
- ]))
-)
-
-
-def safe_range(*args):
- """A range that can't generate ranges with a length of more than
- MAX_RANGE items.
- """
- rng = xrange(*args)
- if len(rng) > MAX_RANGE:
- raise OverflowError('range too big, maximum size for range is %d' %
- MAX_RANGE)
- return rng
-
-
-def unsafe(f):
- """
- Mark a function or method as unsafe::
-
- @unsafe
- def delete(self):
- pass
- """
- f.unsafe_callable = True
- return f
-
-
-def is_internal_attribute(obj, attr):
- """Test if the attribute given is an internal python attribute. For
- example this function returns `True` for the `func_code` attribute of
- python objects. This is useful if the environment method
- :meth:`~SandboxedEnvironment.is_safe_attribute` is overriden.
-
- >>> from jinja2.sandbox import is_internal_attribute
- >>> is_internal_attribute(lambda: None, "func_code")
- True
- >>> is_internal_attribute((lambda x:x).func_code, 'co_code')
- True
- >>> is_internal_attribute(str, "upper")
- False
- """
- if isinstance(obj, FunctionType):
- if attr in UNSAFE_FUNCTION_ATTRIBUTES:
- return True
- elif isinstance(obj, MethodType):
- if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
- attr in UNSAFE_METHOD_ATTRIBUTES:
- return True
- elif isinstance(obj, type):
- if attr == 'mro':
- return True
- elif isinstance(obj, (CodeType, TracebackType, FrameType)):
- return True
- elif isinstance(obj, GeneratorType):
- if attr == 'gi_frame':
- return True
- return attr.startswith('__')
-
-
-def modifies_known_mutable(obj, attr):
- """This function checks if an attribute on a builtin mutable object
- (list, dict, set or deque) would modify it if called. It also supports
- the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
- with Python 2.6 onwards the abstract base classes `MutableSet`,
- `MutableMapping`, and `MutableSequence`.
-
- >>> modifies_known_mutable({}, "clear")
- True
- >>> modifies_known_mutable({}, "keys")
- False
- >>> modifies_known_mutable([], "append")
- True
- >>> modifies_known_mutable([], "index")
- False
-
- If called with an unsupported object (such as unicode) `False` is
- returned.
-
- >>> modifies_known_mutable("foo", "upper")
- False
- """
- for typespec, unsafe in _mutable_spec:
- if isinstance(obj, typespec):
- return attr in unsafe
- return False
-
-
-class SandboxedEnvironment(Environment):
- """The sandboxed environment. It works like the regular environment but
- tells the compiler to generate sandboxed code. Additionally subclasses of
- this environment may override the methods that tell the runtime what
- attributes or functions are safe to access.
-
- If the template tries to access insecure code a :exc:`SecurityError` is
- raised. However also other exceptions may occour during the rendering so
- the caller has to ensure that all exceptions are catched.
- """
- sandboxed = True
-
- def __init__(self, *args, **kwargs):
- Environment.__init__(self, *args, **kwargs)
- self.globals['range'] = safe_range
-
- def is_safe_attribute(self, obj, attr, value):
- """The sandboxed environment will call this method to check if the
- attribute of an object is safe to access. Per default all attributes
- starting with an underscore are considered private as well as the
- special attributes of internal python objects as returned by the
- :func:`is_internal_attribute` function.
- """
- return not (attr.startswith('_') or is_internal_attribute(obj, attr))
-
- def is_safe_callable(self, obj):
- """Check if an object is safely callable. Per default a function is
- considered safe unless the `unsafe_callable` attribute exists and is
- True. Override this method to alter the behavior, but this won't
- affect the `unsafe` decorator from this module.
- """
- return not (getattr(obj, 'unsafe_callable', False) or \
- getattr(obj, 'alters_data', False))
-
- def getitem(self, obj, argument):
- """Subscribe an object from sandboxed code."""
- try:
- return obj[argument]
- except (TypeError, LookupError):
- if isinstance(argument, basestring):
- try:
- attr = str(argument)
- except:
- pass
- else:
- try:
- value = getattr(obj, attr)
- except AttributeError:
- pass
- else:
- if self.is_safe_attribute(obj, argument, value):
- return value
- return self.unsafe_undefined(obj, argument)
- return self.undefined(obj=obj, name=argument)
-
- def getattr(self, obj, attribute):
- """Subscribe an object from sandboxed code and prefer the
- attribute. The attribute passed *must* be a bytestring.
- """
- try:
- value = getattr(obj, attribute)
- except AttributeError:
- try:
- return obj[attribute]
- except (TypeError, LookupError):
- pass
- else:
- if self.is_safe_attribute(obj, attribute, value):
- return value
- return self.unsafe_undefined(obj, attribute)
- return self.undefined(obj=obj, name=attribute)
-
- def unsafe_undefined(self, obj, attribute):
- """Return an undefined object for unsafe attributes."""
- return self.undefined('access to attribute %r of %r '
- 'object is unsafe.' % (
- attribute,
- obj.__class__.__name__
- ), name=attribute, obj=obj, exc=SecurityError)
-
- def call(__self, __context, __obj, *args, **kwargs):
- """Call an object from sandboxed code."""
- # the double prefixes are to avoid double keyword argument
- # errors when proxying the call.
- if not __self.is_safe_callable(__obj):
- raise SecurityError('%r is not safely callable' % (__obj,))
- return __context.call(__obj, *args, **kwargs)
-
-
-class ImmutableSandboxedEnvironment(SandboxedEnvironment):
- """Works exactly like the regular `SandboxedEnvironment` but does not
- permit modifications on the builtin mutable objects `list`, `set`, and
- `dict` by using the :func:`modifies_known_mutable` function.
- """
-
- def is_safe_attribute(self, obj, attr, value):
- if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
- return False
- return not modifies_known_mutable(obj, attr)
diff --git a/module/lib/jinja2/tests.py b/module/lib/jinja2/tests.py
deleted file mode 100644
index d257eca0a..000000000
--- a/module/lib/jinja2/tests.py
+++ /dev/null
@@ -1,146 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.tests
- ~~~~~~~~~~~~
-
- Jinja test functions. Used with the "is" operator.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import re
-from jinja2.runtime import Undefined
-
-# nose, nothing here to test
-__test__ = False
-
-
-number_re = re.compile(r'^-?\d+(\.\d+)?$')
-regex_type = type(number_re)
-
-
-try:
- test_callable = callable
-except NameError:
- def test_callable(x):
- return hasattr(x, '__call__')
-
-
-def test_odd(value):
- """Return true if the variable is odd."""
- return value % 2 == 1
-
-
-def test_even(value):
- """Return true if the variable is even."""
- return value % 2 == 0
-
-
-def test_divisibleby(value, num):
- """Check if a variable is divisible by a number."""
- return value % num == 0
-
-
-def test_defined(value):
- """Return true if the variable is defined:
-
- .. sourcecode:: jinja
-
- {% if variable is defined %}
- value of variable: {{ variable }}
- {% else %}
- variable is not defined
- {% endif %}
-
- See the :func:`default` filter for a simple way to set undefined
- variables.
- """
- return not isinstance(value, Undefined)
-
-
-def test_undefined(value):
- """Like :func:`defined` but the other way round."""
- return isinstance(value, Undefined)
-
-
-def test_none(value):
- """Return true if the variable is none."""
- return value is None
-
-
-def test_lower(value):
- """Return true if the variable is lowercased."""
- return unicode(value).islower()
-
-
-def test_upper(value):
- """Return true if the variable is uppercased."""
- return unicode(value).isupper()
-
-
-def test_string(value):
- """Return true if the object is a string."""
- return isinstance(value, basestring)
-
-
-def test_number(value):
- """Return true if the variable is a number."""
- return isinstance(value, (int, long, float, complex))
-
-
-def test_sequence(value):
- """Return true if the variable is a sequence. Sequences are variables
- that are iterable.
- """
- try:
- len(value)
- value.__getitem__
- except:
- return False
- return True
-
-
-def test_sameas(value, other):
- """Check if an object points to the same memory address than another
- object:
-
- .. sourcecode:: jinja
-
- {% if foo.attribute is sameas false %}
- the foo attribute really is the `False` singleton
- {% endif %}
- """
- return value is other
-
-
-def test_iterable(value):
- """Check if it's possible to iterate over an object."""
- try:
- iter(value)
- except TypeError:
- return False
- return True
-
-
-def test_escaped(value):
- """Check if the value is escaped."""
- return hasattr(value, '__html__')
-
-
-TESTS = {
- 'odd': test_odd,
- 'even': test_even,
- 'divisibleby': test_divisibleby,
- 'defined': test_defined,
- 'undefined': test_undefined,
- 'none': test_none,
- 'lower': test_lower,
- 'upper': test_upper,
- 'string': test_string,
- 'number': test_number,
- 'sequence': test_sequence,
- 'iterable': test_iterable,
- 'callable': test_callable,
- 'sameas': test_sameas,
- 'escaped': test_escaped
-}
diff --git a/module/lib/jinja2/utils.py b/module/lib/jinja2/utils.py
deleted file mode 100644
index 7b77b8eb7..000000000
--- a/module/lib/jinja2/utils.py
+++ /dev/null
@@ -1,601 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.utils
- ~~~~~~~~~~~~
-
- Utility functions.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD, see LICENSE for more details.
-"""
-import re
-import sys
-import errno
-try:
- from thread import allocate_lock
-except ImportError:
- from dummy_thread import allocate_lock
-from collections import deque
-from itertools import imap
-
-
-_word_split_re = re.compile(r'(\s+)')
-_punctuation_re = re.compile(
- '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
- '|'.join(imap(re.escape, ('(', '<', '&lt;'))),
- '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
- )
-)
-_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
-_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
-_entity_re = re.compile(r'&([^;]+);')
-_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
-_digits = '0123456789'
-
-# special singleton representing missing values for the runtime
-missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
-
-# internal code
-internal_code = set()
-
-
-# concatenate a list of strings and convert them to unicode.
-# unfortunately there is a bug in python 2.4 and lower that causes
-# unicode.join trash the traceback.
-_concat = u''.join
-try:
- def _test_gen_bug():
- raise TypeError(_test_gen_bug)
- yield None
- _concat(_test_gen_bug())
-except TypeError, _error:
- if not _error.args or _error.args[0] is not _test_gen_bug:
- def concat(gen):
- try:
- return _concat(list(gen))
- except:
- # this hack is needed so that the current frame
- # does not show up in the traceback.
- exc_type, exc_value, tb = sys.exc_info()
- raise exc_type, exc_value, tb.tb_next
- else:
- concat = _concat
- del _test_gen_bug, _error
-
-
-# for python 2.x we create outselves a next() function that does the
-# basics without exception catching.
-try:
- next = next
-except NameError:
- def next(x):
- return x.next()
-
-
-# if this python version is unable to deal with unicode filenames
-# when passed to encode we let this function encode it properly.
-# This is used in a couple of places. As far as Jinja is concerned
-# filenames are unicode *or* bytestrings in 2.x and unicode only in
-# 3.x because compile cannot handle bytes
-if sys.version_info < (3, 0):
- def _encode_filename(filename):
- if isinstance(filename, unicode):
- return filename.encode('utf-8')
- return filename
-else:
- def _encode_filename(filename):
- assert filename is None or isinstance(filename, str), \
- 'filenames must be strings'
- return filename
-
-from keyword import iskeyword as is_python_keyword
-
-
-# common types. These do exist in the special types module too which however
-# does not exist in IronPython out of the box. Also that way we don't have
-# to deal with implementation specific stuff here
-class _C(object):
- def method(self): pass
-def _func():
- yield None
-FunctionType = type(_func)
-GeneratorType = type(_func())
-MethodType = type(_C.method)
-CodeType = type(_C.method.func_code)
-try:
- raise TypeError()
-except TypeError:
- _tb = sys.exc_info()[2]
- TracebackType = type(_tb)
- FrameType = type(_tb.tb_frame)
-del _C, _tb, _func
-
-
-def contextfunction(f):
- """This decorator can be used to mark a function or method context callable.
- A context callable is passed the active :class:`Context` as first argument when
- called from the template. This is useful if a function wants to get access
- to the context or functions provided on the context object. For example
- a function that returns a sorted list of template variables the current
- template exports could look like this::
-
- @contextfunction
- def get_exported_names(context):
- return sorted(context.exported_vars)
- """
- f.contextfunction = True
- return f
-
-
-def evalcontextfunction(f):
- """This decoraotr can be used to mark a function or method as an eval
- context callable. This is similar to the :func:`contextfunction`
- but instead of passing the context, an evaluation context object is
- passed. For more information about the eval context, see
- :ref:`eval-context`.
-
- .. versionadded:: 2.4
- """
- f.evalcontextfunction = True
- return f
-
-
-def environmentfunction(f):
- """This decorator can be used to mark a function or method as environment
- callable. This decorator works exactly like the :func:`contextfunction`
- decorator just that the first argument is the active :class:`Environment`
- and not context.
- """
- f.environmentfunction = True
- return f
-
-
-def internalcode(f):
- """Marks the function as internally used"""
- internal_code.add(f.func_code)
- return f
-
-
-def is_undefined(obj):
- """Check if the object passed is undefined. This does nothing more than
- performing an instance check against :class:`Undefined` but looks nicer.
- This can be used for custom filters or tests that want to react to
- undefined variables. For example a custom default filter can look like
- this::
-
- def default(var, default=''):
- if is_undefined(var):
- return default
- return var
- """
- from jinja2.runtime import Undefined
- return isinstance(obj, Undefined)
-
-
-def consume(iterable):
- """Consumes an iterable without doing anything with it."""
- for event in iterable:
- pass
-
-
-def clear_caches():
- """Jinja2 keeps internal caches for environments and lexers. These are
- used so that Jinja2 doesn't have to recreate environments and lexers all
- the time. Normally you don't have to care about that but if you are
- messuring memory consumption you may want to clean the caches.
- """
- from jinja2.environment import _spontaneous_environments
- from jinja2.lexer import _lexer_cache
- _spontaneous_environments.clear()
- _lexer_cache.clear()
-
-
-def import_string(import_name, silent=False):
- """Imports an object based on a string. This use useful if you want to
- use import paths as endpoints or something similar. An import path can
- be specified either in dotted notation (``xml.sax.saxutils.escape``)
- or with a colon as object delimiter (``xml.sax.saxutils:escape``).
-
- If the `silent` is True the return value will be `None` if the import
- fails.
-
- :return: imported object
- """
- try:
- if ':' in import_name:
- module, obj = import_name.split(':', 1)
- elif '.' in import_name:
- items = import_name.split('.')
- module = '.'.join(items[:-1])
- obj = items[-1]
- else:
- return __import__(import_name)
- return getattr(__import__(module, None, None, [obj]), obj)
- except (ImportError, AttributeError):
- if not silent:
- raise
-
-
-def open_if_exists(filename, mode='rb'):
- """Returns a file descriptor for the filename if that file exists,
- otherwise `None`.
- """
- try:
- return open(filename, mode)
- except IOError, e:
- if e.errno not in (errno.ENOENT, errno.EISDIR):
- raise
-
-
-def object_type_repr(obj):
- """Returns the name of the object's type. For some recognized
- singletons the name of the object is returned instead. (For
- example for `None` and `Ellipsis`).
- """
- if obj is None:
- return 'None'
- elif obj is Ellipsis:
- return 'Ellipsis'
- # __builtin__ in 2.x, builtins in 3.x
- if obj.__class__.__module__ in ('__builtin__', 'builtins'):
- name = obj.__class__.__name__
- else:
- name = obj.__class__.__module__ + '.' + obj.__class__.__name__
- return '%s object' % name
-
-
-def pformat(obj, verbose=False):
- """Prettyprint an object. Either use the `pretty` library or the
- builtin `pprint`.
- """
- try:
- from pretty import pretty
- return pretty(obj, verbose=verbose)
- except ImportError:
- from pprint import pformat
- return pformat(obj)
-
-
-def urlize(text, trim_url_limit=None, nofollow=False):
- """Converts any URLs in text into clickable links. Works on http://,
- https:// and www. links. Links can have trailing punctuation (periods,
- commas, close-parens) and leading punctuation (opening parens) and
- it'll still do the right thing.
-
- If trim_url_limit is not None, the URLs in link text will be limited
- to trim_url_limit characters.
-
- If nofollow is True, the URLs in link text will get a rel="nofollow"
- attribute.
- """
- trim_url = lambda x, limit=trim_url_limit: limit is not None \
- and (x[:limit] + (len(x) >=limit and '...'
- or '')) or x
- words = _word_split_re.split(unicode(escape(text)))
- nofollow_attr = nofollow and ' rel="nofollow"' or ''
- for i, word in enumerate(words):
- match = _punctuation_re.match(word)
- if match:
- lead, middle, trail = match.groups()
- if middle.startswith('www.') or (
- '@' not in middle and
- not middle.startswith('http://') and
- len(middle) > 0 and
- middle[0] in _letters + _digits and (
- middle.endswith('.org') or
- middle.endswith('.net') or
- middle.endswith('.com')
- )):
- middle = '<a href="http://%s"%s>%s</a>' % (middle,
- nofollow_attr, trim_url(middle))
- if middle.startswith('http://') or \
- middle.startswith('https://'):
- middle = '<a href="%s"%s>%s</a>' % (middle,
- nofollow_attr, trim_url(middle))
- if '@' in middle and not middle.startswith('www.') and \
- not ':' in middle and _simple_email_re.match(middle):
- middle = '<a href="mailto:%s">%s</a>' % (middle, middle)
- if lead + middle + trail != word:
- words[i] = lead + middle + trail
- return u''.join(words)
-
-
-def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
- """Generate some lorem impsum for the template."""
- from jinja2.constants import LOREM_IPSUM_WORDS
- from random import choice, randrange
- words = LOREM_IPSUM_WORDS.split()
- result = []
-
- for _ in xrange(n):
- next_capitalized = True
- last_comma = last_fullstop = 0
- word = None
- last = None
- p = []
-
- # each paragraph contains out of 20 to 100 words.
- for idx, _ in enumerate(xrange(randrange(min, max))):
- while True:
- word = choice(words)
- if word != last:
- last = word
- break
- if next_capitalized:
- word = word.capitalize()
- next_capitalized = False
- # add commas
- if idx - randrange(3, 8) > last_comma:
- last_comma = idx
- last_fullstop += 2
- word += ','
- # add end of sentences
- if idx - randrange(10, 20) > last_fullstop:
- last_comma = last_fullstop = idx
- word += '.'
- next_capitalized = True
- p.append(word)
-
- # ensure that the paragraph ends with a dot.
- p = u' '.join(p)
- if p.endswith(','):
- p = p[:-1] + '.'
- elif not p.endswith('.'):
- p += '.'
- result.append(p)
-
- if not html:
- return u'\n\n'.join(result)
- return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
-
-
-class LRUCache(object):
- """A simple LRU Cache implementation."""
-
- # this is fast for small capacities (something below 1000) but doesn't
- # scale. But as long as it's only used as storage for templates this
- # won't do any harm.
-
- def __init__(self, capacity):
- self.capacity = capacity
- self._mapping = {}
- self._queue = deque()
- self._postinit()
-
- def _postinit(self):
- # alias all queue methods for faster lookup
- self._popleft = self._queue.popleft
- self._pop = self._queue.pop
- if hasattr(self._queue, 'remove'):
- self._remove = self._queue.remove
- self._wlock = allocate_lock()
- self._append = self._queue.append
-
- def _remove(self, obj):
- """Python 2.4 compatibility."""
- for idx, item in enumerate(self._queue):
- if item == obj:
- del self._queue[idx]
- break
-
- def __getstate__(self):
- return {
- 'capacity': self.capacity,
- '_mapping': self._mapping,
- '_queue': self._queue
- }
-
- def __setstate__(self, d):
- self.__dict__.update(d)
- self._postinit()
-
- def __getnewargs__(self):
- return (self.capacity,)
-
- def copy(self):
- """Return an shallow copy of the instance."""
- rv = self.__class__(self.capacity)
- rv._mapping.update(self._mapping)
- rv._queue = deque(self._queue)
- return rv
-
- def get(self, key, default=None):
- """Return an item from the cache dict or `default`"""
- try:
- return self[key]
- except KeyError:
- return default
-
- def setdefault(self, key, default=None):
- """Set `default` if the key is not in the cache otherwise
- leave unchanged. Return the value of this key.
- """
- try:
- return self[key]
- except KeyError:
- self[key] = default
- return default
-
- def clear(self):
- """Clear the cache."""
- self._wlock.acquire()
- try:
- self._mapping.clear()
- self._queue.clear()
- finally:
- self._wlock.release()
-
- def __contains__(self, key):
- """Check if a key exists in this cache."""
- return key in self._mapping
-
- def __len__(self):
- """Return the current size of the cache."""
- return len(self._mapping)
-
- def __repr__(self):
- return '<%s %r>' % (
- self.__class__.__name__,
- self._mapping
- )
-
- def __getitem__(self, key):
- """Get an item from the cache. Moves the item up so that it has the
- highest priority then.
-
- Raise an `KeyError` if it does not exist.
- """
- rv = self._mapping[key]
- if self._queue[-1] != key:
- try:
- self._remove(key)
- except ValueError:
- # if something removed the key from the container
- # when we read, ignore the ValueError that we would
- # get otherwise.
- pass
- self._append(key)
- return rv
-
- def __setitem__(self, key, value):
- """Sets the value for an item. Moves the item up so that it
- has the highest priority then.
- """
- self._wlock.acquire()
- try:
- if key in self._mapping:
- try:
- self._remove(key)
- except ValueError:
- # __getitem__ is not locked, it might happen
- pass
- elif len(self._mapping) == self.capacity:
- del self._mapping[self._popleft()]
- self._append(key)
- self._mapping[key] = value
- finally:
- self._wlock.release()
-
- def __delitem__(self, key):
- """Remove an item from the cache dict.
- Raise an `KeyError` if it does not exist.
- """
- self._wlock.acquire()
- try:
- del self._mapping[key]
- try:
- self._remove(key)
- except ValueError:
- # __getitem__ is not locked, it might happen
- pass
- finally:
- self._wlock.release()
-
- def items(self):
- """Return a list of items."""
- result = [(key, self._mapping[key]) for key in list(self._queue)]
- result.reverse()
- return result
-
- def iteritems(self):
- """Iterate over all items."""
- return iter(self.items())
-
- def values(self):
- """Return a list of all values."""
- return [x[1] for x in self.items()]
-
- def itervalue(self):
- """Iterate over all values."""
- return iter(self.values())
-
- def keys(self):
- """Return a list of all keys ordered by most recent usage."""
- return list(self)
-
- def iterkeys(self):
- """Iterate over all keys in the cache dict, ordered by
- the most recent usage.
- """
- return reversed(tuple(self._queue))
-
- __iter__ = iterkeys
-
- def __reversed__(self):
- """Iterate over the values in the cache dict, oldest items
- coming first.
- """
- return iter(tuple(self._queue))
-
- __copy__ = copy
-
-
-# register the LRU cache as mutable mapping if possible
-try:
- from collections import MutableMapping
- MutableMapping.register(LRUCache)
-except ImportError:
- pass
-
-
-class Cycler(object):
- """A cycle helper for templates."""
-
- def __init__(self, *items):
- if not items:
- raise RuntimeError('at least one item has to be provided')
- self.items = items
- self.reset()
-
- def reset(self):
- """Resets the cycle."""
- self.pos = 0
-
- @property
- def current(self):
- """Returns the current item."""
- return self.items[self.pos]
-
- def next(self):
- """Goes one item ahead and returns it."""
- rv = self.current
- self.pos = (self.pos + 1) % len(self.items)
- return rv
-
-
-class Joiner(object):
- """A joining helper for templates."""
-
- def __init__(self, sep=u', '):
- self.sep = sep
- self.used = False
-
- def __call__(self):
- if not self.used:
- self.used = True
- return u''
- return self.sep
-
-
-# try markupsafe first, if that fails go with Jinja2's bundled version
-# of markupsafe. Markupsafe was previously Jinja2's implementation of
-# the Markup object but was moved into a separate package in a patchleve
-# release
-try:
- from markupsafe import Markup, escape, soft_unicode
-except ImportError:
- from jinja2._markupsafe import Markup, escape, soft_unicode
-
-
-# partials
-try:
- from functools import partial
-except ImportError:
- class partial(object):
- def __init__(self, _func, *args, **kwargs):
- self._func = _func
- self._args = args
- self._kwargs = kwargs
- def __call__(self, *args, **kwargs):
- kwargs.update(self._kwargs)
- return self._func(*(self._args + args), **kwargs)
diff --git a/module/lib/jinja2/visitor.py b/module/lib/jinja2/visitor.py
deleted file mode 100644
index 413e7c309..000000000
--- a/module/lib/jinja2/visitor.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
- jinja2.visitor
- ~~~~~~~~~~~~~~
-
- This module implements a visitor for the nodes.
-
- :copyright: (c) 2010 by the Jinja Team.
- :license: BSD.
-"""
-from jinja2.nodes import Node
-
-
-class NodeVisitor(object):
- """Walks the abstract syntax tree and call visitor functions for every
- node found. The visitor functions may return values which will be
- forwarded by the `visit` method.
-
- Per default the visitor functions for the nodes are ``'visit_'`` +
- class name of the node. So a `TryFinally` node visit function would
- be `visit_TryFinally`. This behavior can be changed by overriding
- the `get_visitor` function. If no visitor function exists for a node
- (return value `None`) the `generic_visit` visitor is used instead.
- """
-
- def get_visitor(self, node):
- """Return the visitor function for this node or `None` if no visitor
- exists for this node. In that case the generic visit function is
- used instead.
- """
- method = 'visit_' + node.__class__.__name__
- return getattr(self, method, None)
-
- def visit(self, node, *args, **kwargs):
- """Visit a node."""
- f = self.get_visitor(node)
- if f is not None:
- return f(node, *args, **kwargs)
- return self.generic_visit(node, *args, **kwargs)
-
- def generic_visit(self, node, *args, **kwargs):
- """Called if no explicit visitor function exists for a node."""
- for node in node.iter_child_nodes():
- self.visit(node, *args, **kwargs)
-
-
-class NodeTransformer(NodeVisitor):
- """Walks the abstract syntax tree and allows modifications of nodes.
-
- The `NodeTransformer` will walk the AST and use the return value of the
- visitor functions to replace or remove the old node. If the return
- value of the visitor function is `None` the node will be removed
- from the previous location otherwise it's replaced with the return
- value. The return value may be the original node in which case no
- replacement takes place.
- """
-
- def generic_visit(self, node, *args, **kwargs):
- for field, old_value in node.iter_fields():
- if isinstance(old_value, list):
- new_values = []
- for value in old_value:
- if isinstance(value, Node):
- value = self.visit(value, *args, **kwargs)
- if value is None:
- continue
- elif not isinstance(value, Node):
- new_values.extend(value)
- continue
- new_values.append(value)
- old_value[:] = new_values
- elif isinstance(old_value, Node):
- new_node = self.visit(old_value, *args, **kwargs)
- if new_node is None:
- delattr(node, field)
- else:
- setattr(node, field, new_node)
- return node
-
- def visit_list(self, node, *args, **kwargs):
- """As transformers may return lists in some places this method
- can be used to enforce a list as return value.
- """
- rv = self.visit(node, *args, **kwargs)
- if not isinstance(rv, list):
- rv = [rv]
- return rv
diff --git a/module/lib/rename_process.py b/module/lib/rename_process.py
deleted file mode 100644
index 2527cef39..000000000
--- a/module/lib/rename_process.py
+++ /dev/null
@@ -1,14 +0,0 @@
-import sys
-
-def renameProcess(new_name):
- """ Renames the process calling the function to the given name. """
- if sys.platform != 'linux2':
- return False
- try:
- from ctypes import CDLL
- libc = CDLL('libc.so.6')
- libc.prctl(15, new_name, 0, 0, 0)
- return True
- except Exception, e:
- #print "Rename process failed", e
- return False
diff --git a/module/lib/simplejson/__init__.py b/module/lib/simplejson/__init__.py
deleted file mode 100644
index ef5c0db48..000000000
--- a/module/lib/simplejson/__init__.py
+++ /dev/null
@@ -1,466 +0,0 @@
-r"""JSON (JavaScript Object Notation) <http://json.org> is a subset of
-JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data
-interchange format.
-
-:mod:`simplejson` exposes an API familiar to users of the standard library
-:mod:`marshal` and :mod:`pickle` modules. It is the externally maintained
-version of the :mod:`json` library contained in Python 2.6, but maintains
-compatibility with Python 2.4 and Python 2.5 and (currently) has
-significant performance advantages, even without using the optional C
-extension for speedups.
-
-Encoding basic Python object hierarchies::
-
- >>> import simplejson as json
- >>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
- '["foo", {"bar": ["baz", null, 1.0, 2]}]'
- >>> print json.dumps("\"foo\bar")
- "\"foo\bar"
- >>> print json.dumps(u'\u1234')
- "\u1234"
- >>> print json.dumps('\\')
- "\\"
- >>> print json.dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True)
- {"a": 0, "b": 0, "c": 0}
- >>> from StringIO import StringIO
- >>> io = StringIO()
- >>> json.dump(['streaming API'], io)
- >>> io.getvalue()
- '["streaming API"]'
-
-Compact encoding::
-
- >>> import simplejson as json
- >>> json.dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':'))
- '[1,2,3,{"4":5,"6":7}]'
-
-Pretty printing::
-
- >>> import simplejson as json
- >>> s = json.dumps({'4': 5, '6': 7}, sort_keys=True, indent=' ')
- >>> print '\n'.join([l.rstrip() for l in s.splitlines()])
- {
- "4": 5,
- "6": 7
- }
-
-Decoding JSON::
-
- >>> import simplejson as json
- >>> obj = [u'foo', {u'bar': [u'baz', None, 1.0, 2]}]
- >>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') == obj
- True
- >>> json.loads('"\\"foo\\bar"') == u'"foo\x08ar'
- True
- >>> from StringIO import StringIO
- >>> io = StringIO('["streaming API"]')
- >>> json.load(io)[0] == 'streaming API'
- True
-
-Specializing JSON object decoding::
-
- >>> import simplejson as json
- >>> def as_complex(dct):
- ... if '__complex__' in dct:
- ... return complex(dct['real'], dct['imag'])
- ... return dct
- ...
- >>> json.loads('{"__complex__": true, "real": 1, "imag": 2}',
- ... object_hook=as_complex)
- (1+2j)
- >>> from decimal import Decimal
- >>> json.loads('1.1', parse_float=Decimal) == Decimal('1.1')
- True
-
-Specializing JSON object encoding::
-
- >>> import simplejson as json
- >>> def encode_complex(obj):
- ... if isinstance(obj, complex):
- ... return [obj.real, obj.imag]
- ... raise TypeError(repr(o) + " is not JSON serializable")
- ...
- >>> json.dumps(2 + 1j, default=encode_complex)
- '[2.0, 1.0]'
- >>> json.JSONEncoder(default=encode_complex).encode(2 + 1j)
- '[2.0, 1.0]'
- >>> ''.join(json.JSONEncoder(default=encode_complex).iterencode(2 + 1j))
- '[2.0, 1.0]'
-
-
-Using simplejson.tool from the shell to validate and pretty-print::
-
- $ echo '{"json":"obj"}' | python -m simplejson.tool
- {
- "json": "obj"
- }
- $ echo '{ 1.2:3.4}' | python -m simplejson.tool
- Expecting property name: line 1 column 2 (char 2)
-"""
-__version__ = '2.2.1'
-__all__ = [
- 'dump', 'dumps', 'load', 'loads',
- 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
- 'OrderedDict',
-]
-
-__author__ = 'Bob Ippolito <bob@redivi.com>'
-
-from decimal import Decimal
-
-from decoder import JSONDecoder, JSONDecodeError
-from encoder import JSONEncoder
-def _import_OrderedDict():
- import collections
- try:
- return collections.OrderedDict
- except AttributeError:
- import ordered_dict
- return ordered_dict.OrderedDict
-OrderedDict = _import_OrderedDict()
-
-def _import_c_make_encoder():
- try:
- from simplejson._speedups import make_encoder
- return make_encoder
- except ImportError:
- return None
-
-_default_encoder = JSONEncoder(
- skipkeys=False,
- ensure_ascii=True,
- check_circular=True,
- allow_nan=True,
- indent=None,
- separators=None,
- encoding='utf-8',
- default=None,
- use_decimal=True,
- namedtuple_as_object=True,
- tuple_as_array=True,
-)
-
-def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True,
- allow_nan=True, cls=None, indent=None, separators=None,
- encoding='utf-8', default=None, use_decimal=True,
- namedtuple_as_object=True, tuple_as_array=True,
- **kw):
- """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
- ``.write()``-supporting file-like object).
-
- If ``skipkeys`` is true then ``dict`` keys that are not basic types
- (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
- will be skipped instead of raising a ``TypeError``.
-
- If ``ensure_ascii`` is false, then the some chunks written to ``fp``
- may be ``unicode`` instances, subject to normal Python ``str`` to
- ``unicode`` coercion rules. Unless ``fp.write()`` explicitly
- understands ``unicode`` (as in ``codecs.getwriter()``) this is likely
- to cause an error.
-
- If ``check_circular`` is false, then the circular reference check
- for container types will be skipped and a circular reference will
- result in an ``OverflowError`` (or worse).
-
- If ``allow_nan`` is false, then it will be a ``ValueError`` to
- serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``)
- in strict compliance of the JSON specification, instead of using the
- JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
-
- If *indent* is a string, then JSON array elements and object members
- will be pretty-printed with a newline followed by that string repeated
- for each level of nesting. ``None`` (the default) selects the most compact
- representation without any newlines. For backwards compatibility with
- versions of simplejson earlier than 2.1.0, an integer is also accepted
- and is converted to a string with that many spaces.
-
- If ``separators`` is an ``(item_separator, dict_separator)`` tuple
- then it will be used instead of the default ``(', ', ': ')`` separators.
- ``(',', ':')`` is the most compact JSON representation.
-
- ``encoding`` is the character encoding for str instances, default is UTF-8.
-
- ``default(obj)`` is a function that should return a serializable version
- of obj or raise TypeError. The default simply raises TypeError.
-
- If *use_decimal* is true (default: ``True``) then decimal.Decimal
- will be natively serialized to JSON with full precision.
-
- If *namedtuple_as_object* is true (default: ``True``),
- :class:`tuple` subclasses with ``_asdict()`` methods will be encoded
- as JSON objects.
-
- If *tuple_as_array* is true (default: ``True``),
- :class:`tuple` (and subclasses) will be encoded as JSON arrays.
-
- To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
- ``.default()`` method to serialize additional types), specify it with
- the ``cls`` kwarg.
-
- """
- # cached encoder
- if (not skipkeys and ensure_ascii and
- check_circular and allow_nan and
- cls is None and indent is None and separators is None and
- encoding == 'utf-8' and default is None and use_decimal
- and namedtuple_as_object and tuple_as_array and not kw):
- iterable = _default_encoder.iterencode(obj)
- else:
- if cls is None:
- cls = JSONEncoder
- iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
- check_circular=check_circular, allow_nan=allow_nan, indent=indent,
- separators=separators, encoding=encoding,
- default=default, use_decimal=use_decimal,
- namedtuple_as_object=namedtuple_as_object,
- tuple_as_array=tuple_as_array,
- **kw).iterencode(obj)
- # could accelerate with writelines in some versions of Python, at
- # a debuggability cost
- for chunk in iterable:
- fp.write(chunk)
-
-
-def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True,
- allow_nan=True, cls=None, indent=None, separators=None,
- encoding='utf-8', default=None, use_decimal=True,
- namedtuple_as_object=True,
- tuple_as_array=True,
- **kw):
- """Serialize ``obj`` to a JSON formatted ``str``.
-
- If ``skipkeys`` is false then ``dict`` keys that are not basic types
- (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``)
- will be skipped instead of raising a ``TypeError``.
-
- If ``ensure_ascii`` is false, then the return value will be a
- ``unicode`` instance subject to normal Python ``str`` to ``unicode``
- coercion rules instead of being escaped to an ASCII ``str``.
-
- If ``check_circular`` is false, then the circular reference check
- for container types will be skipped and a circular reference will
- result in an ``OverflowError`` (or worse).
-
- If ``allow_nan`` is false, then it will be a ``ValueError`` to
- serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
- strict compliance of the JSON specification, instead of using the
- JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).
-
- If ``indent`` is a string, then JSON array elements and object members
- will be pretty-printed with a newline followed by that string repeated
- for each level of nesting. ``None`` (the default) selects the most compact
- representation without any newlines. For backwards compatibility with
- versions of simplejson earlier than 2.1.0, an integer is also accepted
- and is converted to a string with that many spaces.
-
- If ``separators`` is an ``(item_separator, dict_separator)`` tuple
- then it will be used instead of the default ``(', ', ': ')`` separators.
- ``(',', ':')`` is the most compact JSON representation.
-
- ``encoding`` is the character encoding for str instances, default is UTF-8.
-
- ``default(obj)`` is a function that should return a serializable version
- of obj or raise TypeError. The default simply raises TypeError.
-
- If *use_decimal* is true (default: ``True``) then decimal.Decimal
- will be natively serialized to JSON with full precision.
-
- If *namedtuple_as_object* is true (default: ``True``),
- :class:`tuple` subclasses with ``_asdict()`` methods will be encoded
- as JSON objects.
-
- If *tuple_as_array* is true (default: ``True``),
- :class:`tuple` (and subclasses) will be encoded as JSON arrays.
-
- To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
- ``.default()`` method to serialize additional types), specify it with
- the ``cls`` kwarg.
-
- """
- # cached encoder
- if (not skipkeys and ensure_ascii and
- check_circular and allow_nan and
- cls is None and indent is None and separators is None and
- encoding == 'utf-8' and default is None and use_decimal
- and namedtuple_as_object and tuple_as_array and not kw):
- return _default_encoder.encode(obj)
- if cls is None:
- cls = JSONEncoder
- return cls(
- skipkeys=skipkeys, ensure_ascii=ensure_ascii,
- check_circular=check_circular, allow_nan=allow_nan, indent=indent,
- separators=separators, encoding=encoding, default=default,
- use_decimal=use_decimal,
- namedtuple_as_object=namedtuple_as_object,
- tuple_as_array=tuple_as_array,
- **kw).encode(obj)
-
-
-_default_decoder = JSONDecoder(encoding=None, object_hook=None,
- object_pairs_hook=None)
-
-
-def load(fp, encoding=None, cls=None, object_hook=None, parse_float=None,
- parse_int=None, parse_constant=None, object_pairs_hook=None,
- use_decimal=False, namedtuple_as_object=True, tuple_as_array=True,
- **kw):
- """Deserialize ``fp`` (a ``.read()``-supporting file-like object containing
- a JSON document) to a Python object.
-
- *encoding* determines the encoding used to interpret any
- :class:`str` objects decoded by this instance (``'utf-8'`` by
- default). It has no effect when decoding :class:`unicode` objects.
-
- Note that currently only encodings that are a superset of ASCII work,
- strings of other encodings should be passed in as :class:`unicode`.
-
- *object_hook*, if specified, will be called with the result of every
- JSON object decoded and its return value will be used in place of the
- given :class:`dict`. This can be used to provide custom
- deserializations (e.g. to support JSON-RPC class hinting).
-
- *object_pairs_hook* is an optional function that will be called with
- the result of any object literal decode with an ordered list of pairs.
- The return value of *object_pairs_hook* will be used instead of the
- :class:`dict`. This feature can be used to implement custom decoders
- that rely on the order that the key and value pairs are decoded (for
- example, :func:`collections.OrderedDict` will remember the order of
- insertion). If *object_hook* is also defined, the *object_pairs_hook*
- takes priority.
-
- *parse_float*, if specified, will be called with the string of every
- JSON float to be decoded. By default, this is equivalent to
- ``float(num_str)``. This can be used to use another datatype or parser
- for JSON floats (e.g. :class:`decimal.Decimal`).
-
- *parse_int*, if specified, will be called with the string of every
- JSON int to be decoded. By default, this is equivalent to
- ``int(num_str)``. This can be used to use another datatype or parser
- for JSON integers (e.g. :class:`float`).
-
- *parse_constant*, if specified, will be called with one of the
- following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This
- can be used to raise an exception if invalid JSON numbers are
- encountered.
-
- If *use_decimal* is true (default: ``False``) then it implies
- parse_float=decimal.Decimal for parity with ``dump``.
-
- To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
- kwarg.
-
- """
- return loads(fp.read(),
- encoding=encoding, cls=cls, object_hook=object_hook,
- parse_float=parse_float, parse_int=parse_int,
- parse_constant=parse_constant, object_pairs_hook=object_pairs_hook,
- use_decimal=use_decimal, **kw)
-
-
-def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
- parse_int=None, parse_constant=None, object_pairs_hook=None,
- use_decimal=False, **kw):
- """Deserialize ``s`` (a ``str`` or ``unicode`` instance containing a JSON
- document) to a Python object.
-
- *encoding* determines the encoding used to interpret any
- :class:`str` objects decoded by this instance (``'utf-8'`` by
- default). It has no effect when decoding :class:`unicode` objects.
-
- Note that currently only encodings that are a superset of ASCII work,
- strings of other encodings should be passed in as :class:`unicode`.
-
- *object_hook*, if specified, will be called with the result of every
- JSON object decoded and its return value will be used in place of the
- given :class:`dict`. This can be used to provide custom
- deserializations (e.g. to support JSON-RPC class hinting).
-
- *object_pairs_hook* is an optional function that will be called with
- the result of any object literal decode with an ordered list of pairs.
- The return value of *object_pairs_hook* will be used instead of the
- :class:`dict`. This feature can be used to implement custom decoders
- that rely on the order that the key and value pairs are decoded (for
- example, :func:`collections.OrderedDict` will remember the order of
- insertion). If *object_hook* is also defined, the *object_pairs_hook*
- takes priority.
-
- *parse_float*, if specified, will be called with the string of every
- JSON float to be decoded. By default, this is equivalent to
- ``float(num_str)``. This can be used to use another datatype or parser
- for JSON floats (e.g. :class:`decimal.Decimal`).
-
- *parse_int*, if specified, will be called with the string of every
- JSON int to be decoded. By default, this is equivalent to
- ``int(num_str)``. This can be used to use another datatype or parser
- for JSON integers (e.g. :class:`float`).
-
- *parse_constant*, if specified, will be called with one of the
- following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This
- can be used to raise an exception if invalid JSON numbers are
- encountered.
-
- If *use_decimal* is true (default: ``False``) then it implies
- parse_float=decimal.Decimal for parity with ``dump``.
-
- To use a custom ``JSONDecoder`` subclass, specify it with the ``cls``
- kwarg.
-
- """
- if (cls is None and encoding is None and object_hook is None and
- parse_int is None and parse_float is None and
- parse_constant is None and object_pairs_hook is None
- and not use_decimal and not kw):
- return _default_decoder.decode(s)
- if cls is None:
- cls = JSONDecoder
- if object_hook is not None:
- kw['object_hook'] = object_hook
- if object_pairs_hook is not None:
- kw['object_pairs_hook'] = object_pairs_hook
- if parse_float is not None:
- kw['parse_float'] = parse_float
- if parse_int is not None:
- kw['parse_int'] = parse_int
- if parse_constant is not None:
- kw['parse_constant'] = parse_constant
- if use_decimal:
- if parse_float is not None:
- raise TypeError("use_decimal=True implies parse_float=Decimal")
- kw['parse_float'] = Decimal
- return cls(encoding=encoding, **kw).decode(s)
-
-
-def _toggle_speedups(enabled):
- import simplejson.decoder as dec
- import simplejson.encoder as enc
- import simplejson.scanner as scan
- c_make_encoder = _import_c_make_encoder()
- if enabled:
- dec.scanstring = dec.c_scanstring or dec.py_scanstring
- enc.c_make_encoder = c_make_encoder
- enc.encode_basestring_ascii = (enc.c_encode_basestring_ascii or
- enc.py_encode_basestring_ascii)
- scan.make_scanner = scan.c_make_scanner or scan.py_make_scanner
- else:
- dec.scanstring = dec.py_scanstring
- enc.c_make_encoder = None
- enc.encode_basestring_ascii = enc.py_encode_basestring_ascii
- scan.make_scanner = scan.py_make_scanner
- dec.make_scanner = scan.make_scanner
- global _default_decoder
- _default_decoder = JSONDecoder(
- encoding=None,
- object_hook=None,
- object_pairs_hook=None,
- )
- global _default_encoder
- _default_encoder = JSONEncoder(
- skipkeys=False,
- ensure_ascii=True,
- check_circular=True,
- allow_nan=True,
- indent=None,
- separators=None,
- encoding='utf-8',
- default=None,
- )
diff --git a/module/lib/simplejson/decoder.py b/module/lib/simplejson/decoder.py
deleted file mode 100644
index e5496d6e7..000000000
--- a/module/lib/simplejson/decoder.py
+++ /dev/null
@@ -1,421 +0,0 @@
-"""Implementation of JSONDecoder
-"""
-import re
-import sys
-import struct
-
-from simplejson.scanner import make_scanner
-def _import_c_scanstring():
- try:
- from simplejson._speedups import scanstring
- return scanstring
- except ImportError:
- return None
-c_scanstring = _import_c_scanstring()
-
-__all__ = ['JSONDecoder']
-
-FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
-
-def _floatconstants():
- _BYTES = '7FF80000000000007FF0000000000000'.decode('hex')
- # The struct module in Python 2.4 would get frexp() out of range here
- # when an endian is specified in the format string. Fixed in Python 2.5+
- if sys.byteorder != 'big':
- _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1]
- nan, inf = struct.unpack('dd', _BYTES)
- return nan, inf, -inf
-
-NaN, PosInf, NegInf = _floatconstants()
-
-
-class JSONDecodeError(ValueError):
- """Subclass of ValueError with the following additional properties:
-
- msg: The unformatted error message
- doc: The JSON document being parsed
- pos: The start index of doc where parsing failed
- end: The end index of doc where parsing failed (may be None)
- lineno: The line corresponding to pos
- colno: The column corresponding to pos
- endlineno: The line corresponding to end (may be None)
- endcolno: The column corresponding to end (may be None)
-
- """
- def __init__(self, msg, doc, pos, end=None):
- ValueError.__init__(self, errmsg(msg, doc, pos, end=end))
- self.msg = msg
- self.doc = doc
- self.pos = pos
- self.end = end
- self.lineno, self.colno = linecol(doc, pos)
- if end is not None:
- self.endlineno, self.endcolno = linecol(doc, end)
- else:
- self.endlineno, self.endcolno = None, None
-
-
-def linecol(doc, pos):
- lineno = doc.count('\n', 0, pos) + 1
- if lineno == 1:
- colno = pos
- else:
- colno = pos - doc.rindex('\n', 0, pos)
- return lineno, colno
-
-
-def errmsg(msg, doc, pos, end=None):
- # Note that this function is called from _speedups
- lineno, colno = linecol(doc, pos)
- if end is None:
- #fmt = '{0}: line {1} column {2} (char {3})'
- #return fmt.format(msg, lineno, colno, pos)
- fmt = '%s: line %d column %d (char %d)'
- return fmt % (msg, lineno, colno, pos)
- endlineno, endcolno = linecol(doc, end)
- #fmt = '{0}: line {1} column {2} - line {3} column {4} (char {5} - {6})'
- #return fmt.format(msg, lineno, colno, endlineno, endcolno, pos, end)
- fmt = '%s: line %d column %d - line %d column %d (char %d - %d)'
- return fmt % (msg, lineno, colno, endlineno, endcolno, pos, end)
-
-
-_CONSTANTS = {
- '-Infinity': NegInf,
- 'Infinity': PosInf,
- 'NaN': NaN,
-}
-
-STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
-BACKSLASH = {
- '"': u'"', '\\': u'\\', '/': u'/',
- 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t',
-}
-
-DEFAULT_ENCODING = "utf-8"
-
-def py_scanstring(s, end, encoding=None, strict=True,
- _b=BACKSLASH, _m=STRINGCHUNK.match):
- """Scan the string s for a JSON string. End is the index of the
- character in s after the quote that started the JSON string.
- Unescapes all valid JSON string escape sequences and raises ValueError
- on attempt to decode an invalid string. If strict is False then literal
- control characters are allowed in the string.
-
- Returns a tuple of the decoded string and the index of the character in s
- after the end quote."""
- if encoding is None:
- encoding = DEFAULT_ENCODING
- chunks = []
- _append = chunks.append
- begin = end - 1
- while 1:
- chunk = _m(s, end)
- if chunk is None:
- raise JSONDecodeError(
- "Unterminated string starting at", s, begin)
- end = chunk.end()
- content, terminator = chunk.groups()
- # Content is contains zero or more unescaped string characters
- if content:
- if not isinstance(content, unicode):
- content = unicode(content, encoding)
- _append(content)
- # Terminator is the end of string, a literal control character,
- # or a backslash denoting that an escape sequence follows
- if terminator == '"':
- break
- elif terminator != '\\':
- if strict:
- msg = "Invalid control character %r at" % (terminator,)
- #msg = "Invalid control character {0!r} at".format(terminator)
- raise JSONDecodeError(msg, s, end)
- else:
- _append(terminator)
- continue
- try:
- esc = s[end]
- except IndexError:
- raise JSONDecodeError(
- "Unterminated string starting at", s, begin)
- # If not a unicode escape sequence, must be in the lookup table
- if esc != 'u':
- try:
- char = _b[esc]
- except KeyError:
- msg = "Invalid \\escape: " + repr(esc)
- raise JSONDecodeError(msg, s, end)
- end += 1
- else:
- # Unicode escape sequence
- esc = s[end + 1:end + 5]
- next_end = end + 5
- if len(esc) != 4:
- msg = "Invalid \\uXXXX escape"
- raise JSONDecodeError(msg, s, end)
- uni = int(esc, 16)
- # Check for surrogate pair on UCS-4 systems
- if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535:
- msg = "Invalid \\uXXXX\\uXXXX surrogate pair"
- if not s[end + 5:end + 7] == '\\u':
- raise JSONDecodeError(msg, s, end)
- esc2 = s[end + 7:end + 11]
- if len(esc2) != 4:
- raise JSONDecodeError(msg, s, end)
- uni2 = int(esc2, 16)
- uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00))
- next_end += 6
- char = unichr(uni)
- end = next_end
- # Append the unescaped character
- _append(char)
- return u''.join(chunks), end
-
-
-# Use speedup if available
-scanstring = c_scanstring or py_scanstring
-
-WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)
-WHITESPACE_STR = ' \t\n\r'
-
-def JSONObject((s, end), encoding, strict, scan_once, object_hook,
- object_pairs_hook, memo=None,
- _w=WHITESPACE.match, _ws=WHITESPACE_STR):
- # Backwards compatibility
- if memo is None:
- memo = {}
- memo_get = memo.setdefault
- pairs = []
- # Use a slice to prevent IndexError from being raised, the following
- # check will raise a more specific ValueError if the string is empty
- nextchar = s[end:end + 1]
- # Normally we expect nextchar == '"'
- if nextchar != '"':
- if nextchar in _ws:
- end = _w(s, end).end()
- nextchar = s[end:end + 1]
- # Trivial empty object
- if nextchar == '}':
- if object_pairs_hook is not None:
- result = object_pairs_hook(pairs)
- return result, end + 1
- pairs = {}
- if object_hook is not None:
- pairs = object_hook(pairs)
- return pairs, end + 1
- elif nextchar != '"':
- raise JSONDecodeError("Expecting property name", s, end)
- end += 1
- while True:
- key, end = scanstring(s, end, encoding, strict)
- key = memo_get(key, key)
-
- # To skip some function call overhead we optimize the fast paths where
- # the JSON key separator is ": " or just ":".
- if s[end:end + 1] != ':':
- end = _w(s, end).end()
- if s[end:end + 1] != ':':
- raise JSONDecodeError("Expecting : delimiter", s, end)
-
- end += 1
-
- try:
- if s[end] in _ws:
- end += 1
- if s[end] in _ws:
- end = _w(s, end + 1).end()
- except IndexError:
- pass
-
- try:
- value, end = scan_once(s, end)
- except StopIteration:
- raise JSONDecodeError("Expecting object", s, end)
- pairs.append((key, value))
-
- try:
- nextchar = s[end]
- if nextchar in _ws:
- end = _w(s, end + 1).end()
- nextchar = s[end]
- except IndexError:
- nextchar = ''
- end += 1
-
- if nextchar == '}':
- break
- elif nextchar != ',':
- raise JSONDecodeError("Expecting , delimiter", s, end - 1)
-
- try:
- nextchar = s[end]
- if nextchar in _ws:
- end += 1
- nextchar = s[end]
- if nextchar in _ws:
- end = _w(s, end + 1).end()
- nextchar = s[end]
- except IndexError:
- nextchar = ''
-
- end += 1
- if nextchar != '"':
- raise JSONDecodeError("Expecting property name", s, end - 1)
-
- if object_pairs_hook is not None:
- result = object_pairs_hook(pairs)
- return result, end
- pairs = dict(pairs)
- if object_hook is not None:
- pairs = object_hook(pairs)
- return pairs, end
-
-def JSONArray((s, end), scan_once, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
- values = []
- nextchar = s[end:end + 1]
- if nextchar in _ws:
- end = _w(s, end + 1).end()
- nextchar = s[end:end + 1]
- # Look-ahead for trivial empty array
- if nextchar == ']':
- return values, end + 1
- _append = values.append
- while True:
- try:
- value, end = scan_once(s, end)
- except StopIteration:
- raise JSONDecodeError("Expecting object", s, end)
- _append(value)
- nextchar = s[end:end + 1]
- if nextchar in _ws:
- end = _w(s, end + 1).end()
- nextchar = s[end:end + 1]
- end += 1
- if nextchar == ']':
- break
- elif nextchar != ',':
- raise JSONDecodeError("Expecting , delimiter", s, end)
-
- try:
- if s[end] in _ws:
- end += 1
- if s[end] in _ws:
- end = _w(s, end + 1).end()
- except IndexError:
- pass
-
- return values, end
-
-class JSONDecoder(object):
- """Simple JSON <http://json.org> decoder
-
- Performs the following translations in decoding by default:
-
- +---------------+-------------------+
- | JSON | Python |
- +===============+===================+
- | object | dict |
- +---------------+-------------------+
- | array | list |
- +---------------+-------------------+
- | string | unicode |
- +---------------+-------------------+
- | number (int) | int, long |
- +---------------+-------------------+
- | number (real) | float |
- +---------------+-------------------+
- | true | True |
- +---------------+-------------------+
- | false | False |
- +---------------+-------------------+
- | null | None |
- +---------------+-------------------+
-
- It also understands ``NaN``, ``Infinity``, and ``-Infinity`` as
- their corresponding ``float`` values, which is outside the JSON spec.
-
- """
-
- def __init__(self, encoding=None, object_hook=None, parse_float=None,
- parse_int=None, parse_constant=None, strict=True,
- object_pairs_hook=None):
- """
- *encoding* determines the encoding used to interpret any
- :class:`str` objects decoded by this instance (``'utf-8'`` by
- default). It has no effect when decoding :class:`unicode` objects.
-
- Note that currently only encodings that are a superset of ASCII work,
- strings of other encodings should be passed in as :class:`unicode`.
-
- *object_hook*, if specified, will be called with the result of every
- JSON object decoded and its return value will be used in place of the
- given :class:`dict`. This can be used to provide custom
- deserializations (e.g. to support JSON-RPC class hinting).
-
- *object_pairs_hook* is an optional function that will be called with
- the result of any object literal decode with an ordered list of pairs.
- The return value of *object_pairs_hook* will be used instead of the
- :class:`dict`. This feature can be used to implement custom decoders
- that rely on the order that the key and value pairs are decoded (for
- example, :func:`collections.OrderedDict` will remember the order of
- insertion). If *object_hook* is also defined, the *object_pairs_hook*
- takes priority.
-
- *parse_float*, if specified, will be called with the string of every
- JSON float to be decoded. By default, this is equivalent to
- ``float(num_str)``. This can be used to use another datatype or parser
- for JSON floats (e.g. :class:`decimal.Decimal`).
-
- *parse_int*, if specified, will be called with the string of every
- JSON int to be decoded. By default, this is equivalent to
- ``int(num_str)``. This can be used to use another datatype or parser
- for JSON integers (e.g. :class:`float`).
-
- *parse_constant*, if specified, will be called with one of the
- following strings: ``'-Infinity'``, ``'Infinity'``, ``'NaN'``. This
- can be used to raise an exception if invalid JSON numbers are
- encountered.
-
- *strict* controls the parser's behavior when it encounters an
- invalid control character in a string. The default setting of
- ``True`` means that unescaped control characters are parse errors, if
- ``False`` then control characters will be allowed in strings.
-
- """
- self.encoding = encoding
- self.object_hook = object_hook
- self.object_pairs_hook = object_pairs_hook
- self.parse_float = parse_float or float
- self.parse_int = parse_int or int
- self.parse_constant = parse_constant or _CONSTANTS.__getitem__
- self.strict = strict
- self.parse_object = JSONObject
- self.parse_array = JSONArray
- self.parse_string = scanstring
- self.memo = {}
- self.scan_once = make_scanner(self)
-
- def decode(self, s, _w=WHITESPACE.match):
- """Return the Python representation of ``s`` (a ``str`` or ``unicode``
- instance containing a JSON document)
-
- """
- obj, end = self.raw_decode(s, idx=_w(s, 0).end())
- end = _w(s, end).end()
- if end != len(s):
- raise JSONDecodeError("Extra data", s, end, len(s))
- return obj
-
- def raw_decode(self, s, idx=0):
- """Decode a JSON document from ``s`` (a ``str`` or ``unicode``
- beginning with a JSON document) and return a 2-tuple of the Python
- representation and the index in ``s`` where the document ended.
-
- This can be used to decode a JSON document from a string that may
- have extraneous data at the end.
-
- """
- try:
- obj, end = self.scan_once(s, idx)
- except StopIteration:
- raise JSONDecodeError("No JSON object could be decoded", s, idx)
- return obj, end
diff --git a/module/lib/simplejson/encoder.py b/module/lib/simplejson/encoder.py
deleted file mode 100644
index 5ec7440f1..000000000
--- a/module/lib/simplejson/encoder.py
+++ /dev/null
@@ -1,534 +0,0 @@
-"""Implementation of JSONEncoder
-"""
-import re
-from decimal import Decimal
-
-def _import_speedups():
- try:
- from simplejson import _speedups
- return _speedups.encode_basestring_ascii, _speedups.make_encoder
- except ImportError:
- return None, None
-c_encode_basestring_ascii, c_make_encoder = _import_speedups()
-
-from simplejson.decoder import PosInf
-
-ESCAPE = re.compile(ur'[\x00-\x1f\\"\b\f\n\r\t\u2028\u2029]')
-ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])')
-HAS_UTF8 = re.compile(r'[\x80-\xff]')
-ESCAPE_DCT = {
- '\\': '\\\\',
- '"': '\\"',
- '\b': '\\b',
- '\f': '\\f',
- '\n': '\\n',
- '\r': '\\r',
- '\t': '\\t',
- u'\u2028': '\\u2028',
- u'\u2029': '\\u2029',
-}
-for i in range(0x20):
- #ESCAPE_DCT.setdefault(chr(i), '\\u{0:04x}'.format(i))
- ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,))
-
-FLOAT_REPR = repr
-
-def encode_basestring(s):
- """Return a JSON representation of a Python string
-
- """
- if isinstance(s, str) and HAS_UTF8.search(s) is not None:
- s = s.decode('utf-8')
- def replace(match):
- return ESCAPE_DCT[match.group(0)]
- return u'"' + ESCAPE.sub(replace, s) + u'"'
-
-
-def py_encode_basestring_ascii(s):
- """Return an ASCII-only JSON representation of a Python string
-
- """
- if isinstance(s, str) and HAS_UTF8.search(s) is not None:
- s = s.decode('utf-8')
- def replace(match):
- s = match.group(0)
- try:
- return ESCAPE_DCT[s]
- except KeyError:
- n = ord(s)
- if n < 0x10000:
- #return '\\u{0:04x}'.format(n)
- return '\\u%04x' % (n,)
- else:
- # surrogate pair
- n -= 0x10000
- s1 = 0xd800 | ((n >> 10) & 0x3ff)
- s2 = 0xdc00 | (n & 0x3ff)
- #return '\\u{0:04x}\\u{1:04x}'.format(s1, s2)
- return '\\u%04x\\u%04x' % (s1, s2)
- return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"'
-
-
-encode_basestring_ascii = (
- c_encode_basestring_ascii or py_encode_basestring_ascii)
-
-class JSONEncoder(object):
- """Extensible JSON <http://json.org> encoder for Python data structures.
-
- Supports the following objects and types by default:
-
- +-------------------+---------------+
- | Python | JSON |
- +===================+===============+
- | dict, namedtuple | object |
- +-------------------+---------------+
- | list, tuple | array |
- +-------------------+---------------+
- | str, unicode | string |
- +-------------------+---------------+
- | int, long, float | number |
- +-------------------+---------------+
- | True | true |
- +-------------------+---------------+
- | False | false |
- +-------------------+---------------+
- | None | null |
- +-------------------+---------------+
-
- To extend this to recognize other objects, subclass and implement a
- ``.default()`` method with another method that returns a serializable
- object for ``o`` if possible, otherwise it should call the superclass
- implementation (to raise ``TypeError``).
-
- """
- item_separator = ', '
- key_separator = ': '
- def __init__(self, skipkeys=False, ensure_ascii=True,
- check_circular=True, allow_nan=True, sort_keys=False,
- indent=None, separators=None, encoding='utf-8', default=None,
- use_decimal=True, namedtuple_as_object=True,
- tuple_as_array=True):
- """Constructor for JSONEncoder, with sensible defaults.
-
- If skipkeys is false, then it is a TypeError to attempt
- encoding of keys that are not str, int, long, float or None. If
- skipkeys is True, such items are simply skipped.
-
- If ensure_ascii is true, the output is guaranteed to be str
- objects with all incoming unicode characters escaped. If
- ensure_ascii is false, the output will be unicode object.
-
- If check_circular is true, then lists, dicts, and custom encoded
- objects will be checked for circular references during encoding to
- prevent an infinite recursion (which would cause an OverflowError).
- Otherwise, no such check takes place.
-
- If allow_nan is true, then NaN, Infinity, and -Infinity will be
- encoded as such. This behavior is not JSON specification compliant,
- but is consistent with most JavaScript based encoders and decoders.
- Otherwise, it will be a ValueError to encode such floats.
-
- If sort_keys is true, then the output of dictionaries will be
- sorted by key; this is useful for regression tests to ensure
- that JSON serializations can be compared on a day-to-day basis.
-
- If indent is a string, then JSON array elements and object members
- will be pretty-printed with a newline followed by that string repeated
- for each level of nesting. ``None`` (the default) selects the most compact
- representation without any newlines. For backwards compatibility with
- versions of simplejson earlier than 2.1.0, an integer is also accepted
- and is converted to a string with that many spaces.
-
- If specified, separators should be a (item_separator, key_separator)
- tuple. The default is (', ', ': '). To get the most compact JSON
- representation you should specify (',', ':') to eliminate whitespace.
-
- If specified, default is a function that gets called for objects
- that can't otherwise be serialized. It should return a JSON encodable
- version of the object or raise a ``TypeError``.
-
- If encoding is not None, then all input strings will be
- transformed into unicode using that encoding prior to JSON-encoding.
- The default is UTF-8.
-
- If use_decimal is true (not the default), ``decimal.Decimal`` will
- be supported directly by the encoder. For the inverse, decode JSON
- with ``parse_float=decimal.Decimal``.
-
- If namedtuple_as_object is true (the default), tuple subclasses with
- ``_asdict()`` methods will be encoded as JSON objects.
-
- If tuple_as_array is true (the default), tuple (and subclasses) will
- be encoded as JSON arrays.
- """
-
- self.skipkeys = skipkeys
- self.ensure_ascii = ensure_ascii
- self.check_circular = check_circular
- self.allow_nan = allow_nan
- self.sort_keys = sort_keys
- self.use_decimal = use_decimal
- self.namedtuple_as_object = namedtuple_as_object
- self.tuple_as_array = tuple_as_array
- if isinstance(indent, (int, long)):
- indent = ' ' * indent
- self.indent = indent
- if separators is not None:
- self.item_separator, self.key_separator = separators
- elif indent is not None:
- self.item_separator = ','
- if default is not None:
- self.default = default
- self.encoding = encoding
-
- def default(self, o):
- """Implement this method in a subclass such that it returns
- a serializable object for ``o``, or calls the base implementation
- (to raise a ``TypeError``).
-
- For example, to support arbitrary iterators, you could
- implement default like this::
-
- def default(self, o):
- try:
- iterable = iter(o)
- except TypeError:
- pass
- else:
- return list(iterable)
- return JSONEncoder.default(self, o)
-
- """
- raise TypeError(repr(o) + " is not JSON serializable")
-
- def encode(self, o):
- """Return a JSON string representation of a Python data structure.
-
- >>> from simplejson import JSONEncoder
- >>> JSONEncoder().encode({"foo": ["bar", "baz"]})
- '{"foo": ["bar", "baz"]}'
-
- """
- # This is for extremely simple cases and benchmarks.
- if isinstance(o, basestring):
- if isinstance(o, str):
- _encoding = self.encoding
- if (_encoding is not None
- and not (_encoding == 'utf-8')):
- o = o.decode(_encoding)
- if self.ensure_ascii:
- return encode_basestring_ascii(o)
- else:
- return encode_basestring(o)
- # This doesn't pass the iterator directly to ''.join() because the
- # exceptions aren't as detailed. The list call should be roughly
- # equivalent to the PySequence_Fast that ''.join() would do.
- chunks = self.iterencode(o, _one_shot=True)
- if not isinstance(chunks, (list, tuple)):
- chunks = list(chunks)
- if self.ensure_ascii:
- return ''.join(chunks)
- else:
- return u''.join(chunks)
-
- def iterencode(self, o, _one_shot=False):
- """Encode the given object and yield each string
- representation as available.
-
- For example::
-
- for chunk in JSONEncoder().iterencode(bigobject):
- mysocket.write(chunk)
-
- """
- if self.check_circular:
- markers = {}
- else:
- markers = None
- if self.ensure_ascii:
- _encoder = encode_basestring_ascii
- else:
- _encoder = encode_basestring
- if self.encoding != 'utf-8':
- def _encoder(o, _orig_encoder=_encoder, _encoding=self.encoding):
- if isinstance(o, str):
- o = o.decode(_encoding)
- return _orig_encoder(o)
-
- def floatstr(o, allow_nan=self.allow_nan,
- _repr=FLOAT_REPR, _inf=PosInf, _neginf=-PosInf):
- # Check for specials. Note that this type of test is processor
- # and/or platform-specific, so do tests which don't depend on
- # the internals.
-
- if o != o:
- text = 'NaN'
- elif o == _inf:
- text = 'Infinity'
- elif o == _neginf:
- text = '-Infinity'
- else:
- return _repr(o)
-
- if not allow_nan:
- raise ValueError(
- "Out of range float values are not JSON compliant: " +
- repr(o))
-
- return text
-
-
- key_memo = {}
- if (_one_shot and c_make_encoder is not None
- and self.indent is None):
- _iterencode = c_make_encoder(
- markers, self.default, _encoder, self.indent,
- self.key_separator, self.item_separator, self.sort_keys,
- self.skipkeys, self.allow_nan, key_memo, self.use_decimal,
- self.namedtuple_as_object, self.tuple_as_array)
- else:
- _iterencode = _make_iterencode(
- markers, self.default, _encoder, self.indent, floatstr,
- self.key_separator, self.item_separator, self.sort_keys,
- self.skipkeys, _one_shot, self.use_decimal,
- self.namedtuple_as_object, self.tuple_as_array)
- try:
- return _iterencode(o, 0)
- finally:
- key_memo.clear()
-
-
-class JSONEncoderForHTML(JSONEncoder):
- """An encoder that produces JSON safe to embed in HTML.
-
- To embed JSON content in, say, a script tag on a web page, the
- characters &, < and > should be escaped. They cannot be escaped
- with the usual entities (e.g. &amp;) because they are not expanded
- within <script> tags.
- """
-
- def encode(self, o):
- # Override JSONEncoder.encode because it has hacks for
- # performance that make things more complicated.
- chunks = self.iterencode(o, True)
- if self.ensure_ascii:
- return ''.join(chunks)
- else:
- return u''.join(chunks)
-
- def iterencode(self, o, _one_shot=False):
- chunks = super(JSONEncoderForHTML, self).iterencode(o, _one_shot)
- for chunk in chunks:
- chunk = chunk.replace('&', '\\u0026')
- chunk = chunk.replace('<', '\\u003c')
- chunk = chunk.replace('>', '\\u003e')
- yield chunk
-
-
-def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
- _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
- _use_decimal, _namedtuple_as_object, _tuple_as_array,
- ## HACK: hand-optimized bytecode; turn globals into locals
- False=False,
- True=True,
- ValueError=ValueError,
- basestring=basestring,
- Decimal=Decimal,
- dict=dict,
- float=float,
- id=id,
- int=int,
- isinstance=isinstance,
- list=list,
- long=long,
- str=str,
- tuple=tuple,
- ):
-
- def _iterencode_list(lst, _current_indent_level):
- if not lst:
- yield '[]'
- return
- if markers is not None:
- markerid = id(lst)
- if markerid in markers:
- raise ValueError("Circular reference detected")
- markers[markerid] = lst
- buf = '['
- if _indent is not None:
- _current_indent_level += 1
- newline_indent = '\n' + (_indent * _current_indent_level)
- separator = _item_separator + newline_indent
- buf += newline_indent
- else:
- newline_indent = None
- separator = _item_separator
- first = True
- for value in lst:
- if first:
- first = False
- else:
- buf = separator
- if isinstance(value, basestring):
- yield buf + _encoder(value)
- elif value is None:
- yield buf + 'null'
- elif value is True:
- yield buf + 'true'
- elif value is False:
- yield buf + 'false'
- elif isinstance(value, (int, long)):
- yield buf + str(value)
- elif isinstance(value, float):
- yield buf + _floatstr(value)
- elif _use_decimal and isinstance(value, Decimal):
- yield buf + str(value)
- else:
- yield buf
- if isinstance(value, list):
- chunks = _iterencode_list(value, _current_indent_level)
- elif (_namedtuple_as_object and isinstance(value, tuple) and
- hasattr(value, '_asdict')):
- chunks = _iterencode_dict(value._asdict(),
- _current_indent_level)
- elif _tuple_as_array and isinstance(value, tuple):
- chunks = _iterencode_list(value, _current_indent_level)
- elif isinstance(value, dict):
- chunks = _iterencode_dict(value, _current_indent_level)
- else:
- chunks = _iterencode(value, _current_indent_level)
- for chunk in chunks:
- yield chunk
- if newline_indent is not None:
- _current_indent_level -= 1
- yield '\n' + (_indent * _current_indent_level)
- yield ']'
- if markers is not None:
- del markers[markerid]
-
- def _iterencode_dict(dct, _current_indent_level):
- if not dct:
- yield '{}'
- return
- if markers is not None:
- markerid = id(dct)
- if markerid in markers:
- raise ValueError("Circular reference detected")
- markers[markerid] = dct
- yield '{'
- if _indent is not None:
- _current_indent_level += 1
- newline_indent = '\n' + (_indent * _current_indent_level)
- item_separator = _item_separator + newline_indent
- yield newline_indent
- else:
- newline_indent = None
- item_separator = _item_separator
- first = True
- if _sort_keys:
- items = dct.items()
- items.sort(key=lambda kv: kv[0])
- else:
- items = dct.iteritems()
- for key, value in items:
- if isinstance(key, basestring):
- pass
- # JavaScript is weakly typed for these, so it makes sense to
- # also allow them. Many encoders seem to do something like this.
- elif isinstance(key, float):
- key = _floatstr(key)
- elif key is True:
- key = 'true'
- elif key is False:
- key = 'false'
- elif key is None:
- key = 'null'
- elif isinstance(key, (int, long)):
- key = str(key)
- elif _skipkeys:
- continue
- else:
- raise TypeError("key " + repr(key) + " is not a string")
- if first:
- first = False
- else:
- yield item_separator
- yield _encoder(key)
- yield _key_separator
- if isinstance(value, basestring):
- yield _encoder(value)
- elif value is None:
- yield 'null'
- elif value is True:
- yield 'true'
- elif value is False:
- yield 'false'
- elif isinstance(value, (int, long)):
- yield str(value)
- elif isinstance(value, float):
- yield _floatstr(value)
- elif _use_decimal and isinstance(value, Decimal):
- yield str(value)
- else:
- if isinstance(value, list):
- chunks = _iterencode_list(value, _current_indent_level)
- elif (_namedtuple_as_object and isinstance(value, tuple) and
- hasattr(value, '_asdict')):
- chunks = _iterencode_dict(value._asdict(),
- _current_indent_level)
- elif _tuple_as_array and isinstance(value, tuple):
- chunks = _iterencode_list(value, _current_indent_level)
- elif isinstance(value, dict):
- chunks = _iterencode_dict(value, _current_indent_level)
- else:
- chunks = _iterencode(value, _current_indent_level)
- for chunk in chunks:
- yield chunk
- if newline_indent is not None:
- _current_indent_level -= 1
- yield '\n' + (_indent * _current_indent_level)
- yield '}'
- if markers is not None:
- del markers[markerid]
-
- def _iterencode(o, _current_indent_level):
- if isinstance(o, basestring):
- yield _encoder(o)
- elif o is None:
- yield 'null'
- elif o is True:
- yield 'true'
- elif o is False:
- yield 'false'
- elif isinstance(o, (int, long)):
- yield str(o)
- elif isinstance(o, float):
- yield _floatstr(o)
- elif isinstance(o, list):
- for chunk in _iterencode_list(o, _current_indent_level):
- yield chunk
- elif (_namedtuple_as_object and isinstance(o, tuple) and
- hasattr(o, '_asdict')):
- for chunk in _iterencode_dict(o._asdict(), _current_indent_level):
- yield chunk
- elif (_tuple_as_array and isinstance(o, tuple)):
- for chunk in _iterencode_list(o, _current_indent_level):
- yield chunk
- elif isinstance(o, dict):
- for chunk in _iterencode_dict(o, _current_indent_level):
- yield chunk
- elif _use_decimal and isinstance(o, Decimal):
- yield str(o)
- else:
- if markers is not None:
- markerid = id(o)
- if markerid in markers:
- raise ValueError("Circular reference detected")
- markers[markerid] = o
- o = _default(o)
- for chunk in _iterencode(o, _current_indent_level):
- yield chunk
- if markers is not None:
- del markers[markerid]
-
- return _iterencode
diff --git a/module/lib/simplejson/ordered_dict.py b/module/lib/simplejson/ordered_dict.py
deleted file mode 100644
index 87ad88824..000000000
--- a/module/lib/simplejson/ordered_dict.py
+++ /dev/null
@@ -1,119 +0,0 @@
-"""Drop-in replacement for collections.OrderedDict by Raymond Hettinger
-
-http://code.activestate.com/recipes/576693/
-
-"""
-from UserDict import DictMixin
-
-# Modified from original to support Python 2.4, see
-# http://code.google.com/p/simplejson/issues/detail?id=53
-try:
- all
-except NameError:
- def all(seq):
- for elem in seq:
- if not elem:
- return False
- return True
-
-class OrderedDict(dict, DictMixin):
-
- def __init__(self, *args, **kwds):
- if len(args) > 1:
- raise TypeError('expected at most 1 arguments, got %d' % len(args))
- try:
- self.__end
- except AttributeError:
- self.clear()
- self.update(*args, **kwds)
-
- def clear(self):
- self.__end = end = []
- end += [None, end, end] # sentinel node for doubly linked list
- self.__map = {} # key --> [key, prev, next]
- dict.clear(self)
-
- def __setitem__(self, key, value):
- if key not in self:
- end = self.__end
- curr = end[1]
- curr[2] = end[1] = self.__map[key] = [key, curr, end]
- dict.__setitem__(self, key, value)
-
- def __delitem__(self, key):
- dict.__delitem__(self, key)
- key, prev, next = self.__map.pop(key)
- prev[2] = next
- next[1] = prev
-
- def __iter__(self):
- end = self.__end
- curr = end[2]
- while curr is not end:
- yield curr[0]
- curr = curr[2]
-
- def __reversed__(self):
- end = self.__end
- curr = end[1]
- while curr is not end:
- yield curr[0]
- curr = curr[1]
-
- def popitem(self, last=True):
- if not self:
- raise KeyError('dictionary is empty')
- # Modified from original to support Python 2.4, see
- # http://code.google.com/p/simplejson/issues/detail?id=53
- if last:
- key = reversed(self).next()
- else:
- key = iter(self).next()
- value = self.pop(key)
- return key, value
-
- def __reduce__(self):
- items = [[k, self[k]] for k in self]
- tmp = self.__map, self.__end
- del self.__map, self.__end
- inst_dict = vars(self).copy()
- self.__map, self.__end = tmp
- if inst_dict:
- return (self.__class__, (items,), inst_dict)
- return self.__class__, (items,)
-
- def keys(self):
- return list(self)
-
- setdefault = DictMixin.setdefault
- update = DictMixin.update
- pop = DictMixin.pop
- values = DictMixin.values
- items = DictMixin.items
- iterkeys = DictMixin.iterkeys
- itervalues = DictMixin.itervalues
- iteritems = DictMixin.iteritems
-
- def __repr__(self):
- if not self:
- return '%s()' % (self.__class__.__name__,)
- return '%s(%r)' % (self.__class__.__name__, self.items())
-
- def copy(self):
- return self.__class__(self)
-
- @classmethod
- def fromkeys(cls, iterable, value=None):
- d = cls()
- for key in iterable:
- d[key] = value
- return d
-
- def __eq__(self, other):
- if isinstance(other, OrderedDict):
- return len(self)==len(other) and \
- all(p==q for p, q in zip(self.items(), other.items()))
- return dict.__eq__(self, other)
-
- def __ne__(self, other):
- return not self == other
diff --git a/module/lib/simplejson/scanner.py b/module/lib/simplejson/scanner.py
deleted file mode 100644
index 54593a371..000000000
--- a/module/lib/simplejson/scanner.py
+++ /dev/null
@@ -1,77 +0,0 @@
-"""JSON token scanner
-"""
-import re
-def _import_c_make_scanner():
- try:
- from simplejson._speedups import make_scanner
- return make_scanner
- except ImportError:
- return None
-c_make_scanner = _import_c_make_scanner()
-
-__all__ = ['make_scanner']
-
-NUMBER_RE = re.compile(
- r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
- (re.VERBOSE | re.MULTILINE | re.DOTALL))
-
-def py_make_scanner(context):
- parse_object = context.parse_object
- parse_array = context.parse_array
- parse_string = context.parse_string
- match_number = NUMBER_RE.match
- encoding = context.encoding
- strict = context.strict
- parse_float = context.parse_float
- parse_int = context.parse_int
- parse_constant = context.parse_constant
- object_hook = context.object_hook
- object_pairs_hook = context.object_pairs_hook
- memo = context.memo
-
- def _scan_once(string, idx):
- try:
- nextchar = string[idx]
- except IndexError:
- raise StopIteration
-
- if nextchar == '"':
- return parse_string(string, idx + 1, encoding, strict)
- elif nextchar == '{':
- return parse_object((string, idx + 1), encoding, strict,
- _scan_once, object_hook, object_pairs_hook, memo)
- elif nextchar == '[':
- return parse_array((string, idx + 1), _scan_once)
- elif nextchar == 'n' and string[idx:idx + 4] == 'null':
- return None, idx + 4
- elif nextchar == 't' and string[idx:idx + 4] == 'true':
- return True, idx + 4
- elif nextchar == 'f' and string[idx:idx + 5] == 'false':
- return False, idx + 5
-
- m = match_number(string, idx)
- if m is not None:
- integer, frac, exp = m.groups()
- if frac or exp:
- res = parse_float(integer + (frac or '') + (exp or ''))
- else:
- res = parse_int(integer)
- return res, m.end()
- elif nextchar == 'N' and string[idx:idx + 3] == 'NaN':
- return parse_constant('NaN'), idx + 3
- elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity':
- return parse_constant('Infinity'), idx + 8
- elif nextchar == '-' and string[idx:idx + 9] == '-Infinity':
- return parse_constant('-Infinity'), idx + 9
- else:
- raise StopIteration
-
- def scan_once(string, idx):
- try:
- return _scan_once(string, idx)
- finally:
- memo.clear()
-
- return scan_once
-
-make_scanner = c_make_scanner or py_make_scanner
diff --git a/module/lib/simplejson/tool.py b/module/lib/simplejson/tool.py
deleted file mode 100644
index 73370db55..000000000
--- a/module/lib/simplejson/tool.py
+++ /dev/null
@@ -1,39 +0,0 @@
-r"""Command-line tool to validate and pretty-print JSON
-
-Usage::
-
- $ echo '{"json":"obj"}' | python -m simplejson.tool
- {
- "json": "obj"
- }
- $ echo '{ 1.2:3.4}' | python -m simplejson.tool
- Expecting property name: line 1 column 2 (char 2)
-
-"""
-import sys
-import simplejson as json
-
-def main():
- if len(sys.argv) == 1:
- infile = sys.stdin
- outfile = sys.stdout
- elif len(sys.argv) == 2:
- infile = open(sys.argv[1], 'rb')
- outfile = sys.stdout
- elif len(sys.argv) == 3:
- infile = open(sys.argv[1], 'rb')
- outfile = open(sys.argv[2], 'wb')
- else:
- raise SystemExit(sys.argv[0] + " [infile [outfile]]")
- try:
- obj = json.load(infile,
- object_pairs_hook=json.OrderedDict,
- use_decimal=True)
- except ValueError, e:
- raise SystemExit(e)
- json.dump(obj, outfile, sort_keys=True, indent=' ', use_decimal=True)
- outfile.write('\n')
-
-
-if __name__ == '__main__':
- main()
diff --git a/module/lib/thrift/TSCons.py b/module/lib/thrift/TSCons.py
deleted file mode 100644
index 24046256c..000000000
--- a/module/lib/thrift/TSCons.py
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from os import path
-from SCons.Builder import Builder
-
-def scons_env(env, add=''):
- opath = path.dirname(path.abspath('$TARGET'))
- lstr = 'thrift --gen cpp -o ' + opath + ' ' + add + ' $SOURCE'
- cppbuild = Builder(action = lstr)
- env.Append(BUILDERS = {'ThriftCpp' : cppbuild})
-
-def gen_cpp(env, dir, file):
- scons_env(env)
- suffixes = ['_types.h', '_types.cpp']
- targets = map(lambda s: 'gen-cpp/' + file + s, suffixes)
- return env.ThriftCpp(targets, dir+file+'.thrift')
diff --git a/module/lib/thrift/TSerialization.py b/module/lib/thrift/TSerialization.py
deleted file mode 100644
index b19f98aa8..000000000
--- a/module/lib/thrift/TSerialization.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from protocol import TBinaryProtocol
-from transport import TTransport
-
-def serialize(thrift_object, protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()):
- transport = TTransport.TMemoryBuffer()
- protocol = protocol_factory.getProtocol(transport)
- thrift_object.write(protocol)
- return transport.getvalue()
-
-def deserialize(base, buf, protocol_factory = TBinaryProtocol.TBinaryProtocolFactory()):
- transport = TTransport.TMemoryBuffer(buf)
- protocol = protocol_factory.getProtocol(transport)
- base.read(protocol)
- return base
-
diff --git a/module/lib/thrift/Thrift.py b/module/lib/thrift/Thrift.py
deleted file mode 100644
index 1d271fcff..000000000
--- a/module/lib/thrift/Thrift.py
+++ /dev/null
@@ -1,154 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-import sys
-
-class TType:
- STOP = 0
- VOID = 1
- BOOL = 2
- BYTE = 3
- I08 = 3
- DOUBLE = 4
- I16 = 6
- I32 = 8
- I64 = 10
- STRING = 11
- UTF7 = 11
- STRUCT = 12
- MAP = 13
- SET = 14
- LIST = 15
- UTF8 = 16
- UTF16 = 17
-
- _VALUES_TO_NAMES = ( 'STOP',
- 'VOID',
- 'BOOL',
- 'BYTE',
- 'DOUBLE',
- None,
- 'I16',
- None,
- 'I32',
- None,
- 'I64',
- 'STRING',
- 'STRUCT',
- 'MAP',
- 'SET',
- 'LIST',
- 'UTF8',
- 'UTF16' )
-
-class TMessageType:
- CALL = 1
- REPLY = 2
- EXCEPTION = 3
- ONEWAY = 4
-
-class TProcessor:
-
- """Base class for procsessor, which works on two streams."""
-
- def process(iprot, oprot):
- pass
-
-class TException(Exception):
-
- """Base class for all thrift exceptions."""
-
- # BaseException.message is deprecated in Python v[2.6,3.0)
- if (2,6,0) <= sys.version_info < (3,0):
- def _get_message(self):
- return self._message
- def _set_message(self, message):
- self._message = message
- message = property(_get_message, _set_message)
-
- def __init__(self, message=None):
- Exception.__init__(self, message)
- self.message = message
-
-class TApplicationException(TException):
-
- """Application level thrift exceptions."""
-
- UNKNOWN = 0
- UNKNOWN_METHOD = 1
- INVALID_MESSAGE_TYPE = 2
- WRONG_METHOD_NAME = 3
- BAD_SEQUENCE_ID = 4
- MISSING_RESULT = 5
- INTERNAL_ERROR = 6
- PROTOCOL_ERROR = 7
-
- def __init__(self, type=UNKNOWN, message=None):
- TException.__init__(self, message)
- self.type = type
-
- def __str__(self):
- if self.message:
- return self.message
- elif self.type == self.UNKNOWN_METHOD:
- return 'Unknown method'
- elif self.type == self.INVALID_MESSAGE_TYPE:
- return 'Invalid message type'
- elif self.type == self.WRONG_METHOD_NAME:
- return 'Wrong method name'
- elif self.type == self.BAD_SEQUENCE_ID:
- return 'Bad sequence ID'
- elif self.type == self.MISSING_RESULT:
- return 'Missing result'
- else:
- return 'Default (unknown) TApplicationException'
-
- def read(self, iprot):
- iprot.readStructBegin()
- while True:
- (fname, ftype, fid) = iprot.readFieldBegin()
- if ftype == TType.STOP:
- break
- if fid == 1:
- if ftype == TType.STRING:
- self.message = iprot.readString();
- else:
- iprot.skip(ftype)
- elif fid == 2:
- if ftype == TType.I32:
- self.type = iprot.readI32();
- else:
- iprot.skip(ftype)
- else:
- iprot.skip(ftype)
- iprot.readFieldEnd()
- iprot.readStructEnd()
-
- def write(self, oprot):
- oprot.writeStructBegin('TApplicationException')
- if self.message != None:
- oprot.writeFieldBegin('message', TType.STRING, 1)
- oprot.writeString(self.message)
- oprot.writeFieldEnd()
- if self.type != None:
- oprot.writeFieldBegin('type', TType.I32, 2)
- oprot.writeI32(self.type)
- oprot.writeFieldEnd()
- oprot.writeFieldStop()
- oprot.writeStructEnd()
diff --git a/module/lib/thrift/__init__.py b/module/lib/thrift/__init__.py
deleted file mode 100644
index 48d659c40..000000000
--- a/module/lib/thrift/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-__all__ = ['Thrift', 'TSCons']
diff --git a/module/lib/thrift/protocol/TBase.py b/module/lib/thrift/protocol/TBase.py
deleted file mode 100644
index e675c7dc0..000000000
--- a/module/lib/thrift/protocol/TBase.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from thrift.Thrift import *
-from thrift.protocol import TBinaryProtocol
-from thrift.transport import TTransport
-
-try:
- from thrift.protocol import fastbinary
-except:
- fastbinary = None
-
-class TBase(object):
- __slots__ = []
-
- def __repr__(self):
- L = ['%s=%r' % (key, getattr(self, key))
- for key in self.__slots__ ]
- return '%s(%s)' % (self.__class__.__name__, ', '.join(L))
-
- def __eq__(self, other):
- if not isinstance(other, self.__class__):
- return False
- for attr in self.__slots__:
- my_val = getattr(self, attr)
- other_val = getattr(other, attr)
- if my_val != other_val:
- return False
- return True
-
- def __ne__(self, other):
- return not (self == other)
-
- def read(self, iprot):
- if iprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None and fastbinary is not None:
- fastbinary.decode_binary(self, iprot.trans, (self.__class__, self.thrift_spec))
- return
- iprot.readStruct(self, self.thrift_spec)
-
- def write(self, oprot):
- if oprot.__class__ == TBinaryProtocol.TBinaryProtocolAccelerated and self.thrift_spec is not None and fastbinary is not None:
- oprot.trans.write(fastbinary.encode_binary(self, (self.__class__, self.thrift_spec)))
- return
- oprot.writeStruct(self, self.thrift_spec)
-
-class TExceptionBase(Exception):
- # old style class so python2.4 can raise exceptions derived from this
- # This can't inherit from TBase because of that limitation.
- __slots__ = []
-
- __repr__ = TBase.__repr__.im_func
- __eq__ = TBase.__eq__.im_func
- __ne__ = TBase.__ne__.im_func
- read = TBase.read.im_func
- write = TBase.write.im_func
-
diff --git a/module/lib/thrift/protocol/TBinaryProtocol.py b/module/lib/thrift/protocol/TBinaryProtocol.py
deleted file mode 100644
index 50c6aa896..000000000
--- a/module/lib/thrift/protocol/TBinaryProtocol.py
+++ /dev/null
@@ -1,259 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from TProtocol import *
-from struct import pack, unpack
-
-class TBinaryProtocol(TProtocolBase):
-
- """Binary implementation of the Thrift protocol driver."""
-
- # NastyHaxx. Python 2.4+ on 32-bit machines forces hex constants to be
- # positive, converting this into a long. If we hardcode the int value
- # instead it'll stay in 32 bit-land.
-
- # VERSION_MASK = 0xffff0000
- VERSION_MASK = -65536
-
- # VERSION_1 = 0x80010000
- VERSION_1 = -2147418112
-
- TYPE_MASK = 0x000000ff
-
- def __init__(self, trans, strictRead=False, strictWrite=True):
- TProtocolBase.__init__(self, trans)
- self.strictRead = strictRead
- self.strictWrite = strictWrite
-
- def writeMessageBegin(self, name, type, seqid):
- if self.strictWrite:
- self.writeI32(TBinaryProtocol.VERSION_1 | type)
- self.writeString(name)
- self.writeI32(seqid)
- else:
- self.writeString(name)
- self.writeByte(type)
- self.writeI32(seqid)
-
- def writeMessageEnd(self):
- pass
-
- def writeStructBegin(self, name):
- pass
-
- def writeStructEnd(self):
- pass
-
- def writeFieldBegin(self, name, type, id):
- self.writeByte(type)
- self.writeI16(id)
-
- def writeFieldEnd(self):
- pass
-
- def writeFieldStop(self):
- self.writeByte(TType.STOP);
-
- def writeMapBegin(self, ktype, vtype, size):
- self.writeByte(ktype)
- self.writeByte(vtype)
- self.writeI32(size)
-
- def writeMapEnd(self):
- pass
-
- def writeListBegin(self, etype, size):
- self.writeByte(etype)
- self.writeI32(size)
-
- def writeListEnd(self):
- pass
-
- def writeSetBegin(self, etype, size):
- self.writeByte(etype)
- self.writeI32(size)
-
- def writeSetEnd(self):
- pass
-
- def writeBool(self, bool):
- if bool:
- self.writeByte(1)
- else:
- self.writeByte(0)
-
- def writeByte(self, byte):
- buff = pack("!b", byte)
- self.trans.write(buff)
-
- def writeI16(self, i16):
- buff = pack("!h", i16)
- self.trans.write(buff)
-
- def writeI32(self, i32):
- buff = pack("!i", i32)
- self.trans.write(buff)
-
- def writeI64(self, i64):
- buff = pack("!q", i64)
- self.trans.write(buff)
-
- def writeDouble(self, dub):
- buff = pack("!d", dub)
- self.trans.write(buff)
-
- def writeString(self, str):
- self.writeI32(len(str))
- self.trans.write(str)
-
- def readMessageBegin(self):
- sz = self.readI32()
- if sz < 0:
- version = sz & TBinaryProtocol.VERSION_MASK
- if version != TBinaryProtocol.VERSION_1:
- raise TProtocolException(type=TProtocolException.BAD_VERSION, message='Bad version in readMessageBegin: %d' % (sz))
- type = sz & TBinaryProtocol.TYPE_MASK
- name = self.readString()
- seqid = self.readI32()
- else:
- if self.strictRead:
- raise TProtocolException(type=TProtocolException.BAD_VERSION, message='No protocol version header')
- name = self.trans.readAll(sz)
- type = self.readByte()
- seqid = self.readI32()
- return (name, type, seqid)
-
- def readMessageEnd(self):
- pass
-
- def readStructBegin(self):
- pass
-
- def readStructEnd(self):
- pass
-
- def readFieldBegin(self):
- type = self.readByte()
- if type == TType.STOP:
- return (None, type, 0)
- id = self.readI16()
- return (None, type, id)
-
- def readFieldEnd(self):
- pass
-
- def readMapBegin(self):
- ktype = self.readByte()
- vtype = self.readByte()
- size = self.readI32()
- return (ktype, vtype, size)
-
- def readMapEnd(self):
- pass
-
- def readListBegin(self):
- etype = self.readByte()
- size = self.readI32()
- return (etype, size)
-
- def readListEnd(self):
- pass
-
- def readSetBegin(self):
- etype = self.readByte()
- size = self.readI32()
- return (etype, size)
-
- def readSetEnd(self):
- pass
-
- def readBool(self):
- byte = self.readByte()
- if byte == 0:
- return False
- return True
-
- def readByte(self):
- buff = self.trans.readAll(1)
- val, = unpack('!b', buff)
- return val
-
- def readI16(self):
- buff = self.trans.readAll(2)
- val, = unpack('!h', buff)
- return val
-
- def readI32(self):
- buff = self.trans.readAll(4)
- val, = unpack('!i', buff)
- return val
-
- def readI64(self):
- buff = self.trans.readAll(8)
- val, = unpack('!q', buff)
- return val
-
- def readDouble(self):
- buff = self.trans.readAll(8)
- val, = unpack('!d', buff)
- return val
-
- def readString(self):
- len = self.readI32()
- str = self.trans.readAll(len)
- return str
-
-
-class TBinaryProtocolFactory:
- def __init__(self, strictRead=False, strictWrite=True):
- self.strictRead = strictRead
- self.strictWrite = strictWrite
-
- def getProtocol(self, trans):
- prot = TBinaryProtocol(trans, self.strictRead, self.strictWrite)
- return prot
-
-
-class TBinaryProtocolAccelerated(TBinaryProtocol):
-
- """C-Accelerated version of TBinaryProtocol.
-
- This class does not override any of TBinaryProtocol's methods,
- but the generated code recognizes it directly and will call into
- our C module to do the encoding, bypassing this object entirely.
- We inherit from TBinaryProtocol so that the normal TBinaryProtocol
- encoding can happen if the fastbinary module doesn't work for some
- reason. (TODO(dreiss): Make this happen sanely in more cases.)
-
- In order to take advantage of the C module, just use
- TBinaryProtocolAccelerated instead of TBinaryProtocol.
-
- NOTE: This code was contributed by an external developer.
- The internal Thrift team has reviewed and tested it,
- but we cannot guarantee that it is production-ready.
- Please feel free to report bugs and/or success stories
- to the public mailing list.
- """
-
- pass
-
-
-class TBinaryProtocolAcceleratedFactory:
- def getProtocol(self, trans):
- return TBinaryProtocolAccelerated(trans)
diff --git a/module/lib/thrift/protocol/TCompactProtocol.py b/module/lib/thrift/protocol/TCompactProtocol.py
deleted file mode 100644
index 016a33171..000000000
--- a/module/lib/thrift/protocol/TCompactProtocol.py
+++ /dev/null
@@ -1,395 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from TProtocol import *
-from struct import pack, unpack
-
-__all__ = ['TCompactProtocol', 'TCompactProtocolFactory']
-
-CLEAR = 0
-FIELD_WRITE = 1
-VALUE_WRITE = 2
-CONTAINER_WRITE = 3
-BOOL_WRITE = 4
-FIELD_READ = 5
-CONTAINER_READ = 6
-VALUE_READ = 7
-BOOL_READ = 8
-
-def make_helper(v_from, container):
- def helper(func):
- def nested(self, *args, **kwargs):
- assert self.state in (v_from, container), (self.state, v_from, container)
- return func(self, *args, **kwargs)
- return nested
- return helper
-writer = make_helper(VALUE_WRITE, CONTAINER_WRITE)
-reader = make_helper(VALUE_READ, CONTAINER_READ)
-
-def makeZigZag(n, bits):
- return (n << 1) ^ (n >> (bits - 1))
-
-def fromZigZag(n):
- return (n >> 1) ^ -(n & 1)
-
-def writeVarint(trans, n):
- out = []
- while True:
- if n & ~0x7f == 0:
- out.append(n)
- break
- else:
- out.append((n & 0xff) | 0x80)
- n = n >> 7
- trans.write(''.join(map(chr, out)))
-
-def readVarint(trans):
- result = 0
- shift = 0
- while True:
- x = trans.readAll(1)
- byte = ord(x)
- result |= (byte & 0x7f) << shift
- if byte >> 7 == 0:
- return result
- shift += 7
-
-class CompactType:
- STOP = 0x00
- TRUE = 0x01
- FALSE = 0x02
- BYTE = 0x03
- I16 = 0x04
- I32 = 0x05
- I64 = 0x06
- DOUBLE = 0x07
- BINARY = 0x08
- LIST = 0x09
- SET = 0x0A
- MAP = 0x0B
- STRUCT = 0x0C
-
-CTYPES = {TType.STOP: CompactType.STOP,
- TType.BOOL: CompactType.TRUE, # used for collection
- TType.BYTE: CompactType.BYTE,
- TType.I16: CompactType.I16,
- TType.I32: CompactType.I32,
- TType.I64: CompactType.I64,
- TType.DOUBLE: CompactType.DOUBLE,
- TType.STRING: CompactType.BINARY,
- TType.STRUCT: CompactType.STRUCT,
- TType.LIST: CompactType.LIST,
- TType.SET: CompactType.SET,
- TType.MAP: CompactType.MAP
- }
-
-TTYPES = {}
-for k, v in CTYPES.items():
- TTYPES[v] = k
-TTYPES[CompactType.FALSE] = TType.BOOL
-del k
-del v
-
-class TCompactProtocol(TProtocolBase):
- "Compact implementation of the Thrift protocol driver."
-
- PROTOCOL_ID = 0x82
- VERSION = 1
- VERSION_MASK = 0x1f
- TYPE_MASK = 0xe0
- TYPE_SHIFT_AMOUNT = 5
-
- def __init__(self, trans):
- TProtocolBase.__init__(self, trans)
- self.state = CLEAR
- self.__last_fid = 0
- self.__bool_fid = None
- self.__bool_value = None
- self.__structs = []
- self.__containers = []
-
- def __writeVarint(self, n):
- writeVarint(self.trans, n)
-
- def writeMessageBegin(self, name, type, seqid):
- assert self.state == CLEAR
- self.__writeUByte(self.PROTOCOL_ID)
- self.__writeUByte(self.VERSION | (type << self.TYPE_SHIFT_AMOUNT))
- self.__writeVarint(seqid)
- self.__writeString(name)
- self.state = VALUE_WRITE
-
- def writeMessageEnd(self):
- assert self.state == VALUE_WRITE
- self.state = CLEAR
-
- def writeStructBegin(self, name):
- assert self.state in (CLEAR, CONTAINER_WRITE, VALUE_WRITE), self.state
- self.__structs.append((self.state, self.__last_fid))
- self.state = FIELD_WRITE
- self.__last_fid = 0
-
- def writeStructEnd(self):
- assert self.state == FIELD_WRITE
- self.state, self.__last_fid = self.__structs.pop()
-
- def writeFieldStop(self):
- self.__writeByte(0)
-
- def __writeFieldHeader(self, type, fid):
- delta = fid - self.__last_fid
- if 0 < delta <= 15:
- self.__writeUByte(delta << 4 | type)
- else:
- self.__writeByte(type)
- self.__writeI16(fid)
- self.__last_fid = fid
-
- def writeFieldBegin(self, name, type, fid):
- assert self.state == FIELD_WRITE, self.state
- if type == TType.BOOL:
- self.state = BOOL_WRITE
- self.__bool_fid = fid
- else:
- self.state = VALUE_WRITE
- self.__writeFieldHeader(CTYPES[type], fid)
-
- def writeFieldEnd(self):
- assert self.state in (VALUE_WRITE, BOOL_WRITE), self.state
- self.state = FIELD_WRITE
-
- def __writeUByte(self, byte):
- self.trans.write(pack('!B', byte))
-
- def __writeByte(self, byte):
- self.trans.write(pack('!b', byte))
-
- def __writeI16(self, i16):
- self.__writeVarint(makeZigZag(i16, 16))
-
- def __writeSize(self, i32):
- self.__writeVarint(i32)
-
- def writeCollectionBegin(self, etype, size):
- assert self.state in (VALUE_WRITE, CONTAINER_WRITE), self.state
- if size <= 14:
- self.__writeUByte(size << 4 | CTYPES[etype])
- else:
- self.__writeUByte(0xf0 | CTYPES[etype])
- self.__writeSize(size)
- self.__containers.append(self.state)
- self.state = CONTAINER_WRITE
- writeSetBegin = writeCollectionBegin
- writeListBegin = writeCollectionBegin
-
- def writeMapBegin(self, ktype, vtype, size):
- assert self.state in (VALUE_WRITE, CONTAINER_WRITE), self.state
- if size == 0:
- self.__writeByte(0)
- else:
- self.__writeSize(size)
- self.__writeUByte(CTYPES[ktype] << 4 | CTYPES[vtype])
- self.__containers.append(self.state)
- self.state = CONTAINER_WRITE
-
- def writeCollectionEnd(self):
- assert self.state == CONTAINER_WRITE, self.state
- self.state = self.__containers.pop()
- writeMapEnd = writeCollectionEnd
- writeSetEnd = writeCollectionEnd
- writeListEnd = writeCollectionEnd
-
- def writeBool(self, bool):
- if self.state == BOOL_WRITE:
- if bool:
- ctype = CompactType.TRUE
- else:
- ctype = CompactType.FALSE
- self.__writeFieldHeader(ctype, self.__bool_fid)
- elif self.state == CONTAINER_WRITE:
- if bool:
- self.__writeByte(CompactType.TRUE)
- else:
- self.__writeByte(CompactType.FALSE)
- else:
- raise AssertionError, "Invalid state in compact protocol"
-
- writeByte = writer(__writeByte)
- writeI16 = writer(__writeI16)
-
- @writer
- def writeI32(self, i32):
- self.__writeVarint(makeZigZag(i32, 32))
-
- @writer
- def writeI64(self, i64):
- self.__writeVarint(makeZigZag(i64, 64))
-
- @writer
- def writeDouble(self, dub):
- self.trans.write(pack('!d', dub))
-
- def __writeString(self, s):
- self.__writeSize(len(s))
- self.trans.write(s)
- writeString = writer(__writeString)
-
- def readFieldBegin(self):
- assert self.state == FIELD_READ, self.state
- type = self.__readUByte()
- if type & 0x0f == TType.STOP:
- return (None, 0, 0)
- delta = type >> 4
- if delta == 0:
- fid = self.__readI16()
- else:
- fid = self.__last_fid + delta
- self.__last_fid = fid
- type = type & 0x0f
- if type == CompactType.TRUE:
- self.state = BOOL_READ
- self.__bool_value = True
- elif type == CompactType.FALSE:
- self.state = BOOL_READ
- self.__bool_value = False
- else:
- self.state = VALUE_READ
- return (None, self.__getTType(type), fid)
-
- def readFieldEnd(self):
- assert self.state in (VALUE_READ, BOOL_READ), self.state
- self.state = FIELD_READ
-
- def __readUByte(self):
- result, = unpack('!B', self.trans.readAll(1))
- return result
-
- def __readByte(self):
- result, = unpack('!b', self.trans.readAll(1))
- return result
-
- def __readVarint(self):
- return readVarint(self.trans)
-
- def __readZigZag(self):
- return fromZigZag(self.__readVarint())
-
- def __readSize(self):
- result = self.__readVarint()
- if result < 0:
- raise TException("Length < 0")
- return result
-
- def readMessageBegin(self):
- assert self.state == CLEAR
- proto_id = self.__readUByte()
- if proto_id != self.PROTOCOL_ID:
- raise TProtocolException(TProtocolException.BAD_VERSION,
- 'Bad protocol id in the message: %d' % proto_id)
- ver_type = self.__readUByte()
- type = (ver_type & self.TYPE_MASK) >> self.TYPE_SHIFT_AMOUNT
- version = ver_type & self.VERSION_MASK
- if version != self.VERSION:
- raise TProtocolException(TProtocolException.BAD_VERSION,
- 'Bad version: %d (expect %d)' % (version, self.VERSION))
- seqid = self.__readVarint()
- name = self.__readString()
- return (name, type, seqid)
-
- def readMessageEnd(self):
- assert self.state == CLEAR
- assert len(self.__structs) == 0
-
- def readStructBegin(self):
- assert self.state in (CLEAR, CONTAINER_READ, VALUE_READ), self.state
- self.__structs.append((self.state, self.__last_fid))
- self.state = FIELD_READ
- self.__last_fid = 0
-
- def readStructEnd(self):
- assert self.state == FIELD_READ
- self.state, self.__last_fid = self.__structs.pop()
-
- def readCollectionBegin(self):
- assert self.state in (VALUE_READ, CONTAINER_READ), self.state
- size_type = self.__readUByte()
- size = size_type >> 4
- type = self.__getTType(size_type)
- if size == 15:
- size = self.__readSize()
- self.__containers.append(self.state)
- self.state = CONTAINER_READ
- return type, size
- readSetBegin = readCollectionBegin
- readListBegin = readCollectionBegin
-
- def readMapBegin(self):
- assert self.state in (VALUE_READ, CONTAINER_READ), self.state
- size = self.__readSize()
- types = 0
- if size > 0:
- types = self.__readUByte()
- vtype = self.__getTType(types)
- ktype = self.__getTType(types >> 4)
- self.__containers.append(self.state)
- self.state = CONTAINER_READ
- return (ktype, vtype, size)
-
- def readCollectionEnd(self):
- assert self.state == CONTAINER_READ, self.state
- self.state = self.__containers.pop()
- readSetEnd = readCollectionEnd
- readListEnd = readCollectionEnd
- readMapEnd = readCollectionEnd
-
- def readBool(self):
- if self.state == BOOL_READ:
- return self.__bool_value == CompactType.TRUE
- elif self.state == CONTAINER_READ:
- return self.__readByte() == CompactType.TRUE
- else:
- raise AssertionError, "Invalid state in compact protocol: %d" % self.state
-
- readByte = reader(__readByte)
- __readI16 = __readZigZag
- readI16 = reader(__readZigZag)
- readI32 = reader(__readZigZag)
- readI64 = reader(__readZigZag)
-
- @reader
- def readDouble(self):
- buff = self.trans.readAll(8)
- val, = unpack('!d', buff)
- return val
-
- def __readString(self):
- len = self.__readSize()
- return self.trans.readAll(len)
- readString = reader(__readString)
-
- def __getTType(self, byte):
- return TTYPES[byte & 0x0f]
-
-
-class TCompactProtocolFactory:
- def __init__(self):
- pass
-
- def getProtocol(self, trans):
- return TCompactProtocol(trans)
diff --git a/module/lib/thrift/protocol/TProtocol.py b/module/lib/thrift/protocol/TProtocol.py
deleted file mode 100644
index 7338ff68a..000000000
--- a/module/lib/thrift/protocol/TProtocol.py
+++ /dev/null
@@ -1,404 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from thrift.Thrift import *
-
-class TProtocolException(TException):
-
- """Custom Protocol Exception class"""
-
- UNKNOWN = 0
- INVALID_DATA = 1
- NEGATIVE_SIZE = 2
- SIZE_LIMIT = 3
- BAD_VERSION = 4
-
- def __init__(self, type=UNKNOWN, message=None):
- TException.__init__(self, message)
- self.type = type
-
-class TProtocolBase:
-
- """Base class for Thrift protocol driver."""
-
- def __init__(self, trans):
- self.trans = trans
-
- def writeMessageBegin(self, name, type, seqid):
- pass
-
- def writeMessageEnd(self):
- pass
-
- def writeStructBegin(self, name):
- pass
-
- def writeStructEnd(self):
- pass
-
- def writeFieldBegin(self, name, type, id):
- pass
-
- def writeFieldEnd(self):
- pass
-
- def writeFieldStop(self):
- pass
-
- def writeMapBegin(self, ktype, vtype, size):
- pass
-
- def writeMapEnd(self):
- pass
-
- def writeListBegin(self, etype, size):
- pass
-
- def writeListEnd(self):
- pass
-
- def writeSetBegin(self, etype, size):
- pass
-
- def writeSetEnd(self):
- pass
-
- def writeBool(self, bool):
- pass
-
- def writeByte(self, byte):
- pass
-
- def writeI16(self, i16):
- pass
-
- def writeI32(self, i32):
- pass
-
- def writeI64(self, i64):
- pass
-
- def writeDouble(self, dub):
- pass
-
- def writeString(self, str):
- pass
-
- def readMessageBegin(self):
- pass
-
- def readMessageEnd(self):
- pass
-
- def readStructBegin(self):
- pass
-
- def readStructEnd(self):
- pass
-
- def readFieldBegin(self):
- pass
-
- def readFieldEnd(self):
- pass
-
- def readMapBegin(self):
- pass
-
- def readMapEnd(self):
- pass
-
- def readListBegin(self):
- pass
-
- def readListEnd(self):
- pass
-
- def readSetBegin(self):
- pass
-
- def readSetEnd(self):
- pass
-
- def readBool(self):
- pass
-
- def readByte(self):
- pass
-
- def readI16(self):
- pass
-
- def readI32(self):
- pass
-
- def readI64(self):
- pass
-
- def readDouble(self):
- pass
-
- def readString(self):
- pass
-
- def skip(self, type):
- if type == TType.STOP:
- return
- elif type == TType.BOOL:
- self.readBool()
- elif type == TType.BYTE:
- self.readByte()
- elif type == TType.I16:
- self.readI16()
- elif type == TType.I32:
- self.readI32()
- elif type == TType.I64:
- self.readI64()
- elif type == TType.DOUBLE:
- self.readDouble()
- elif type == TType.STRING:
- self.readString()
- elif type == TType.STRUCT:
- name = self.readStructBegin()
- while True:
- (name, type, id) = self.readFieldBegin()
- if type == TType.STOP:
- break
- self.skip(type)
- self.readFieldEnd()
- self.readStructEnd()
- elif type == TType.MAP:
- (ktype, vtype, size) = self.readMapBegin()
- for i in range(size):
- self.skip(ktype)
- self.skip(vtype)
- self.readMapEnd()
- elif type == TType.SET:
- (etype, size) = self.readSetBegin()
- for i in range(size):
- self.skip(etype)
- self.readSetEnd()
- elif type == TType.LIST:
- (etype, size) = self.readListBegin()
- for i in range(size):
- self.skip(etype)
- self.readListEnd()
-
- # tuple of: ( 'reader method' name, is_container boolean, 'writer_method' name )
- _TTYPE_HANDLERS = (
- (None, None, False), # 0 == TType,STOP
- (None, None, False), # 1 == TType.VOID # TODO: handle void?
- ('readBool', 'writeBool', False), # 2 == TType.BOOL
- ('readByte', 'writeByte', False), # 3 == TType.BYTE and I08
- ('readDouble', 'writeDouble', False), # 4 == TType.DOUBLE
- (None, None, False), # 5, undefined
- ('readI16', 'writeI16', False), # 6 == TType.I16
- (None, None, False), # 7, undefined
- ('readI32', 'writeI32', False), # 8 == TType.I32
- (None, None, False), # 9, undefined
- ('readI64', 'writeI64', False), # 10 == TType.I64
- ('readString', 'writeString', False), # 11 == TType.STRING and UTF7
- ('readContainerStruct', 'writeContainerStruct', True), # 12 == TType.STRUCT
- ('readContainerMap', 'writeContainerMap', True), # 13 == TType.MAP
- ('readContainerSet', 'writeContainerSet', True), # 14 == TType.SET
- ('readContainerList', 'writeContainerList', True), # 15 == TType.LIST
- (None, None, False), # 16 == TType.UTF8 # TODO: handle utf8 types?
- (None, None, False)# 17 == TType.UTF16 # TODO: handle utf16 types?
- )
-
- def readFieldByTType(self, ttype, spec):
- try:
- (r_handler, w_handler, is_container) = self._TTYPE_HANDLERS[ttype]
- except IndexError:
- raise TProtocolException(type=TProtocolException.INVALID_DATA,
- message='Invalid field type %d' % (ttype))
- if r_handler is None:
- raise TProtocolException(type=TProtocolException.INVALID_DATA,
- message='Invalid field type %d' % (ttype))
- reader = getattr(self, r_handler)
- if not is_container:
- return reader()
- return reader(spec)
-
- def readContainerList(self, spec):
- results = []
- ttype, tspec = spec[0], spec[1]
- r_handler = self._TTYPE_HANDLERS[ttype][0]
- reader = getattr(self, r_handler)
- (list_type, list_len) = self.readListBegin()
- if tspec is None:
- # list values are simple types
- for idx in xrange(list_len):
- results.append(reader())
- else:
- # this is like an inlined readFieldByTType
- container_reader = self._TTYPE_HANDLERS[list_type][0]
- val_reader = getattr(self, container_reader)
- for idx in xrange(list_len):
- val = val_reader(tspec)
- results.append(val)
- self.readListEnd()
- return results
-
- def readContainerSet(self, spec):
- results = set()
- ttype, tspec = spec[0], spec[1]
- r_handler = self._TTYPE_HANDLERS[ttype][0]
- reader = getattr(self, r_handler)
- (set_type, set_len) = self.readSetBegin()
- if tspec is None:
- # set members are simple types
- for idx in xrange(set_len):
- results.add(reader())
- else:
- container_reader = self._TTYPE_HANDLERS[set_type][0]
- val_reader = getattr(self, container_reader)
- for idx in xrange(set_len):
- results.add(val_reader(tspec))
- self.readSetEnd()
- return results
-
- def readContainerStruct(self, spec):
- (obj_class, obj_spec) = spec
- obj = obj_class()
- obj.read(self)
- return obj
-
- def readContainerMap(self, spec):
- results = dict()
- key_ttype, key_spec = spec[0], spec[1]
- val_ttype, val_spec = spec[2], spec[3]
- (map_ktype, map_vtype, map_len) = self.readMapBegin()
- # TODO: compare types we just decoded with thrift_spec and abort/skip if types disagree
- key_reader = getattr(self, self._TTYPE_HANDLERS[key_ttype][0])
- val_reader = getattr(self, self._TTYPE_HANDLERS[val_ttype][0])
- # list values are simple types
- for idx in xrange(map_len):
- if key_spec is None:
- k_val = key_reader()
- else:
- k_val = self.readFieldByTType(key_ttype, key_spec)
- if val_spec is None:
- v_val = val_reader()
- else:
- v_val = self.readFieldByTType(val_ttype, val_spec)
- # this raises a TypeError with unhashable keys types. i.e. d=dict(); d[[0,1]] = 2 fails
- results[k_val] = v_val
- self.readMapEnd()
- return results
-
- def readStruct(self, obj, thrift_spec):
- self.readStructBegin()
- while True:
- (fname, ftype, fid) = self.readFieldBegin()
- if ftype == TType.STOP:
- break
- try:
- field = thrift_spec[fid]
- except IndexError:
- self.skip(ftype)
- else:
- if field is not None and ftype == field[1]:
- fname = field[2]
- fspec = field[3]
- val = self.readFieldByTType(ftype, fspec)
- setattr(obj, fname, val)
- else:
- self.skip(ftype)
- self.readFieldEnd()
- self.readStructEnd()
-
- def writeContainerStruct(self, val, spec):
- val.write(self)
-
- def writeContainerList(self, val, spec):
- self.writeListBegin(spec[0], len(val))
- r_handler, w_handler, is_container = self._TTYPE_HANDLERS[spec[0]]
- e_writer = getattr(self, w_handler)
- if not is_container:
- for elem in val:
- e_writer(elem)
- else:
- for elem in val:
- e_writer(elem, spec[1])
- self.writeListEnd()
-
- def writeContainerSet(self, val, spec):
- self.writeSetBegin(spec[0], len(val))
- r_handler, w_handler, is_container = self._TTYPE_HANDLERS[spec[0]]
- e_writer = getattr(self, w_handler)
- if not is_container:
- for elem in val:
- e_writer(elem)
- else:
- for elem in val:
- e_writer(elem, spec[1])
- self.writeSetEnd()
-
- def writeContainerMap(self, val, spec):
- k_type = spec[0]
- v_type = spec[2]
- ignore, ktype_name, k_is_container = self._TTYPE_HANDLERS[k_type]
- ignore, vtype_name, v_is_container = self._TTYPE_HANDLERS[v_type]
- k_writer = getattr(self, ktype_name)
- v_writer = getattr(self, vtype_name)
- self.writeMapBegin(k_type, v_type, len(val))
- for m_key, m_val in val.iteritems():
- if not k_is_container:
- k_writer(m_key)
- else:
- k_writer(m_key, spec[1])
- if not v_is_container:
- v_writer(m_val)
- else:
- v_writer(m_val, spec[3])
- self.writeMapEnd()
-
- def writeStruct(self, obj, thrift_spec):
- self.writeStructBegin(obj.__class__.__name__)
- for field in thrift_spec:
- if field is None:
- continue
- fname = field[2]
- val = getattr(obj, fname)
- if val is None:
- # skip writing out unset fields
- continue
- fid = field[0]
- ftype = field[1]
- fspec = field[3]
- # get the writer method for this value
- self.writeFieldBegin(fname, ftype, fid)
- self.writeFieldByTType(ftype, val, fspec)
- self.writeFieldEnd()
- self.writeFieldStop()
- self.writeStructEnd()
-
- def writeFieldByTType(self, ttype, val, spec):
- r_handler, w_handler, is_container = self._TTYPE_HANDLERS[ttype]
- writer = getattr(self, w_handler)
- if is_container:
- writer(val, spec)
- else:
- writer(val)
-
-class TProtocolFactory:
- def getProtocol(self, trans):
- pass
-
diff --git a/module/lib/thrift/protocol/__init__.py b/module/lib/thrift/protocol/__init__.py
deleted file mode 100644
index d53359b28..000000000
--- a/module/lib/thrift/protocol/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-__all__ = ['TProtocol', 'TBinaryProtocol', 'fastbinary', 'TBase']
diff --git a/module/lib/thrift/server/THttpServer.py b/module/lib/thrift/server/THttpServer.py
deleted file mode 100644
index 3047d9c00..000000000
--- a/module/lib/thrift/server/THttpServer.py
+++ /dev/null
@@ -1,82 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-import BaseHTTPServer
-
-from thrift.server import TServer
-from thrift.transport import TTransport
-
-class ResponseException(Exception):
- """Allows handlers to override the HTTP response
-
- Normally, THttpServer always sends a 200 response. If a handler wants
- to override this behavior (e.g., to simulate a misconfigured or
- overloaded web server during testing), it can raise a ResponseException.
- The function passed to the constructor will be called with the
- RequestHandler as its only argument.
- """
- def __init__(self, handler):
- self.handler = handler
-
-
-class THttpServer(TServer.TServer):
- """A simple HTTP-based Thrift server
-
- This class is not very performant, but it is useful (for example) for
- acting as a mock version of an Apache-based PHP Thrift endpoint."""
-
- def __init__(self, processor, server_address,
- inputProtocolFactory, outputProtocolFactory = None,
- server_class = BaseHTTPServer.HTTPServer):
- """Set up protocol factories and HTTP server.
-
- See BaseHTTPServer for server_address.
- See TServer for protocol factories."""
-
- if outputProtocolFactory is None:
- outputProtocolFactory = inputProtocolFactory
-
- TServer.TServer.__init__(self, processor, None, None, None,
- inputProtocolFactory, outputProtocolFactory)
-
- thttpserver = self
-
- class RequestHander(BaseHTTPServer.BaseHTTPRequestHandler):
- def do_POST(self):
- # Don't care about the request path.
- itrans = TTransport.TFileObjectTransport(self.rfile)
- otrans = TTransport.TFileObjectTransport(self.wfile)
- itrans = TTransport.TBufferedTransport(itrans, int(self.headers['Content-Length']))
- otrans = TTransport.TMemoryBuffer()
- iprot = thttpserver.inputProtocolFactory.getProtocol(itrans)
- oprot = thttpserver.outputProtocolFactory.getProtocol(otrans)
- try:
- thttpserver.processor.process(iprot, oprot)
- except ResponseException, exn:
- exn.handler(self)
- else:
- self.send_response(200)
- self.send_header("content-type", "application/x-thrift")
- self.end_headers()
- self.wfile.write(otrans.getvalue())
-
- self.httpd = server_class(server_address, RequestHander)
-
- def serve(self):
- self.httpd.serve_forever()
diff --git a/module/lib/thrift/server/TNonblockingServer.py b/module/lib/thrift/server/TNonblockingServer.py
deleted file mode 100644
index ea348a0b6..000000000
--- a/module/lib/thrift/server/TNonblockingServer.py
+++ /dev/null
@@ -1,310 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-"""Implementation of non-blocking server.
-
-The main idea of the server is reciving and sending requests
-only from main thread.
-
-It also makes thread pool server in tasks terms, not connections.
-"""
-import threading
-import socket
-import Queue
-import select
-import struct
-import logging
-
-from thrift.transport import TTransport
-from thrift.protocol.TBinaryProtocol import TBinaryProtocolFactory
-
-__all__ = ['TNonblockingServer']
-
-class Worker(threading.Thread):
- """Worker is a small helper to process incoming connection."""
- def __init__(self, queue):
- threading.Thread.__init__(self)
- self.queue = queue
-
- def run(self):
- """Process queries from task queue, stop if processor is None."""
- while True:
- try:
- processor, iprot, oprot, otrans, callback = self.queue.get()
- if processor is None:
- break
- processor.process(iprot, oprot)
- callback(True, otrans.getvalue())
- except Exception:
- logging.exception("Exception while processing request")
- callback(False, '')
-
-WAIT_LEN = 0
-WAIT_MESSAGE = 1
-WAIT_PROCESS = 2
-SEND_ANSWER = 3
-CLOSED = 4
-
-def locked(func):
- "Decorator which locks self.lock."
- def nested(self, *args, **kwargs):
- self.lock.acquire()
- try:
- return func(self, *args, **kwargs)
- finally:
- self.lock.release()
- return nested
-
-def socket_exception(func):
- "Decorator close object on socket.error."
- def read(self, *args, **kwargs):
- try:
- return func(self, *args, **kwargs)
- except socket.error:
- self.close()
- return read
-
-class Connection:
- """Basic class is represented connection.
-
- It can be in state:
- WAIT_LEN --- connection is reading request len.
- WAIT_MESSAGE --- connection is reading request.
- WAIT_PROCESS --- connection has just read whole request and
- waits for call ready routine.
- SEND_ANSWER --- connection is sending answer string (including length
- of answer).
- CLOSED --- socket was closed and connection should be deleted.
- """
- def __init__(self, new_socket, wake_up):
- self.socket = new_socket
- self.socket.setblocking(False)
- self.status = WAIT_LEN
- self.len = 0
- self.message = ''
- self.lock = threading.Lock()
- self.wake_up = wake_up
-
- def _read_len(self):
- """Reads length of request.
-
- It's really paranoic routine and it may be replaced by
- self.socket.recv(4)."""
- read = self.socket.recv(4 - len(self.message))
- if len(read) == 0:
- # if we read 0 bytes and self.message is empty, it means client close
- # connection
- if len(self.message) != 0:
- logging.error("can't read frame size from socket")
- self.close()
- return
- self.message += read
- if len(self.message) == 4:
- self.len, = struct.unpack('!i', self.message)
- if self.len < 0:
- logging.error("negative frame size, it seems client"\
- " doesn't use FramedTransport")
- self.close()
- elif self.len == 0:
- logging.error("empty frame, it's really strange")
- self.close()
- else:
- self.message = ''
- self.status = WAIT_MESSAGE
-
- @socket_exception
- def read(self):
- """Reads data from stream and switch state."""
- assert self.status in (WAIT_LEN, WAIT_MESSAGE)
- if self.status == WAIT_LEN:
- self._read_len()
- # go back to the main loop here for simplicity instead of
- # falling through, even though there is a good chance that
- # the message is already available
- elif self.status == WAIT_MESSAGE:
- read = self.socket.recv(self.len - len(self.message))
- if len(read) == 0:
- logging.error("can't read frame from socket (get %d of %d bytes)" %
- (len(self.message), self.len))
- self.close()
- return
- self.message += read
- if len(self.message) == self.len:
- self.status = WAIT_PROCESS
-
- @socket_exception
- def write(self):
- """Writes data from socket and switch state."""
- assert self.status == SEND_ANSWER
- sent = self.socket.send(self.message)
- if sent == len(self.message):
- self.status = WAIT_LEN
- self.message = ''
- self.len = 0
- else:
- self.message = self.message[sent:]
-
- @locked
- def ready(self, all_ok, message):
- """Callback function for switching state and waking up main thread.
-
- This function is the only function witch can be called asynchronous.
-
- The ready can switch Connection to three states:
- WAIT_LEN if request was oneway.
- SEND_ANSWER if request was processed in normal way.
- CLOSED if request throws unexpected exception.
-
- The one wakes up main thread.
- """
- assert self.status == WAIT_PROCESS
- if not all_ok:
- self.close()
- self.wake_up()
- return
- self.len = ''
- if len(message) == 0:
- # it was a oneway request, do not write answer
- self.message = ''
- self.status = WAIT_LEN
- else:
- self.message = struct.pack('!i', len(message)) + message
- self.status = SEND_ANSWER
- self.wake_up()
-
- @locked
- def is_writeable(self):
- "Returns True if connection should be added to write list of select."
- return self.status == SEND_ANSWER
-
- # it's not necessary, but...
- @locked
- def is_readable(self):
- "Returns True if connection should be added to read list of select."
- return self.status in (WAIT_LEN, WAIT_MESSAGE)
-
- @locked
- def is_closed(self):
- "Returns True if connection is closed."
- return self.status == CLOSED
-
- def fileno(self):
- "Returns the file descriptor of the associated socket."
- return self.socket.fileno()
-
- def close(self):
- "Closes connection"
- self.status = CLOSED
- self.socket.close()
-
-class TNonblockingServer:
- """Non-blocking server."""
- def __init__(self, processor, lsocket, inputProtocolFactory=None,
- outputProtocolFactory=None, threads=10):
- self.processor = processor
- self.socket = lsocket
- self.in_protocol = inputProtocolFactory or TBinaryProtocolFactory()
- self.out_protocol = outputProtocolFactory or self.in_protocol
- self.threads = int(threads)
- self.clients = {}
- self.tasks = Queue.Queue()
- self._read, self._write = socket.socketpair()
- self.prepared = False
-
- def setNumThreads(self, num):
- """Set the number of worker threads that should be created."""
- # implement ThreadPool interface
- assert not self.prepared, "You can't change number of threads for working server"
- self.threads = num
-
- def prepare(self):
- """Prepares server for serve requests."""
- self.socket.listen()
- for _ in xrange(self.threads):
- thread = Worker(self.tasks)
- thread.setDaemon(True)
- thread.start()
- self.prepared = True
-
- def wake_up(self):
- """Wake up main thread.
-
- The server usualy waits in select call in we should terminate one.
- The simplest way is using socketpair.
-
- Select always wait to read from the first socket of socketpair.
-
- In this case, we can just write anything to the second socket from
- socketpair."""
- self._write.send('1')
-
- def _select(self):
- """Does select on open connections."""
- readable = [self.socket.handle.fileno(), self._read.fileno()]
- writable = []
- for i, connection in self.clients.items():
- if connection.is_readable():
- readable.append(connection.fileno())
- if connection.is_writeable():
- writable.append(connection.fileno())
- if connection.is_closed():
- del self.clients[i]
- return select.select(readable, writable, readable)
-
- def handle(self):
- """Handle requests.
-
- WARNING! You must call prepare BEFORE calling handle.
- """
- assert self.prepared, "You have to call prepare before handle"
- rset, wset, xset = self._select()
- for readable in rset:
- if readable == self._read.fileno():
- # don't care i just need to clean readable flag
- self._read.recv(1024)
- elif readable == self.socket.handle.fileno():
- client = self.socket.accept().handle
- self.clients[client.fileno()] = Connection(client, self.wake_up)
- else:
- connection = self.clients[readable]
- connection.read()
- if connection.status == WAIT_PROCESS:
- itransport = TTransport.TMemoryBuffer(connection.message)
- otransport = TTransport.TMemoryBuffer()
- iprot = self.in_protocol.getProtocol(itransport)
- oprot = self.out_protocol.getProtocol(otransport)
- self.tasks.put([self.processor, iprot, oprot,
- otransport, connection.ready])
- for writeable in wset:
- self.clients[writeable].write()
- for oob in xset:
- self.clients[oob].close()
- del self.clients[oob]
-
- def close(self):
- """Closes the server."""
- for _ in xrange(self.threads):
- self.tasks.put([None, None, None, None, None])
- self.socket.close()
- self.prepared = False
-
- def serve(self):
- """Serve forever."""
- self.prepare()
- while True:
- self.handle()
diff --git a/module/lib/thrift/server/TProcessPoolServer.py b/module/lib/thrift/server/TProcessPoolServer.py
deleted file mode 100644
index 7ed814a88..000000000
--- a/module/lib/thrift/server/TProcessPoolServer.py
+++ /dev/null
@@ -1,125 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-
-import logging
-from multiprocessing import Process, Value, Condition, reduction
-
-from TServer import TServer
-from thrift.transport.TTransport import TTransportException
-
-class TProcessPoolServer(TServer):
-
- """
- Server with a fixed size pool of worker subprocesses which service requests.
- Note that if you need shared state between the handlers - it's up to you!
- Written by Dvir Volk, doat.com
- """
-
- def __init__(self, * args):
- TServer.__init__(self, *args)
- self.numWorkers = 10
- self.workers = []
- self.isRunning = Value('b', False)
- self.stopCondition = Condition()
- self.postForkCallback = None
-
- def setPostForkCallback(self, callback):
- if not callable(callback):
- raise TypeError("This is not a callback!")
- self.postForkCallback = callback
-
- def setNumWorkers(self, num):
- """Set the number of worker threads that should be created"""
- self.numWorkers = num
-
- def workerProcess(self):
- """Loop around getting clients from the shared queue and process them."""
-
- if self.postForkCallback:
- self.postForkCallback()
-
- while self.isRunning.value == True:
- try:
- client = self.serverTransport.accept()
- self.serveClient(client)
- except (KeyboardInterrupt, SystemExit):
- return 0
- except Exception, x:
- logging.exception(x)
-
- def serveClient(self, client):
- """Process input/output from a client for as long as possible"""
- itrans = self.inputTransportFactory.getTransport(client)
- otrans = self.outputTransportFactory.getTransport(client)
- iprot = self.inputProtocolFactory.getProtocol(itrans)
- oprot = self.outputProtocolFactory.getProtocol(otrans)
-
- try:
- while True:
- self.processor.process(iprot, oprot)
- except TTransportException, tx:
- pass
- except Exception, x:
- logging.exception(x)
-
- itrans.close()
- otrans.close()
-
-
- def serve(self):
- """Start a fixed number of worker threads and put client into a queue"""
-
- #this is a shared state that can tell the workers to exit when set as false
- self.isRunning.value = True
-
- #first bind and listen to the port
- self.serverTransport.listen()
-
- #fork the children
- for i in range(self.numWorkers):
- try:
- w = Process(target=self.workerProcess)
- w.daemon = True
- w.start()
- self.workers.append(w)
- except Exception, x:
- logging.exception(x)
-
- #wait until the condition is set by stop()
-
- while True:
-
- self.stopCondition.acquire()
- try:
- self.stopCondition.wait()
- break
- except (SystemExit, KeyboardInterrupt):
- break
- except Exception, x:
- logging.exception(x)
-
- self.isRunning.value = False
-
- def stop(self):
- self.isRunning.value = False
- self.stopCondition.acquire()
- self.stopCondition.notify()
- self.stopCondition.release()
-
diff --git a/module/lib/thrift/server/TServer.py b/module/lib/thrift/server/TServer.py
deleted file mode 100644
index 8456e2d40..000000000
--- a/module/lib/thrift/server/TServer.py
+++ /dev/null
@@ -1,274 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-import logging
-import sys
-import os
-import traceback
-import threading
-import Queue
-
-from thrift.Thrift import TProcessor
-from thrift.transport import TTransport
-from thrift.protocol import TBinaryProtocol
-
-class TServer:
-
- """Base interface for a server, which must have a serve method."""
-
- """ 3 constructors for all servers:
- 1) (processor, serverTransport)
- 2) (processor, serverTransport, transportFactory, protocolFactory)
- 3) (processor, serverTransport,
- inputTransportFactory, outputTransportFactory,
- inputProtocolFactory, outputProtocolFactory)"""
- def __init__(self, *args):
- if (len(args) == 2):
- self.__initArgs__(args[0], args[1],
- TTransport.TTransportFactoryBase(),
- TTransport.TTransportFactoryBase(),
- TBinaryProtocol.TBinaryProtocolFactory(),
- TBinaryProtocol.TBinaryProtocolFactory())
- elif (len(args) == 4):
- self.__initArgs__(args[0], args[1], args[2], args[2], args[3], args[3])
- elif (len(args) == 6):
- self.__initArgs__(args[0], args[1], args[2], args[3], args[4], args[5])
-
- def __initArgs__(self, processor, serverTransport,
- inputTransportFactory, outputTransportFactory,
- inputProtocolFactory, outputProtocolFactory):
- self.processor = processor
- self.serverTransport = serverTransport
- self.inputTransportFactory = inputTransportFactory
- self.outputTransportFactory = outputTransportFactory
- self.inputProtocolFactory = inputProtocolFactory
- self.outputProtocolFactory = outputProtocolFactory
-
- def serve(self):
- pass
-
-class TSimpleServer(TServer):
-
- """Simple single-threaded server that just pumps around one transport."""
-
- def __init__(self, *args):
- TServer.__init__(self, *args)
-
- def serve(self):
- self.serverTransport.listen()
- while True:
- client = self.serverTransport.accept()
- itrans = self.inputTransportFactory.getTransport(client)
- otrans = self.outputTransportFactory.getTransport(client)
- iprot = self.inputProtocolFactory.getProtocol(itrans)
- oprot = self.outputProtocolFactory.getProtocol(otrans)
- try:
- while True:
- self.processor.process(iprot, oprot)
- except TTransport.TTransportException, tx:
- pass
- except Exception, x:
- logging.exception(x)
-
- itrans.close()
- otrans.close()
-
-class TThreadedServer(TServer):
-
- """Threaded server that spawns a new thread per each connection."""
-
- def __init__(self, *args, **kwargs):
- TServer.__init__(self, *args)
- self.daemon = kwargs.get("daemon", False)
-
- def serve(self):
- self.serverTransport.listen()
- while True:
- try:
- client = self.serverTransport.accept()
- t = threading.Thread(target = self.handle, args=(client,))
- t.setDaemon(self.daemon)
- t.start()
- except KeyboardInterrupt:
- raise
- except Exception, x:
- logging.exception(x)
-
- def handle(self, client):
- itrans = self.inputTransportFactory.getTransport(client)
- otrans = self.outputTransportFactory.getTransport(client)
- iprot = self.inputProtocolFactory.getProtocol(itrans)
- oprot = self.outputProtocolFactory.getProtocol(otrans)
- try:
- while True:
- self.processor.process(iprot, oprot)
- except TTransport.TTransportException, tx:
- pass
- except Exception, x:
- logging.exception(x)
-
- itrans.close()
- otrans.close()
-
-class TThreadPoolServer(TServer):
-
- """Server with a fixed size pool of threads which service requests."""
-
- def __init__(self, *args, **kwargs):
- TServer.__init__(self, *args)
- self.clients = Queue.Queue()
- self.threads = 10
- self.daemon = kwargs.get("daemon", False)
-
- def setNumThreads(self, num):
- """Set the number of worker threads that should be created"""
- self.threads = num
-
- def serveThread(self):
- """Loop around getting clients from the shared queue and process them."""
- while True:
- try:
- client = self.clients.get()
- self.serveClient(client)
- except Exception, x:
- logging.exception(x)
-
- def serveClient(self, client):
- """Process input/output from a client for as long as possible"""
- itrans = self.inputTransportFactory.getTransport(client)
- otrans = self.outputTransportFactory.getTransport(client)
- iprot = self.inputProtocolFactory.getProtocol(itrans)
- oprot = self.outputProtocolFactory.getProtocol(otrans)
- try:
- while True:
- self.processor.process(iprot, oprot)
- except TTransport.TTransportException, tx:
- pass
- except Exception, x:
- logging.exception(x)
-
- itrans.close()
- otrans.close()
-
- def serve(self):
- """Start a fixed number of worker threads and put client into a queue"""
- for i in range(self.threads):
- try:
- t = threading.Thread(target = self.serveThread)
- t.setDaemon(self.daemon)
- t.start()
- except Exception, x:
- logging.exception(x)
-
- # Pump the socket for clients
- self.serverTransport.listen()
- while True:
- try:
- client = self.serverTransport.accept()
- self.clients.put(client)
- except Exception, x:
- logging.exception(x)
-
-
-class TForkingServer(TServer):
-
- """A Thrift server that forks a new process for each request"""
- """
- This is more scalable than the threaded server as it does not cause
- GIL contention.
-
- Note that this has different semantics from the threading server.
- Specifically, updates to shared variables will no longer be shared.
- It will also not work on windows.
-
- This code is heavily inspired by SocketServer.ForkingMixIn in the
- Python stdlib.
- """
-
- def __init__(self, *args):
- TServer.__init__(self, *args)
- self.children = []
-
- def serve(self):
- def try_close(file):
- try:
- file.close()
- except IOError, e:
- logging.warning(e, exc_info=True)
-
-
- self.serverTransport.listen()
- while True:
- client = self.serverTransport.accept()
- try:
- pid = os.fork()
-
- if pid: # parent
- # add before collect, otherwise you race w/ waitpid
- self.children.append(pid)
- self.collect_children()
-
- # Parent must close socket or the connection may not get
- # closed promptly
- itrans = self.inputTransportFactory.getTransport(client)
- otrans = self.outputTransportFactory.getTransport(client)
- try_close(itrans)
- try_close(otrans)
- else:
- itrans = self.inputTransportFactory.getTransport(client)
- otrans = self.outputTransportFactory.getTransport(client)
-
- iprot = self.inputProtocolFactory.getProtocol(itrans)
- oprot = self.outputProtocolFactory.getProtocol(otrans)
-
- ecode = 0
- try:
- try:
- while True:
- self.processor.process(iprot, oprot)
- except TTransport.TTransportException, tx:
- pass
- except Exception, e:
- logging.exception(e)
- ecode = 1
- finally:
- try_close(itrans)
- try_close(otrans)
-
- os._exit(ecode)
-
- except TTransport.TTransportException, tx:
- pass
- except Exception, x:
- logging.exception(x)
-
-
- def collect_children(self):
- while self.children:
- try:
- pid, status = os.waitpid(0, os.WNOHANG)
- except os.error:
- pid = None
-
- if pid:
- self.children.remove(pid)
- else:
- break
-
-
diff --git a/module/lib/thrift/server/__init__.py b/module/lib/thrift/server/__init__.py
deleted file mode 100644
index 1bf6e254e..000000000
--- a/module/lib/thrift/server/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-__all__ = ['TServer', 'TNonblockingServer']
diff --git a/module/lib/thrift/transport/THttpClient.py b/module/lib/thrift/transport/THttpClient.py
deleted file mode 100644
index 50269785c..000000000
--- a/module/lib/thrift/transport/THttpClient.py
+++ /dev/null
@@ -1,126 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from TTransport import *
-from cStringIO import StringIO
-
-import urlparse
-import httplib
-import warnings
-import socket
-
-class THttpClient(TTransportBase):
-
- """Http implementation of TTransport base."""
-
- def __init__(self, uri_or_host, port=None, path=None):
- """THttpClient supports two different types constructor parameters.
-
- THttpClient(host, port, path) - deprecated
- THttpClient(uri)
-
- Only the second supports https."""
-
- if port is not None:
- warnings.warn("Please use the THttpClient('http://host:port/path') syntax", DeprecationWarning, stacklevel=2)
- self.host = uri_or_host
- self.port = port
- assert path
- self.path = path
- self.scheme = 'http'
- else:
- parsed = urlparse.urlparse(uri_or_host)
- self.scheme = parsed.scheme
- assert self.scheme in ('http', 'https')
- if self.scheme == 'http':
- self.port = parsed.port or httplib.HTTP_PORT
- elif self.scheme == 'https':
- self.port = parsed.port or httplib.HTTPS_PORT
- self.host = parsed.hostname
- self.path = parsed.path
- if parsed.query:
- self.path += '?%s' % parsed.query
- self.__wbuf = StringIO()
- self.__http = None
- self.__timeout = None
-
- def open(self):
- if self.scheme == 'http':
- self.__http = httplib.HTTP(self.host, self.port)
- else:
- self.__http = httplib.HTTPS(self.host, self.port)
-
- def close(self):
- self.__http.close()
- self.__http = None
-
- def isOpen(self):
- return self.__http != None
-
- def setTimeout(self, ms):
- if not hasattr(socket, 'getdefaulttimeout'):
- raise NotImplementedError
-
- if ms is None:
- self.__timeout = None
- else:
- self.__timeout = ms/1000.0
-
- def read(self, sz):
- return self.__http.file.read(sz)
-
- def write(self, buf):
- self.__wbuf.write(buf)
-
- def __withTimeout(f):
- def _f(*args, **kwargs):
- orig_timeout = socket.getdefaulttimeout()
- socket.setdefaulttimeout(args[0].__timeout)
- result = f(*args, **kwargs)
- socket.setdefaulttimeout(orig_timeout)
- return result
- return _f
-
- def flush(self):
- if self.isOpen():
- self.close()
- self.open();
-
- # Pull data out of buffer
- data = self.__wbuf.getvalue()
- self.__wbuf = StringIO()
-
- # HTTP request
- self.__http.putrequest('POST', self.path)
-
- # Write headers
- self.__http.putheader('Host', self.host)
- self.__http.putheader('Content-Type', 'application/x-thrift')
- self.__http.putheader('Content-Length', str(len(data)))
- self.__http.endheaders()
-
- # Write payload
- self.__http.send(data)
-
- # Get reply to flush the request
- self.code, self.message, self.headers = self.__http.getreply()
-
- # Decorate if we know how to timeout
- if hasattr(socket, 'getdefaulttimeout'):
- flush = __withTimeout(flush)
diff --git a/module/lib/thrift/transport/TSocket.py b/module/lib/thrift/transport/TSocket.py
deleted file mode 100644
index 4e0e1874f..000000000
--- a/module/lib/thrift/transport/TSocket.py
+++ /dev/null
@@ -1,163 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from TTransport import *
-import os
-import errno
-import socket
-import sys
-
-class TSocketBase(TTransportBase):
- def _resolveAddr(self):
- if self._unix_socket is not None:
- return [(socket.AF_UNIX, socket.SOCK_STREAM, None, None, self._unix_socket)]
- else:
- return socket.getaddrinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE | socket.AI_ADDRCONFIG)
-
- def close(self):
- if self.handle:
- self.handle.close()
- self.handle = None
-
-class TSocket(TSocketBase):
- """Socket implementation of TTransport base."""
-
- def __init__(self, host='localhost', port=9090, unix_socket=None):
- """Initialize a TSocket
-
- @param host(str) The host to connect to.
- @param port(int) The (TCP) port to connect to.
- @param unix_socket(str) The filename of a unix socket to connect to.
- (host and port will be ignored.)
- """
-
- self.host = host
- self.port = port
- self.handle = None
- self._unix_socket = unix_socket
- self._timeout = None
-
- def setHandle(self, h):
- self.handle = h
-
- def isOpen(self):
- return self.handle is not None
-
- def setTimeout(self, ms):
- if ms is None:
- self._timeout = None
- else:
- self._timeout = ms/1000.0
-
- if self.handle is not None:
- self.handle.settimeout(self._timeout)
-
- def open(self):
- try:
- res0 = self._resolveAddr()
- for res in res0:
- self.handle = socket.socket(res[0], res[1])
- self.handle.settimeout(self._timeout)
- try:
- self.handle.connect(res[4])
- except socket.error, e:
- if res is not res0[-1]:
- continue
- else:
- raise e
- break
- except socket.error, e:
- if self._unix_socket:
- message = 'Could not connect to socket %s' % self._unix_socket
- else:
- message = 'Could not connect to %s:%d' % (self.host, self.port)
- raise TTransportException(type=TTransportException.NOT_OPEN, message=message)
-
- def read(self, sz):
- try:
- buff = self.handle.recv(sz)
- except socket.error, e:
- if (e.args[0] == errno.ECONNRESET and
- (sys.platform == 'darwin' or sys.platform.startswith('freebsd'))):
- # freebsd and Mach don't follow POSIX semantic of recv
- # and fail with ECONNRESET if peer performed shutdown.
- # See corresponding comment and code in TSocket::read()
- # in lib/cpp/src/transport/TSocket.cpp.
- self.close()
- # Trigger the check to raise the END_OF_FILE exception below.
- buff = ''
- else:
- raise
- if len(buff) == 0:
- raise TTransportException(type=TTransportException.END_OF_FILE, message='TSocket read 0 bytes')
- return buff
-
- def write(self, buff):
- if not self.handle:
- raise TTransportException(type=TTransportException.NOT_OPEN, message='Transport not open')
- sent = 0
- have = len(buff)
- while sent < have:
- plus = self.handle.send(buff)
- if plus == 0:
- raise TTransportException(type=TTransportException.END_OF_FILE, message='TSocket sent 0 bytes')
- sent += plus
- buff = buff[plus:]
-
- def flush(self):
- pass
-
-class TServerSocket(TSocketBase, TServerTransportBase):
- """Socket implementation of TServerTransport base."""
-
- def __init__(self, host=None, port=9090, unix_socket=None):
- self.host = host
- self.port = port
- self._unix_socket = unix_socket
- self.handle = None
-
- def listen(self):
- res0 = self._resolveAddr()
- for res in res0:
- if res[0] is socket.AF_INET6 or res is res0[-1]:
- break
-
- # We need remove the old unix socket if the file exists and
- # nobody is listening on it.
- if self._unix_socket:
- tmp = socket.socket(res[0], res[1])
- try:
- tmp.connect(res[4])
- except socket.error, err:
- eno, message = err.args
- if eno == errno.ECONNREFUSED:
- os.unlink(res[4])
-
- self.handle = socket.socket(res[0], res[1])
- self.handle.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- if hasattr(self.handle, 'settimeout'):
- self.handle.settimeout(None)
- self.handle.bind(res[4])
- self.handle.listen(128)
-
- def accept(self):
- client, addr = self.handle.accept()
- result = TSocket()
- result.setHandle(client)
- return result
diff --git a/module/lib/thrift/transport/TTransport.py b/module/lib/thrift/transport/TTransport.py
deleted file mode 100644
index 12e51a9bf..000000000
--- a/module/lib/thrift/transport/TTransport.py
+++ /dev/null
@@ -1,331 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-from cStringIO import StringIO
-from struct import pack,unpack
-from thrift.Thrift import TException
-
-class TTransportException(TException):
-
- """Custom Transport Exception class"""
-
- UNKNOWN = 0
- NOT_OPEN = 1
- ALREADY_OPEN = 2
- TIMED_OUT = 3
- END_OF_FILE = 4
-
- def __init__(self, type=UNKNOWN, message=None):
- TException.__init__(self, message)
- self.type = type
-
-class TTransportBase:
-
- """Base class for Thrift transport layer."""
-
- def isOpen(self):
- pass
-
- def open(self):
- pass
-
- def close(self):
- pass
-
- def read(self, sz):
- pass
-
- def readAll(self, sz):
- buff = ''
- have = 0
- while (have < sz):
- chunk = self.read(sz-have)
- have += len(chunk)
- buff += chunk
-
- if len(chunk) == 0:
- raise EOFError()
-
- return buff
-
- def write(self, buf):
- pass
-
- def flush(self):
- pass
-
-# This class should be thought of as an interface.
-class CReadableTransport:
- """base class for transports that are readable from C"""
-
- # TODO(dreiss): Think about changing this interface to allow us to use
- # a (Python, not c) StringIO instead, because it allows
- # you to write after reading.
-
- # NOTE: This is a classic class, so properties will NOT work
- # correctly for setting.
- @property
- def cstringio_buf(self):
- """A cStringIO buffer that contains the current chunk we are reading."""
- pass
-
- def cstringio_refill(self, partialread, reqlen):
- """Refills cstringio_buf.
-
- Returns the currently used buffer (which can but need not be the same as
- the old cstringio_buf). partialread is what the C code has read from the
- buffer, and should be inserted into the buffer before any more reads. The
- return value must be a new, not borrowed reference. Something along the
- lines of self._buf should be fine.
-
- If reqlen bytes can't be read, throw EOFError.
- """
- pass
-
-class TServerTransportBase:
-
- """Base class for Thrift server transports."""
-
- def listen(self):
- pass
-
- def accept(self):
- pass
-
- def close(self):
- pass
-
-class TTransportFactoryBase:
-
- """Base class for a Transport Factory"""
-
- def getTransport(self, trans):
- return trans
-
-class TBufferedTransportFactory:
-
- """Factory transport that builds buffered transports"""
-
- def getTransport(self, trans):
- buffered = TBufferedTransport(trans)
- return buffered
-
-
-class TBufferedTransport(TTransportBase,CReadableTransport):
-
- """Class that wraps another transport and buffers its I/O.
-
- The implementation uses a (configurable) fixed-size read buffer
- but buffers all writes until a flush is performed.
- """
-
- DEFAULT_BUFFER = 4096
-
- def __init__(self, trans, rbuf_size = DEFAULT_BUFFER):
- self.__trans = trans
- self.__wbuf = StringIO()
- self.__rbuf = StringIO("")
- self.__rbuf_size = rbuf_size
-
- def isOpen(self):
- return self.__trans.isOpen()
-
- def open(self):
- return self.__trans.open()
-
- def close(self):
- return self.__trans.close()
-
- def read(self, sz):
- ret = self.__rbuf.read(sz)
- if len(ret) != 0:
- return ret
-
- self.__rbuf = StringIO(self.__trans.read(max(sz, self.__rbuf_size)))
- return self.__rbuf.read(sz)
-
- def write(self, buf):
- self.__wbuf.write(buf)
-
- def flush(self):
- out = self.__wbuf.getvalue()
- # reset wbuf before write/flush to preserve state on underlying failure
- self.__wbuf = StringIO()
- self.__trans.write(out)
- self.__trans.flush()
-
- # Implement the CReadableTransport interface.
- @property
- def cstringio_buf(self):
- return self.__rbuf
-
- def cstringio_refill(self, partialread, reqlen):
- retstring = partialread
- if reqlen < self.__rbuf_size:
- # try to make a read of as much as we can.
- retstring += self.__trans.read(self.__rbuf_size)
-
- # but make sure we do read reqlen bytes.
- if len(retstring) < reqlen:
- retstring += self.__trans.readAll(reqlen - len(retstring))
-
- self.__rbuf = StringIO(retstring)
- return self.__rbuf
-
-class TMemoryBuffer(TTransportBase, CReadableTransport):
- """Wraps a cStringIO object as a TTransport.
-
- NOTE: Unlike the C++ version of this class, you cannot write to it
- then immediately read from it. If you want to read from a
- TMemoryBuffer, you must either pass a string to the constructor.
- TODO(dreiss): Make this work like the C++ version.
- """
-
- def __init__(self, value=None):
- """value -- a value to read from for stringio
-
- If value is set, this will be a transport for reading,
- otherwise, it is for writing"""
- if value is not None:
- self._buffer = StringIO(value)
- else:
- self._buffer = StringIO()
-
- def isOpen(self):
- return not self._buffer.closed
-
- def open(self):
- pass
-
- def close(self):
- self._buffer.close()
-
- def read(self, sz):
- return self._buffer.read(sz)
-
- def write(self, buf):
- self._buffer.write(buf)
-
- def flush(self):
- pass
-
- def getvalue(self):
- return self._buffer.getvalue()
-
- # Implement the CReadableTransport interface.
- @property
- def cstringio_buf(self):
- return self._buffer
-
- def cstringio_refill(self, partialread, reqlen):
- # only one shot at reading...
- raise EOFError()
-
-class TFramedTransportFactory:
-
- """Factory transport that builds framed transports"""
-
- def getTransport(self, trans):
- framed = TFramedTransport(trans)
- return framed
-
-
-class TFramedTransport(TTransportBase, CReadableTransport):
-
- """Class that wraps another transport and frames its I/O when writing."""
-
- def __init__(self, trans,):
- self.__trans = trans
- self.__rbuf = StringIO()
- self.__wbuf = StringIO()
-
- def isOpen(self):
- return self.__trans.isOpen()
-
- def open(self):
- return self.__trans.open()
-
- def close(self):
- return self.__trans.close()
-
- def read(self, sz):
- ret = self.__rbuf.read(sz)
- if len(ret) != 0:
- return ret
-
- self.readFrame()
- return self.__rbuf.read(sz)
-
- def readFrame(self):
- buff = self.__trans.readAll(4)
- sz, = unpack('!i', buff)
- self.__rbuf = StringIO(self.__trans.readAll(sz))
-
- def write(self, buf):
- self.__wbuf.write(buf)
-
- def flush(self):
- wout = self.__wbuf.getvalue()
- wsz = len(wout)
- # reset wbuf before write/flush to preserve state on underlying failure
- self.__wbuf = StringIO()
- # N.B.: Doing this string concatenation is WAY cheaper than making
- # two separate calls to the underlying socket object. Socket writes in
- # Python turn out to be REALLY expensive, but it seems to do a pretty
- # good job of managing string buffer operations without excessive copies
- buf = pack("!i", wsz) + wout
- self.__trans.write(buf)
- self.__trans.flush()
-
- # Implement the CReadableTransport interface.
- @property
- def cstringio_buf(self):
- return self.__rbuf
-
- def cstringio_refill(self, prefix, reqlen):
- # self.__rbuf will already be empty here because fastbinary doesn't
- # ask for a refill until the previous buffer is empty. Therefore,
- # we can start reading new frames immediately.
- while len(prefix) < reqlen:
- self.readFrame()
- prefix += self.__rbuf.getvalue()
- self.__rbuf = StringIO(prefix)
- return self.__rbuf
-
-
-class TFileObjectTransport(TTransportBase):
- """Wraps a file-like object to make it work as a Thrift transport."""
-
- def __init__(self, fileobj):
- self.fileobj = fileobj
-
- def isOpen(self):
- return True
-
- def close(self):
- self.fileobj.close()
-
- def read(self, sz):
- return self.fileobj.read(sz)
-
- def write(self, buf):
- self.fileobj.write(buf)
-
- def flush(self):
- self.fileobj.flush()
diff --git a/module/lib/thrift/transport/TTwisted.py b/module/lib/thrift/transport/TTwisted.py
deleted file mode 100644
index b6dcb4e0b..000000000
--- a/module/lib/thrift/transport/TTwisted.py
+++ /dev/null
@@ -1,219 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-from zope.interface import implements, Interface, Attribute
-from twisted.internet.protocol import Protocol, ServerFactory, ClientFactory, \
- connectionDone
-from twisted.internet import defer
-from twisted.protocols import basic
-from twisted.python import log
-from twisted.web import server, resource, http
-
-from thrift.transport import TTransport
-from cStringIO import StringIO
-
-
-class TMessageSenderTransport(TTransport.TTransportBase):
-
- def __init__(self):
- self.__wbuf = StringIO()
-
- def write(self, buf):
- self.__wbuf.write(buf)
-
- def flush(self):
- msg = self.__wbuf.getvalue()
- self.__wbuf = StringIO()
- self.sendMessage(msg)
-
- def sendMessage(self, message):
- raise NotImplementedError
-
-
-class TCallbackTransport(TMessageSenderTransport):
-
- def __init__(self, func):
- TMessageSenderTransport.__init__(self)
- self.func = func
-
- def sendMessage(self, message):
- self.func(message)
-
-
-class ThriftClientProtocol(basic.Int32StringReceiver):
-
- MAX_LENGTH = 2 ** 31 - 1
-
- def __init__(self, client_class, iprot_factory, oprot_factory=None):
- self._client_class = client_class
- self._iprot_factory = iprot_factory
- if oprot_factory is None:
- self._oprot_factory = iprot_factory
- else:
- self._oprot_factory = oprot_factory
-
- self.recv_map = {}
- self.started = defer.Deferred()
-
- def dispatch(self, msg):
- self.sendString(msg)
-
- def connectionMade(self):
- tmo = TCallbackTransport(self.dispatch)
- self.client = self._client_class(tmo, self._oprot_factory)
- self.started.callback(self.client)
-
- def connectionLost(self, reason=connectionDone):
- for k,v in self.client._reqs.iteritems():
- tex = TTransport.TTransportException(
- type=TTransport.TTransportException.END_OF_FILE,
- message='Connection closed')
- v.errback(tex)
-
- def stringReceived(self, frame):
- tr = TTransport.TMemoryBuffer(frame)
- iprot = self._iprot_factory.getProtocol(tr)
- (fname, mtype, rseqid) = iprot.readMessageBegin()
-
- try:
- method = self.recv_map[fname]
- except KeyError:
- method = getattr(self.client, 'recv_' + fname)
- self.recv_map[fname] = method
-
- method(iprot, mtype, rseqid)
-
-
-class ThriftServerProtocol(basic.Int32StringReceiver):
-
- MAX_LENGTH = 2 ** 31 - 1
-
- def dispatch(self, msg):
- self.sendString(msg)
-
- def processError(self, error):
- self.transport.loseConnection()
-
- def processOk(self, _, tmo):
- msg = tmo.getvalue()
-
- if len(msg) > 0:
- self.dispatch(msg)
-
- def stringReceived(self, frame):
- tmi = TTransport.TMemoryBuffer(frame)
- tmo = TTransport.TMemoryBuffer()
-
- iprot = self.factory.iprot_factory.getProtocol(tmi)
- oprot = self.factory.oprot_factory.getProtocol(tmo)
-
- d = self.factory.processor.process(iprot, oprot)
- d.addCallbacks(self.processOk, self.processError,
- callbackArgs=(tmo,))
-
-
-class IThriftServerFactory(Interface):
-
- processor = Attribute("Thrift processor")
-
- iprot_factory = Attribute("Input protocol factory")
-
- oprot_factory = Attribute("Output protocol factory")
-
-
-class IThriftClientFactory(Interface):
-
- client_class = Attribute("Thrift client class")
-
- iprot_factory = Attribute("Input protocol factory")
-
- oprot_factory = Attribute("Output protocol factory")
-
-
-class ThriftServerFactory(ServerFactory):
-
- implements(IThriftServerFactory)
-
- protocol = ThriftServerProtocol
-
- def __init__(self, processor, iprot_factory, oprot_factory=None):
- self.processor = processor
- self.iprot_factory = iprot_factory
- if oprot_factory is None:
- self.oprot_factory = iprot_factory
- else:
- self.oprot_factory = oprot_factory
-
-
-class ThriftClientFactory(ClientFactory):
-
- implements(IThriftClientFactory)
-
- protocol = ThriftClientProtocol
-
- def __init__(self, client_class, iprot_factory, oprot_factory=None):
- self.client_class = client_class
- self.iprot_factory = iprot_factory
- if oprot_factory is None:
- self.oprot_factory = iprot_factory
- else:
- self.oprot_factory = oprot_factory
-
- def buildProtocol(self, addr):
- p = self.protocol(self.client_class, self.iprot_factory,
- self.oprot_factory)
- p.factory = self
- return p
-
-
-class ThriftResource(resource.Resource):
-
- allowedMethods = ('POST',)
-
- def __init__(self, processor, inputProtocolFactory,
- outputProtocolFactory=None):
- resource.Resource.__init__(self)
- self.inputProtocolFactory = inputProtocolFactory
- if outputProtocolFactory is None:
- self.outputProtocolFactory = inputProtocolFactory
- else:
- self.outputProtocolFactory = outputProtocolFactory
- self.processor = processor
-
- def getChild(self, path, request):
- return self
-
- def _cbProcess(self, _, request, tmo):
- msg = tmo.getvalue()
- request.setResponseCode(http.OK)
- request.setHeader("content-type", "application/x-thrift")
- request.write(msg)
- request.finish()
-
- def render_POST(self, request):
- request.content.seek(0, 0)
- data = request.content.read()
- tmi = TTransport.TMemoryBuffer(data)
- tmo = TTransport.TMemoryBuffer()
-
- iprot = self.inputProtocolFactory.getProtocol(tmi)
- oprot = self.outputProtocolFactory.getProtocol(tmo)
-
- d = self.processor.process(iprot, oprot)
- d.addCallback(self._cbProcess, request, tmo)
- return server.NOT_DONE_YET
diff --git a/module/lib/thrift/transport/TZlibTransport.py b/module/lib/thrift/transport/TZlibTransport.py
deleted file mode 100644
index 784d4e1e0..000000000
--- a/module/lib/thrift/transport/TZlibTransport.py
+++ /dev/null
@@ -1,261 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-'''
-TZlibTransport provides a compressed transport and transport factory
-class, using the python standard library zlib module to implement
-data compression.
-'''
-
-from __future__ import division
-import zlib
-from cStringIO import StringIO
-from TTransport import TTransportBase, CReadableTransport
-
-class TZlibTransportFactory(object):
- '''
- Factory transport that builds zlib compressed transports.
-
- This factory caches the last single client/transport that it was passed
- and returns the same TZlibTransport object that was created.
-
- This caching means the TServer class will get the _same_ transport
- object for both input and output transports from this factory.
- (For non-threaded scenarios only, since the cache only holds one object)
-
- The purpose of this caching is to allocate only one TZlibTransport where
- only one is really needed (since it must have separate read/write buffers),
- and makes the statistics from getCompSavings() and getCompRatio()
- easier to understand.
- '''
-
- # class scoped cache of last transport given and zlibtransport returned
- _last_trans = None
- _last_z = None
-
- def getTransport(self, trans, compresslevel=9):
- '''Wrap a transport , trans, with the TZlibTransport
- compressed transport class, returning a new
- transport to the caller.
-
- @param compresslevel: The zlib compression level, ranging
- from 0 (no compression) to 9 (best compression). Defaults to 9.
- @type compresslevel: int
-
- This method returns a TZlibTransport which wraps the
- passed C{trans} TTransport derived instance.
- '''
- if trans == self._last_trans:
- return self._last_z
- ztrans = TZlibTransport(trans, compresslevel)
- self._last_trans = trans
- self._last_z = ztrans
- return ztrans
-
-
-class TZlibTransport(TTransportBase, CReadableTransport):
- '''
- Class that wraps a transport with zlib, compressing writes
- and decompresses reads, using the python standard
- library zlib module.
- '''
-
- # Read buffer size for the python fastbinary C extension,
- # the TBinaryProtocolAccelerated class.
- DEFAULT_BUFFSIZE = 4096
-
- def __init__(self, trans, compresslevel=9):
- '''
- Create a new TZlibTransport, wrapping C{trans}, another
- TTransport derived object.
-
- @param trans: A thrift transport object, i.e. a TSocket() object.
- @type trans: TTransport
- @param compresslevel: The zlib compression level, ranging
- from 0 (no compression) to 9 (best compression). Default is 9.
- @type compresslevel: int
- '''
- self.__trans = trans
- self.compresslevel = compresslevel
- self.__rbuf = StringIO()
- self.__wbuf = StringIO()
- self._init_zlib()
- self._init_stats()
-
- def _reinit_buffers(self):
- '''
- Internal method to initialize/reset the internal StringIO objects
- for read and write buffers.
- '''
- self.__rbuf = StringIO()
- self.__wbuf = StringIO()
-
- def _init_stats(self):
- '''
- Internal method to reset the internal statistics counters
- for compression ratios and bandwidth savings.
- '''
- self.bytes_in = 0
- self.bytes_out = 0
- self.bytes_in_comp = 0
- self.bytes_out_comp = 0
-
- def _init_zlib(self):
- '''
- Internal method for setting up the zlib compression and
- decompression objects.
- '''
- self._zcomp_read = zlib.decompressobj()
- self._zcomp_write = zlib.compressobj(self.compresslevel)
-
- def getCompRatio(self):
- '''
- Get the current measured compression ratios (in,out) from
- this transport.
-
- Returns a tuple of:
- (inbound_compression_ratio, outbound_compression_ratio)
-
- The compression ratios are computed as:
- compressed / uncompressed
-
- E.g., data that compresses by 10x will have a ratio of: 0.10
- and data that compresses to half of ts original size will
- have a ratio of 0.5
-
- None is returned if no bytes have yet been processed in
- a particular direction.
- '''
- r_percent, w_percent = (None, None)
- if self.bytes_in > 0:
- r_percent = self.bytes_in_comp / self.bytes_in
- if self.bytes_out > 0:
- w_percent = self.bytes_out_comp / self.bytes_out
- return (r_percent, w_percent)
-
- def getCompSavings(self):
- '''
- Get the current count of saved bytes due to data
- compression.
-
- Returns a tuple of:
- (inbound_saved_bytes, outbound_saved_bytes)
-
- Note: if compression is actually expanding your
- data (only likely with very tiny thrift objects), then
- the values returned will be negative.
- '''
- r_saved = self.bytes_in - self.bytes_in_comp
- w_saved = self.bytes_out - self.bytes_out_comp
- return (r_saved, w_saved)
-
- def isOpen(self):
- '''Return the underlying transport's open status'''
- return self.__trans.isOpen()
-
- def open(self):
- """Open the underlying transport"""
- self._init_stats()
- return self.__trans.open()
-
- def listen(self):
- '''Invoke the underlying transport's listen() method'''
- self.__trans.listen()
-
- def accept(self):
- '''Accept connections on the underlying transport'''
- return self.__trans.accept()
-
- def close(self):
- '''Close the underlying transport,'''
- self._reinit_buffers()
- self._init_zlib()
- return self.__trans.close()
-
- def read(self, sz):
- '''
- Read up to sz bytes from the decompressed bytes buffer, and
- read from the underlying transport if the decompression
- buffer is empty.
- '''
- ret = self.__rbuf.read(sz)
- if len(ret) > 0:
- return ret
- # keep reading from transport until something comes back
- while True:
- if self.readComp(sz):
- break
- ret = self.__rbuf.read(sz)
- return ret
-
- def readComp(self, sz):
- '''
- Read compressed data from the underlying transport, then
- decompress it and append it to the internal StringIO read buffer
- '''
- zbuf = self.__trans.read(sz)
- zbuf = self._zcomp_read.unconsumed_tail + zbuf
- buf = self._zcomp_read.decompress(zbuf)
- self.bytes_in += len(zbuf)
- self.bytes_in_comp += len(buf)
- old = self.__rbuf.read()
- self.__rbuf = StringIO(old + buf)
- if len(old) + len(buf) == 0:
- return False
- return True
-
- def write(self, buf):
- '''
- Write some bytes, putting them into the internal write
- buffer for eventual compression.
- '''
- self.__wbuf.write(buf)
-
- def flush(self):
- '''
- Flush any queued up data in the write buffer and ensure the
- compression buffer is flushed out to the underlying transport
- '''
- wout = self.__wbuf.getvalue()
- if len(wout) > 0:
- zbuf = self._zcomp_write.compress(wout)
- self.bytes_out += len(wout)
- self.bytes_out_comp += len(zbuf)
- else:
- zbuf = ''
- ztail = self._zcomp_write.flush(zlib.Z_SYNC_FLUSH)
- self.bytes_out_comp += len(ztail)
- if (len(zbuf) + len(ztail)) > 0:
- self.__wbuf = StringIO()
- self.__trans.write(zbuf + ztail)
- self.__trans.flush()
-
- @property
- def cstringio_buf(self):
- '''Implement the CReadableTransport interface'''
- return self.__rbuf
-
- def cstringio_refill(self, partialread, reqlen):
- '''Implement the CReadableTransport interface for refill'''
- retstring = partialread
- if reqlen < self.DEFAULT_BUFFSIZE:
- retstring += self.read(self.DEFAULT_BUFFSIZE)
- while len(retstring) < reqlen:
- retstring += self.read(reqlen - len(retstring))
- self.__rbuf = StringIO(retstring)
- return self.__rbuf
diff --git a/module/lib/thrift/transport/__init__.py b/module/lib/thrift/transport/__init__.py
deleted file mode 100644
index 46e54fe6b..000000000
--- a/module/lib/thrift/transport/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-#
-
-__all__ = ['TTransport', 'TSocket', 'THttpClient','TZlibTransport']
diff --git a/module/lib/wsgiserver/LICENSE.txt b/module/lib/wsgiserver/LICENSE.txt
deleted file mode 100644
index a15165ee2..000000000
--- a/module/lib/wsgiserver/LICENSE.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright (c) 2004-2007, CherryPy Team (team@cherrypy.org)
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * Neither the name of the CherryPy Team nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/module/lib/wsgiserver/__init__.py b/module/lib/wsgiserver/__init__.py
deleted file mode 100644
index c380e18b0..000000000
--- a/module/lib/wsgiserver/__init__.py
+++ /dev/null
@@ -1,1794 +0,0 @@
-"""A high-speed, production ready, thread pooled, generic WSGI server.
-
-Simplest example on how to use this module directly
-(without using CherryPy's application machinery):
-
- from cherrypy import wsgiserver
-
- def my_crazy_app(environ, start_response):
- status = '200 OK'
- response_headers = [('Content-type','text/plain')]
- start_response(status, response_headers)
- return ['Hello world!\n']
-
- server = wsgiserver.CherryPyWSGIServer(
- ('0.0.0.0', 8070), my_crazy_app,
- server_name='www.cherrypy.example')
-
-The CherryPy WSGI server can serve as many WSGI applications
-as you want in one instance by using a WSGIPathInfoDispatcher:
-
- d = WSGIPathInfoDispatcher({'/': my_crazy_app, '/blog': my_blog_app})
- server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), d)
-
-Want SSL support? Just set these attributes:
-
- server.ssl_certificate = <filename>
- server.ssl_private_key = <filename>
-
- if __name__ == '__main__':
- try:
- server.start()
- except KeyboardInterrupt:
- server.stop()
-
-This won't call the CherryPy engine (application side) at all, only the
-WSGI server, which is independant from the rest of CherryPy. Don't
-let the name "CherryPyWSGIServer" throw you; the name merely reflects
-its origin, not its coupling.
-
-For those of you wanting to understand internals of this module, here's the
-basic call flow. The server's listening thread runs a very tight loop,
-sticking incoming connections onto a Queue:
-
- server = CherryPyWSGIServer(...)
- server.start()
- while True:
- tick()
- # This blocks until a request comes in:
- child = socket.accept()
- conn = HTTPConnection(child, ...)
- server.requests.put(conn)
-
-Worker threads are kept in a pool and poll the Queue, popping off and then
-handling each connection in turn. Each connection can consist of an arbitrary
-number of requests and their responses, so we run a nested loop:
-
- while True:
- conn = server.requests.get()
- conn.communicate()
- -> while True:
- req = HTTPRequest(...)
- req.parse_request()
- -> # Read the Request-Line, e.g. "GET /page HTTP/1.1"
- req.rfile.readline()
- req.read_headers()
- req.respond()
- -> response = wsgi_app(...)
- try:
- for chunk in response:
- if chunk:
- req.write(chunk)
- finally:
- if hasattr(response, "close"):
- response.close()
- if req.close_connection:
- return
-"""
-
-
-import base64
-import os
-import Queue
-import re
-quoted_slash = re.compile("(?i)%2F")
-import rfc822
-import socket
-try:
- import cStringIO as StringIO
-except ImportError:
- import StringIO
-
-_fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring)
-
-import sys
-import threading
-import time
-import traceback
-from urllib import unquote
-from urlparse import urlparse
-import warnings
-
-try:
- from OpenSSL import SSL
- from OpenSSL import crypto
-except ImportError:
- SSL = None
-
-import errno
-
-def plat_specific_errors(*errnames):
- """Return error numbers for all errors in errnames on this platform.
-
- The 'errno' module contains different global constants depending on
- the specific platform (OS). This function will return the list of
- numeric values for a given list of potential names.
- """
- errno_names = dir(errno)
- nums = [getattr(errno, k) for k in errnames if k in errno_names]
- # de-dupe the list
- return dict.fromkeys(nums).keys()
-
-socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR")
-
-socket_errors_to_ignore = plat_specific_errors(
- "EPIPE",
- "EBADF", "WSAEBADF",
- "ENOTSOCK", "WSAENOTSOCK",
- "ETIMEDOUT", "WSAETIMEDOUT",
- "ECONNREFUSED", "WSAECONNREFUSED",
- "ECONNRESET", "WSAECONNRESET",
- "ECONNABORTED", "WSAECONNABORTED",
- "ENETRESET", "WSAENETRESET",
- "EHOSTDOWN", "EHOSTUNREACH",
- )
-socket_errors_to_ignore.append("timed out")
-
-socket_errors_nonblocking = plat_specific_errors(
- 'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
-
-comma_separated_headers = ['ACCEPT', 'ACCEPT-CHARSET', 'ACCEPT-ENCODING',
- 'ACCEPT-LANGUAGE', 'ACCEPT-RANGES', 'ALLOW', 'CACHE-CONTROL',
- 'CONNECTION', 'CONTENT-ENCODING', 'CONTENT-LANGUAGE', 'EXPECT',
- 'IF-MATCH', 'IF-NONE-MATCH', 'PRAGMA', 'PROXY-AUTHENTICATE', 'TE',
- 'TRAILER', 'TRANSFER-ENCODING', 'UPGRADE', 'VARY', 'VIA', 'WARNING',
- 'WWW-AUTHENTICATE']
-
-
-class WSGIPathInfoDispatcher(object):
- """A WSGI dispatcher for dispatch based on the PATH_INFO.
-
- apps: a dict or list of (path_prefix, app) pairs.
- """
-
- def __init__(self, apps):
- try:
- apps = apps.items()
- except AttributeError:
- pass
-
- # Sort the apps by len(path), descending
- apps.sort()
- apps.reverse()
-
- # The path_prefix strings must start, but not end, with a slash.
- # Use "" instead of "/".
- self.apps = [(p.rstrip("/"), a) for p, a in apps]
-
- def __call__(self, environ, start_response):
- path = environ["PATH_INFO"] or "/"
- for p, app in self.apps:
- # The apps list should be sorted by length, descending.
- if path.startswith(p + "/") or path == p:
- environ = environ.copy()
- environ["SCRIPT_NAME"] = environ["SCRIPT_NAME"] + p
- environ["PATH_INFO"] = path[len(p):]
- return app(environ, start_response)
-
- start_response('404 Not Found', [('Content-Type', 'text/plain'),
- ('Content-Length', '0')])
- return ['']
-
-
-class MaxSizeExceeded(Exception):
- pass
-
-class SizeCheckWrapper(object):
- """Wraps a file-like object, raising MaxSizeExceeded if too large."""
-
- def __init__(self, rfile, maxlen):
- self.rfile = rfile
- self.maxlen = maxlen
- self.bytes_read = 0
-
- def _check_length(self):
- if self.maxlen and self.bytes_read > self.maxlen:
- raise MaxSizeExceeded()
-
- def read(self, size=None):
- data = self.rfile.read(size)
- self.bytes_read += len(data)
- self._check_length()
- return data
-
- def readline(self, size=None):
- if size is not None:
- data = self.rfile.readline(size)
- self.bytes_read += len(data)
- self._check_length()
- return data
-
- # User didn't specify a size ...
- # We read the line in chunks to make sure it's not a 100MB line !
- res = []
- while True:
- data = self.rfile.readline(256)
- self.bytes_read += len(data)
- self._check_length()
- res.append(data)
- # See http://www.cherrypy.org/ticket/421
- if len(data) < 256 or data[-1:] == "\n":
- return ''.join(res)
-
- def readlines(self, sizehint=0):
- # Shamelessly stolen from StringIO
- total = 0
- lines = []
- line = self.readline()
- while line:
- lines.append(line)
- total += len(line)
- if 0 < sizehint <= total:
- break
- line = self.readline()
- return lines
-
- def close(self):
- self.rfile.close()
-
- def __iter__(self):
- return self
-
- def next(self):
- data = self.rfile.next()
- self.bytes_read += len(data)
- self._check_length()
- return data
-
-
-class HTTPRequest(object):
- """An HTTP Request (and response).
-
- A single HTTP connection may consist of multiple request/response pairs.
-
- send: the 'send' method from the connection's socket object.
- wsgi_app: the WSGI application to call.
- environ: a partial WSGI environ (server and connection entries).
- The caller MUST set the following entries:
- * All wsgi.* entries, including .input
- * SERVER_NAME and SERVER_PORT
- * Any SSL_* entries
- * Any custom entries like REMOTE_ADDR and REMOTE_PORT
- * SERVER_SOFTWARE: the value to write in the "Server" response header.
- * ACTUAL_SERVER_PROTOCOL: the value to write in the Status-Line of
- the response. From RFC 2145: "An HTTP server SHOULD send a
- response version equal to the highest version for which the
- server is at least conditionally compliant, and whose major
- version is less than or equal to the one received in the
- request. An HTTP server MUST NOT send a version for which
- it is not at least conditionally compliant."
-
- outheaders: a list of header tuples to write in the response.
- ready: when True, the request has been parsed and is ready to begin
- generating the response. When False, signals the calling Connection
- that the response should not be generated and the connection should
- close.
- close_connection: signals the calling Connection that the request
- should close. This does not imply an error! The client and/or
- server may each request that the connection be closed.
- chunked_write: if True, output will be encoded with the "chunked"
- transfer-coding. This value is set automatically inside
- send_headers.
- """
-
- max_request_header_size = 0
- max_request_body_size = 0
-
- def __init__(self, wfile, environ, wsgi_app):
- self.rfile = environ['wsgi.input']
- self.wfile = wfile
- self.environ = environ.copy()
- self.wsgi_app = wsgi_app
-
- self.ready = False
- self.started_response = False
- self.status = ""
- self.outheaders = []
- self.sent_headers = False
- self.close_connection = False
- self.chunked_write = False
-
- def parse_request(self):
- """Parse the next HTTP request start-line and message-headers."""
- self.rfile.maxlen = self.max_request_header_size
- self.rfile.bytes_read = 0
-
- try:
- self._parse_request()
- except MaxSizeExceeded:
- self.simple_response("413 Request Entity Too Large")
- return
-
- def _parse_request(self):
- # HTTP/1.1 connections are persistent by default. If a client
- # requests a page, then idles (leaves the connection open),
- # then rfile.readline() will raise socket.error("timed out").
- # Note that it does this based on the value given to settimeout(),
- # and doesn't need the client to request or acknowledge the close
- # (although your TCP stack might suffer for it: cf Apache's history
- # with FIN_WAIT_2).
- request_line = self.rfile.readline()
- if not request_line:
- # Force self.ready = False so the connection will close.
- self.ready = False
- return
-
- if request_line == "\r\n":
- # RFC 2616 sec 4.1: "...if the server is reading the protocol
- # stream at the beginning of a message and receives a CRLF
- # first, it should ignore the CRLF."
- # But only ignore one leading line! else we enable a DoS.
- request_line = self.rfile.readline()
- if not request_line:
- self.ready = False
- return
-
- environ = self.environ
-
- try:
- method, path, req_protocol = request_line.strip().split(" ", 2)
- except ValueError:
- self.simple_response(400, "Malformed Request-Line")
- return
-
- environ["REQUEST_METHOD"] = method
-
- # path may be an abs_path (including "http://host.domain.tld");
- scheme, location, path, params, qs, frag = urlparse(path)
-
- if frag:
- self.simple_response("400 Bad Request",
- "Illegal #fragment in Request-URI.")
- return
-
- if scheme:
- environ["wsgi.url_scheme"] = scheme
- if params:
- path = path + ";" + params
-
- environ["SCRIPT_NAME"] = ""
-
- # Unquote the path+params (e.g. "/this%20path" -> "this path").
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
- #
- # But note that "...a URI must be separated into its components
- # before the escaped characters within those components can be
- # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
- atoms = [unquote(x) for x in quoted_slash.split(path)]
- path = "%2F".join(atoms)
- environ["PATH_INFO"] = path
-
- # Note that, like wsgiref and most other WSGI servers,
- # we unquote the path but not the query string.
- environ["QUERY_STRING"] = qs
-
- # Compare request and server HTTP protocol versions, in case our
- # server does not support the requested protocol. Limit our output
- # to min(req, server). We want the following output:
- # request server actual written supported response
- # protocol protocol response protocol feature set
- # a 1.0 1.0 1.0 1.0
- # b 1.0 1.1 1.1 1.0
- # c 1.1 1.0 1.0 1.0
- # d 1.1 1.1 1.1 1.1
- # Notice that, in (b), the response will be "HTTP/1.1" even though
- # the client only understands 1.0. RFC 2616 10.5.6 says we should
- # only return 505 if the _major_ version is different.
- rp = int(req_protocol[5]), int(req_protocol[7])
- server_protocol = environ["ACTUAL_SERVER_PROTOCOL"]
- sp = int(server_protocol[5]), int(server_protocol[7])
- if sp[0] != rp[0]:
- self.simple_response("505 HTTP Version Not Supported")
- return
- # Bah. "SERVER_PROTOCOL" is actually the REQUEST protocol.
- environ["SERVER_PROTOCOL"] = req_protocol
- self.response_protocol = "HTTP/%s.%s" % min(rp, sp)
-
- # If the Request-URI was an absoluteURI, use its location atom.
- if location:
- environ["SERVER_NAME"] = location
-
- # then all the http headers
- try:
- self.read_headers()
- except ValueError, ex:
- self.simple_response("400 Bad Request", repr(ex.args))
- return
-
- mrbs = self.max_request_body_size
- if mrbs and int(environ.get("CONTENT_LENGTH", 0)) > mrbs:
- self.simple_response("413 Request Entity Too Large")
- return
-
- # Persistent connection support
- if self.response_protocol == "HTTP/1.1":
- # Both server and client are HTTP/1.1
- if environ.get("HTTP_CONNECTION", "") == "close":
- self.close_connection = True
- else:
- # Either the server or client (or both) are HTTP/1.0
- if environ.get("HTTP_CONNECTION", "") != "Keep-Alive":
- self.close_connection = True
-
- # Transfer-Encoding support
- te = None
- if self.response_protocol == "HTTP/1.1":
- te = environ.get("HTTP_TRANSFER_ENCODING")
- if te:
- te = [x.strip().lower() for x in te.split(",") if x.strip()]
-
- self.chunked_read = False
-
- if te:
- for enc in te:
- if enc == "chunked":
- self.chunked_read = True
- else:
- # Note that, even if we see "chunked", we must reject
- # if there is an extension we don't recognize.
- self.simple_response("501 Unimplemented")
- self.close_connection = True
- return
-
- # From PEP 333:
- # "Servers and gateways that implement HTTP 1.1 must provide
- # transparent support for HTTP 1.1's "expect/continue" mechanism.
- # This may be done in any of several ways:
- # 1. Respond to requests containing an Expect: 100-continue request
- # with an immediate "100 Continue" response, and proceed normally.
- # 2. Proceed with the request normally, but provide the application
- # with a wsgi.input stream that will send the "100 Continue"
- # response if/when the application first attempts to read from
- # the input stream. The read request must then remain blocked
- # until the client responds.
- # 3. Wait until the client decides that the server does not support
- # expect/continue, and sends the request body on its own.
- # (This is suboptimal, and is not recommended.)
- #
- # We used to do 3, but are now doing 1. Maybe we'll do 2 someday,
- # but it seems like it would be a big slowdown for such a rare case.
- if environ.get("HTTP_EXPECT", "") == "100-continue":
- self.simple_response(100)
-
- self.ready = True
-
- def read_headers(self):
- """Read header lines from the incoming stream."""
- environ = self.environ
-
- while True:
- line = self.rfile.readline()
- if not line:
- # No more data--illegal end of headers
- raise ValueError("Illegal end of headers.")
-
- if line == '\r\n':
- # Normal end of headers
- break
-
- if line[0] in ' \t':
- # It's a continuation line.
- v = line.strip()
- else:
- k, v = line.split(":", 1)
- k, v = k.strip().upper(), v.strip()
- envname = "HTTP_" + k.replace("-", "_")
-
- if k in comma_separated_headers:
- existing = environ.get(envname)
- if existing:
- v = ", ".join((existing, v))
- environ[envname] = v
-
- ct = environ.pop("HTTP_CONTENT_TYPE", None)
- if ct is not None:
- environ["CONTENT_TYPE"] = ct
- cl = environ.pop("HTTP_CONTENT_LENGTH", None)
- if cl is not None:
- environ["CONTENT_LENGTH"] = cl
-
- def decode_chunked(self):
- """Decode the 'chunked' transfer coding."""
- cl = 0
- data = StringIO.StringIO()
- while True:
- line = self.rfile.readline().strip().split(";", 1)
- chunk_size = int(line.pop(0), 16)
- if chunk_size <= 0:
- break
-## if line: chunk_extension = line[0]
- cl += chunk_size
- data.write(self.rfile.read(chunk_size))
- crlf = self.rfile.read(2)
- if crlf != "\r\n":
- self.simple_response("400 Bad Request",
- "Bad chunked transfer coding "
- "(expected '\\r\\n', got %r)" % crlf)
- return
-
- # Grab any trailer headers
- self.read_headers()
-
- data.seek(0)
- self.environ["wsgi.input"] = data
- self.environ["CONTENT_LENGTH"] = str(cl) or ""
- return True
-
- def respond(self):
- """Call the appropriate WSGI app and write its iterable output."""
- # Set rfile.maxlen to ensure we don't read past Content-Length.
- # This will also be used to read the entire request body if errors
- # are raised before the app can read the body.
- if self.chunked_read:
- # If chunked, Content-Length will be 0.
- self.rfile.maxlen = self.max_request_body_size
- else:
- cl = int(self.environ.get("CONTENT_LENGTH", 0))
- if self.max_request_body_size:
- self.rfile.maxlen = min(cl, self.max_request_body_size)
- else:
- self.rfile.maxlen = cl
- self.rfile.bytes_read = 0
-
- try:
- self._respond()
- except MaxSizeExceeded:
- if not self.sent_headers:
- self.simple_response("413 Request Entity Too Large")
- return
-
- def _respond(self):
- if self.chunked_read:
- if not self.decode_chunked():
- self.close_connection = True
- return
-
- response = self.wsgi_app(self.environ, self.start_response)
- try:
- for chunk in response:
- # "The start_response callable must not actually transmit
- # the response headers. Instead, it must store them for the
- # server or gateway to transmit only after the first
- # iteration of the application return value that yields
- # a NON-EMPTY string, or upon the application's first
- # invocation of the write() callable." (PEP 333)
- if chunk:
- self.write(chunk)
- finally:
- if hasattr(response, "close"):
- response.close()
-
- if (self.ready and not self.sent_headers):
- self.sent_headers = True
- self.send_headers()
- if self.chunked_write:
- self.wfile.sendall("0\r\n\r\n")
-
- def simple_response(self, status, msg=""):
- """Write a simple response back to the client."""
- status = str(status)
- buf = ["%s %s\r\n" % (self.environ['ACTUAL_SERVER_PROTOCOL'], status),
- "Content-Length: %s\r\n" % len(msg),
- "Content-Type: text/plain\r\n"]
-
- if status[:3] == "413" and self.response_protocol == 'HTTP/1.1':
- # Request Entity Too Large
- self.close_connection = True
- buf.append("Connection: close\r\n")
-
- buf.append("\r\n")
- if msg:
- buf.append(msg)
-
- try:
- self.wfile.sendall("".join(buf))
- except socket.error, x:
- if x.args[0] not in socket_errors_to_ignore:
- raise
-
- def start_response(self, status, headers, exc_info = None):
- """WSGI callable to begin the HTTP response."""
- # "The application may call start_response more than once,
- # if and only if the exc_info argument is provided."
- if self.started_response and not exc_info:
- raise AssertionError("WSGI start_response called a second "
- "time with no exc_info.")
-
- # "if exc_info is provided, and the HTTP headers have already been
- # sent, start_response must raise an error, and should raise the
- # exc_info tuple."
- if self.sent_headers:
- try:
- raise exc_info[0], exc_info[1], exc_info[2]
- finally:
- exc_info = None
-
- self.started_response = True
- self.status = status
- self.outheaders.extend(headers)
- return self.write
-
- def write(self, chunk):
- """WSGI callable to write unbuffered data to the client.
-
- This method is also used internally by start_response (to write
- data from the iterable returned by the WSGI application).
- """
- if not self.started_response:
- raise AssertionError("WSGI write called before start_response.")
-
- if not self.sent_headers:
- self.sent_headers = True
- self.send_headers()
-
- if self.chunked_write and chunk:
- buf = [hex(len(chunk))[2:], "\r\n", chunk, "\r\n"]
- self.wfile.sendall("".join(buf))
- else:
- self.wfile.sendall(chunk)
-
- def send_headers(self):
- """Assert, process, and send the HTTP response message-headers."""
- hkeys = [key.lower() for key, value in self.outheaders]
- status = int(self.status[:3])
-
- if status == 413:
- # Request Entity Too Large. Close conn to avoid garbage.
- self.close_connection = True
- elif "content-length" not in hkeys:
- # "All 1xx (informational), 204 (no content),
- # and 304 (not modified) responses MUST NOT
- # include a message-body." So no point chunking.
- if status < 200 or status in (204, 205, 304):
- pass
- else:
- if (self.response_protocol == 'HTTP/1.1'
- and self.environ["REQUEST_METHOD"] != 'HEAD'):
- # Use the chunked transfer-coding
- self.chunked_write = True
- self.outheaders.append(("Transfer-Encoding", "chunked"))
- else:
- # Closing the conn is the only way to determine len.
- self.close_connection = True
-
- if "connection" not in hkeys:
- if self.response_protocol == 'HTTP/1.1':
- # Both server and client are HTTP/1.1 or better
- if self.close_connection:
- self.outheaders.append(("Connection", "close"))
- else:
- # Server and/or client are HTTP/1.0
- if not self.close_connection:
- self.outheaders.append(("Connection", "Keep-Alive"))
-
- if (not self.close_connection) and (not self.chunked_read):
- # Read any remaining request body data on the socket.
- # "If an origin server receives a request that does not include an
- # Expect request-header field with the "100-continue" expectation,
- # the request includes a request body, and the server responds
- # with a final status code before reading the entire request body
- # from the transport connection, then the server SHOULD NOT close
- # the transport connection until it has read the entire request,
- # or until the client closes the connection. Otherwise, the client
- # might not reliably receive the response message. However, this
- # requirement is not be construed as preventing a server from
- # defending itself against denial-of-service attacks, or from
- # badly broken client implementations."
- size = self.rfile.maxlen - self.rfile.bytes_read
- if size > 0:
- self.rfile.read(size)
-
- if "date" not in hkeys:
- self.outheaders.append(("Date", rfc822.formatdate()))
-
- if "server" not in hkeys:
- self.outheaders.append(("Server", self.environ['SERVER_SOFTWARE']))
-
- buf = [self.environ['ACTUAL_SERVER_PROTOCOL'], " ", self.status, "\r\n"]
- try:
- buf += [k + ": " + v + "\r\n" for k, v in self.outheaders]
- except TypeError:
- if not isinstance(k, str):
- raise TypeError("WSGI response header key %r is not a string.")
- if not isinstance(v, str):
- raise TypeError("WSGI response header value %r is not a string.")
- else:
- raise
- buf.append("\r\n")
- self.wfile.sendall("".join(buf))
-
-
-class NoSSLError(Exception):
- """Exception raised when a client speaks HTTP to an HTTPS socket."""
- pass
-
-
-class FatalSSLAlert(Exception):
- """Exception raised when the SSL implementation signals a fatal alert."""
- pass
-
-
-if not _fileobject_uses_str_type:
- class CP_fileobject(socket._fileobject):
- """Faux file object attached to a socket object."""
-
- def sendall(self, data):
- """Sendall for non-blocking sockets."""
- while data:
- try:
- bytes_sent = self.send(data)
- data = data[bytes_sent:]
- except socket.error, e:
- if e.args[0] not in socket_errors_nonblocking:
- raise
-
- def send(self, data):
- return self._sock.send(data)
-
- def flush(self):
- if self._wbuf:
- buffer = "".join(self._wbuf)
- self._wbuf = []
- self.sendall(buffer)
-
- def recv(self, size):
- while True:
- try:
- return self._sock.recv(size)
- except socket.error, e:
- if (e.args[0] not in socket_errors_nonblocking
- and e.args[0] not in socket_error_eintr):
- raise
-
- def read(self, size=-1):
- # Use max, disallow tiny reads in a loop as they are very inefficient.
- # We never leave read() with any leftover data from a new recv() call
- # in our internal buffer.
- rbufsize = max(self._rbufsize, self.default_bufsize)
- # Our use of StringIO rather than lists of string objects returned by
- # recv() minimizes memory usage and fragmentation that occurs when
- # rbufsize is large compared to the typical return value of recv().
- buf = self._rbuf
- buf.seek(0, 2) # seek end
- if size < 0:
- # Read until EOF
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
- while True:
- data = self.recv(rbufsize)
- if not data:
- break
- buf.write(data)
- return buf.getvalue()
- else:
- # Read until size bytes or EOF seen, whichever comes first
- buf_len = buf.tell()
- if buf_len >= size:
- # Already have size bytes in our buffer? Extract and return.
- buf.seek(0)
- rv = buf.read(size)
- self._rbuf = StringIO.StringIO()
- self._rbuf.write(buf.read())
- return rv
-
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
- while True:
- left = size - buf_len
- # recv() will malloc the amount of memory given as its
- # parameter even though it often returns much less data
- # than that. The returned data string is short lived
- # as we copy it into a StringIO and free it. This avoids
- # fragmentation issues on many platforms.
- data = self.recv(left)
- if not data:
- break
- n = len(data)
- if n == size and not buf_len:
- # Shortcut. Avoid buffer data copies when:
- # - We have no data in our buffer.
- # AND
- # - Our call to recv returned exactly the
- # number of bytes we were asked to read.
- return data
- if n == left:
- buf.write(data)
- del data # explicit free
- break
- assert n <= left, "recv(%d) returned %d bytes" % (left, n)
- buf.write(data)
- buf_len += n
- del data # explicit free
- #assert buf_len == buf.tell()
- return buf.getvalue()
-
- def readline(self, size=-1):
- buf = self._rbuf
- buf.seek(0, 2) # seek end
- if buf.tell() > 0:
- # check if we already have it in our buffer
- buf.seek(0)
- bline = buf.readline(size)
- if bline.endswith('\n') or len(bline) == size:
- self._rbuf = StringIO.StringIO()
- self._rbuf.write(buf.read())
- return bline
- del bline
- if size < 0:
- # Read until \n or EOF, whichever comes first
- if self._rbufsize <= 1:
- # Speed up unbuffered case
- buf.seek(0)
- buffers = [buf.read()]
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
- data = None
- recv = self.recv
- while data != "\n":
- data = recv(1)
- if not data:
- break
- buffers.append(data)
- return "".join(buffers)
-
- buf.seek(0, 2) # seek end
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
- while True:
- data = self.recv(self._rbufsize)
- if not data:
- break
- nl = data.find('\n')
- if nl >= 0:
- nl += 1
- buf.write(data[:nl])
- self._rbuf.write(data[nl:])
- del data
- break
- buf.write(data)
- return buf.getvalue()
- else:
- # Read until size bytes or \n or EOF seen, whichever comes first
- buf.seek(0, 2) # seek end
- buf_len = buf.tell()
- if buf_len >= size:
- buf.seek(0)
- rv = buf.read(size)
- self._rbuf = StringIO.StringIO()
- self._rbuf.write(buf.read())
- return rv
- self._rbuf = StringIO.StringIO() # reset _rbuf. we consume it via buf.
- while True:
- data = self.recv(self._rbufsize)
- if not data:
- break
- left = size - buf_len
- # did we just receive a newline?
- nl = data.find('\n', 0, left)
- if nl >= 0:
- nl += 1
- # save the excess data to _rbuf
- self._rbuf.write(data[nl:])
- if buf_len:
- buf.write(data[:nl])
- break
- else:
- # Shortcut. Avoid data copy through buf when returning
- # a substring of our first recv().
- return data[:nl]
- n = len(data)
- if n == size and not buf_len:
- # Shortcut. Avoid data copy through buf when
- # returning exactly all of our first recv().
- return data
- if n >= left:
- buf.write(data[:left])
- self._rbuf.write(data[left:])
- break
- buf.write(data)
- buf_len += n
- #assert buf_len == buf.tell()
- return buf.getvalue()
-
-else:
- class CP_fileobject(socket._fileobject):
- """Faux file object attached to a socket object."""
-
- def sendall(self, data):
- """Sendall for non-blocking sockets."""
- while data:
- try:
- bytes_sent = self.send(data)
- data = data[bytes_sent:]
- except socket.error, e:
- if e.args[0] not in socket_errors_nonblocking:
- raise
-
- def send(self, data):
- return self._sock.send(data)
-
- def flush(self):
- if self._wbuf:
- buffer = "".join(self._wbuf)
- self._wbuf = []
- self.sendall(buffer)
-
- def recv(self, size):
- while True:
- try:
- return self._sock.recv(size)
- except socket.error, e:
- if (e.args[0] not in socket_errors_nonblocking
- and e.args[0] not in socket_error_eintr):
- raise
-
- def read(self, size=-1):
- if size < 0:
- # Read until EOF
- buffers = [self._rbuf]
- self._rbuf = ""
- if self._rbufsize <= 1:
- recv_size = self.default_bufsize
- else:
- recv_size = self._rbufsize
-
- while True:
- data = self.recv(recv_size)
- if not data:
- break
- buffers.append(data)
- return "".join(buffers)
- else:
- # Read until size bytes or EOF seen, whichever comes first
- data = self._rbuf
- buf_len = len(data)
- if buf_len >= size:
- self._rbuf = data[size:]
- return data[:size]
- buffers = []
- if data:
- buffers.append(data)
- self._rbuf = ""
- while True:
- left = size - buf_len
- recv_size = max(self._rbufsize, left)
- data = self.recv(recv_size)
- if not data:
- break
- buffers.append(data)
- n = len(data)
- if n >= left:
- self._rbuf = data[left:]
- buffers[-1] = data[:left]
- break
- buf_len += n
- return "".join(buffers)
-
- def readline(self, size=-1):
- data = self._rbuf
- if size < 0:
- # Read until \n or EOF, whichever comes first
- if self._rbufsize <= 1:
- # Speed up unbuffered case
- assert data == ""
- buffers = []
- while data != "\n":
- data = self.recv(1)
- if not data:
- break
- buffers.append(data)
- return "".join(buffers)
- nl = data.find('\n')
- if nl >= 0:
- nl += 1
- self._rbuf = data[nl:]
- return data[:nl]
- buffers = []
- if data:
- buffers.append(data)
- self._rbuf = ""
- while True:
- data = self.recv(self._rbufsize)
- if not data:
- break
- buffers.append(data)
- nl = data.find('\n')
- if nl >= 0:
- nl += 1
- self._rbuf = data[nl:]
- buffers[-1] = data[:nl]
- break
- return "".join(buffers)
- else:
- # Read until size bytes or \n or EOF seen, whichever comes first
- nl = data.find('\n', 0, size)
- if nl >= 0:
- nl += 1
- self._rbuf = data[nl:]
- return data[:nl]
- buf_len = len(data)
- if buf_len >= size:
- self._rbuf = data[size:]
- return data[:size]
- buffers = []
- if data:
- buffers.append(data)
- self._rbuf = ""
- while True:
- data = self.recv(self._rbufsize)
- if not data:
- break
- buffers.append(data)
- left = size - buf_len
- nl = data.find('\n', 0, left)
- if nl >= 0:
- nl += 1
- self._rbuf = data[nl:]
- buffers[-1] = data[:nl]
- break
- n = len(data)
- if n >= left:
- self._rbuf = data[left:]
- buffers[-1] = data[:left]
- break
- buf_len += n
- return "".join(buffers)
-
-
-class SSL_fileobject(CP_fileobject):
- """SSL file object attached to a socket object."""
-
- ssl_timeout = 3
- ssl_retry = .01
-
- def _safe_call(self, is_reader, call, *args, **kwargs):
- """Wrap the given call with SSL error-trapping.
-
- is_reader: if False EOF errors will be raised. If True, EOF errors
- will return "" (to emulate normal sockets).
- """
- start = time.time()
- while True:
- try:
- return call(*args, **kwargs)
- except SSL.WantReadError:
- # Sleep and try again. This is dangerous, because it means
- # the rest of the stack has no way of differentiating
- # between a "new handshake" error and "client dropped".
- # Note this isn't an endless loop: there's a timeout below.
- time.sleep(self.ssl_retry)
- except SSL.WantWriteError:
- time.sleep(self.ssl_retry)
- except SSL.SysCallError, e:
- if is_reader and e.args == (-1, 'Unexpected EOF'):
- return ""
-
- errnum = e.args[0]
- if is_reader and errnum in socket_errors_to_ignore:
- return ""
- raise socket.error(errnum)
- except SSL.Error, e:
- if is_reader and e.args == (-1, 'Unexpected EOF'):
- return ""
-
- thirdarg = None
- try:
- thirdarg = e.args[0][0][2]
- except IndexError:
- pass
-
- if thirdarg == 'http request':
- # The client is talking HTTP to an HTTPS server.
- raise NoSSLError()
- raise FatalSSLAlert(*e.args)
- except:
- raise
-
- if time.time() - start > self.ssl_timeout:
- raise socket.timeout("timed out")
-
- def recv(self, *args, **kwargs):
- buf = []
- r = super(SSL_fileobject, self).recv
- while True:
- data = self._safe_call(True, r, *args, **kwargs)
- buf.append(data)
- p = self._sock.pending()
- if not p:
- return "".join(buf)
-
- def sendall(self, *args, **kwargs):
- return self._safe_call(False, super(SSL_fileobject, self).sendall, *args, **kwargs)
-
- def send(self, *args, **kwargs):
- return self._safe_call(False, super(SSL_fileobject, self).send, *args, **kwargs)
-
-
-class HTTPConnection(object):
- """An HTTP connection (active socket).
-
- socket: the raw socket object (usually TCP) for this connection.
- wsgi_app: the WSGI application for this server/connection.
- environ: a WSGI environ template. This will be copied for each request.
-
- rfile: a fileobject for reading from the socket.
- send: a function for writing (+ flush) to the socket.
- """
-
- rbufsize = -1
- RequestHandlerClass = HTTPRequest
- environ = {"wsgi.version": (1, 0),
- "wsgi.url_scheme": "http",
- "wsgi.multithread": True,
- "wsgi.multiprocess": False,
- "wsgi.run_once": False,
- "wsgi.errors": sys.stderr,
- }
-
- def __init__(self, sock, wsgi_app, environ):
- self.socket = sock
- self.wsgi_app = wsgi_app
-
- # Copy the class environ into self.
- self.environ = self.environ.copy()
- self.environ.update(environ)
-
- if SSL and isinstance(sock, SSL.ConnectionType):
- timeout = sock.gettimeout()
- self.rfile = SSL_fileobject(sock, "rb", self.rbufsize)
- self.rfile.ssl_timeout = timeout
- self.wfile = SSL_fileobject(sock, "wb", -1)
- self.wfile.ssl_timeout = timeout
- else:
- self.rfile = CP_fileobject(sock, "rb", self.rbufsize)
- self.wfile = CP_fileobject(sock, "wb", -1)
-
- # Wrap wsgi.input but not HTTPConnection.rfile itself.
- # We're also not setting maxlen yet; we'll do that separately
- # for headers and body for each iteration of self.communicate
- # (if maxlen is 0 the wrapper doesn't check length).
- self.environ["wsgi.input"] = SizeCheckWrapper(self.rfile, 0)
-
- def communicate(self):
- """Read each request and respond appropriately."""
- try:
- while True:
- # (re)set req to None so that if something goes wrong in
- # the RequestHandlerClass constructor, the error doesn't
- # get written to the previous request.
- req = None
- req = self.RequestHandlerClass(self.wfile, self.environ,
- self.wsgi_app)
-
- # This order of operations should guarantee correct pipelining.
- req.parse_request()
- if not req.ready:
- return
-
- req.respond()
- if req.close_connection:
- return
-
- except socket.error, e:
- errnum = e.args[0]
- if errnum == 'timed out':
- if req and not req.sent_headers:
- req.simple_response("408 Request Timeout")
- elif errnum not in socket_errors_to_ignore:
- if req and not req.sent_headers:
- req.simple_response("500 Internal Server Error",
- format_exc())
- return
- except (KeyboardInterrupt, SystemExit):
- raise
- except FatalSSLAlert, e:
- # Close the connection.
- return
- except NoSSLError:
- if req and not req.sent_headers:
- # Unwrap our wfile
- req.wfile = CP_fileobject(self.socket._sock, "wb", -1)
- req.simple_response("400 Bad Request",
- "The client sent a plain HTTP request, but "
- "this server only speaks HTTPS on this port.")
- self.linger = True
- except Exception, e:
- if req and not req.sent_headers:
- req.simple_response("500 Internal Server Error", format_exc())
-
- linger = False
-
- def close(self):
- """Close the socket underlying this connection."""
- self.rfile.close()
-
- if not self.linger:
- # Python's socket module does NOT call close on the kernel socket
- # when you call socket.close(). We do so manually here because we
- # want this server to send a FIN TCP segment immediately. Note this
- # must be called *before* calling socket.close(), because the latter
- # drops its reference to the kernel socket.
- self.socket._sock.close()
- self.socket.close()
- else:
- # On the other hand, sometimes we want to hang around for a bit
- # to make sure the client has a chance to read our entire
- # response. Skipping the close() calls here delays the FIN
- # packet until the socket object is garbage-collected later.
- # Someday, perhaps, we'll do the full lingering_close that
- # Apache does, but not today.
- pass
-
-
-def format_exc(limit=None):
- """Like print_exc() but return a string. Backport for Python 2.3."""
- try:
- etype, value, tb = sys.exc_info()
- return ''.join(traceback.format_exception(etype, value, tb, limit))
- finally:
- etype = value = tb = None
-
-
-_SHUTDOWNREQUEST = None
-
-class WorkerThread(threading.Thread):
- """Thread which continuously polls a Queue for Connection objects.
-
- server: the HTTP Server which spawned this thread, and which owns the
- Queue and is placing active connections into it.
- ready: a simple flag for the calling server to know when this thread
- has begun polling the Queue.
-
- Due to the timing issues of polling a Queue, a WorkerThread does not
- check its own 'ready' flag after it has started. To stop the thread,
- it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue
- (one for each running WorkerThread).
- """
-
- conn = None
-
- def __init__(self, server):
- self.ready = False
- self.server = server
- threading.Thread.__init__(self)
-
- def run(self):
- try:
- self.ready = True
- while True:
- conn = self.server.requests.get()
- if conn is _SHUTDOWNREQUEST:
- return
-
- self.conn = conn
- try:
- conn.communicate()
- finally:
- conn.close()
- self.conn = None
- except (KeyboardInterrupt, SystemExit), exc:
- self.server.interrupt = exc
-
-
-class ThreadPool(object):
- """A Request Queue for the CherryPyWSGIServer which pools threads.
-
- ThreadPool objects must provide min, get(), put(obj), start()
- and stop(timeout) attributes.
- """
-
- def __init__(self, server, min=10, max=-1):
- self.server = server
- self.min = min
- self.max = max
- self._threads = []
- self._queue = Queue.Queue()
- self.get = self._queue.get
-
- def start(self):
- """Start the pool of threads."""
- for i in xrange(self.min):
- self._threads.append(WorkerThread(self.server))
- for worker in self._threads:
- worker.setName("CP WSGIServer " + worker.getName())
- worker.start()
- for worker in self._threads:
- while not worker.ready:
- time.sleep(.1)
-
- def _get_idle(self):
- """Number of worker threads which are idle. Read-only."""
- return len([t for t in self._threads if t.conn is None])
- idle = property(_get_idle, doc=_get_idle.__doc__)
-
- def put(self, obj):
- self._queue.put(obj)
- if obj is _SHUTDOWNREQUEST:
- return
-
- def grow(self, amount):
- """Spawn new worker threads (not above self.max)."""
- for i in xrange(amount):
- if self.max > 0 and len(self._threads) >= self.max:
- break
- worker = WorkerThread(self.server)
- worker.setName("CP WSGIServer " + worker.getName())
- self._threads.append(worker)
- worker.start()
-
- def shrink(self, amount):
- """Kill off worker threads (not below self.min)."""
- # Grow/shrink the pool if necessary.
- # Remove any dead threads from our list
- for t in self._threads:
- if not t.isAlive():
- self._threads.remove(t)
- amount -= 1
-
- if amount > 0:
- for i in xrange(min(amount, len(self._threads) - self.min)):
- # Put a number of shutdown requests on the queue equal
- # to 'amount'. Once each of those is processed by a worker,
- # that worker will terminate and be culled from our list
- # in self.put.
- self._queue.put(_SHUTDOWNREQUEST)
-
- def stop(self, timeout=5):
- # Must shut down threads here so the code that calls
- # this method can know when all threads are stopped.
- for worker in self._threads:
- self._queue.put(_SHUTDOWNREQUEST)
-
- # Don't join currentThread (when stop is called inside a request).
- current = threading.currentThread()
- while self._threads:
- worker = self._threads.pop()
- if worker is not current and worker.isAlive():
- try:
- if timeout is None or timeout < 0:
- worker.join()
- else:
- worker.join(timeout)
- if worker.isAlive():
- # We exhausted the timeout.
- # Forcibly shut down the socket.
- c = worker.conn
- if c and not c.rfile.closed:
- if SSL and isinstance(c.socket, SSL.ConnectionType):
- # pyOpenSSL.socket.shutdown takes no args
- c.socket.shutdown()
- else:
- c.socket.shutdown(socket.SHUT_RD)
- worker.join()
- except (AssertionError,
- # Ignore repeated Ctrl-C.
- # See http://www.cherrypy.org/ticket/691.
- KeyboardInterrupt), exc1:
- pass
-
-
-
-class SSLConnection:
- """A thread-safe wrapper for an SSL.Connection.
-
- *args: the arguments to create the wrapped SSL.Connection(*args).
- """
-
- def __init__(self, *args):
- self._ssl_conn = SSL.Connection(*args)
- self._lock = threading.RLock()
-
- for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read',
- 'renegotiate', 'bind', 'listen', 'connect', 'accept',
- 'setblocking', 'fileno', 'shutdown', 'close', 'get_cipher_list',
- 'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
- 'makefile', 'get_app_data', 'set_app_data', 'state_string',
- 'sock_shutdown', 'get_peer_certificate', 'want_read',
- 'want_write', 'set_connect_state', 'set_accept_state',
- 'connect_ex', 'sendall', 'settimeout'):
- exec """def %s(self, *args):
- self._lock.acquire()
- try:
- return self._ssl_conn.%s(*args)
- finally:
- self._lock.release()
-""" % (f, f)
-
-
-try:
- import fcntl
-except ImportError:
- try:
- from ctypes import windll, WinError
- except ImportError:
- def prevent_socket_inheritance(sock):
- """Dummy function, since neither fcntl nor ctypes are available."""
- pass
- else:
- def prevent_socket_inheritance(sock):
- """Mark the given socket fd as non-inheritable (Windows)."""
- if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0):
- raise WinError()
-else:
- def prevent_socket_inheritance(sock):
- """Mark the given socket fd as non-inheritable (POSIX)."""
- fd = sock.fileno()
- old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
- fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
-
-
-class CherryPyWSGIServer(object):
- """An HTTP server for WSGI.
-
- bind_addr: The interface on which to listen for connections.
- For TCP sockets, a (host, port) tuple. Host values may be any IPv4
- or IPv6 address, or any valid hostname. The string 'localhost' is a
- synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
- The string '0.0.0.0' is a special IPv4 entry meaning "any active
- interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
- IPv6. The empty string or None are not allowed.
-
- For UNIX sockets, supply the filename as a string.
- wsgi_app: the WSGI 'application callable'; multiple WSGI applications
- may be passed as (path_prefix, app) pairs.
- numthreads: the number of worker threads to create (default 10).
- server_name: the string to set for WSGI's SERVER_NAME environ entry.
- Defaults to socket.gethostname().
- max: the maximum number of queued requests (defaults to -1 = no limit).
- request_queue_size: the 'backlog' argument to socket.listen();
- specifies the maximum number of queued connections (default 5).
- timeout: the timeout in seconds for accepted connections (default 10).
-
- nodelay: if True (the default since 3.1), sets the TCP_NODELAY socket
- option.
-
- protocol: the version string to write in the Status-Line of all
- HTTP responses. For example, "HTTP/1.1" (the default). This
- also limits the supported features used in the response.
-
-
- SSL/HTTPS
- ---------
- The OpenSSL module must be importable for SSL functionality.
- You can obtain it from http://pyopenssl.sourceforge.net/
-
- ssl_certificate: the filename of the server SSL certificate.
- ssl_privatekey: the filename of the server's private key file.
-
- If either of these is None (both are None by default), this server
- will not use SSL. If both are given and are valid, they will be read
- on server start and used in the SSL context for the listening socket.
- """
-
- protocol = "HTTP/1.1"
- _bind_addr = "127.0.0.1"
- version = "CherryPy/3.1.2"
- ready = False
- _interrupt = None
-
- nodelay = True
-
- ConnectionClass = HTTPConnection
- environ = {}
-
- # Paths to certificate and private key files
- ssl_certificate = None
- ssl_private_key = None
-
- def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
- max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
- self.requests = ThreadPool(self, min=numthreads or 1, max=max)
-
- if callable(wsgi_app):
- # We've been handed a single wsgi_app, in CP-2.1 style.
- # Assume it's mounted at "".
- self.wsgi_app = wsgi_app
- else:
- # We've been handed a list of (path_prefix, wsgi_app) tuples,
- # so that the server can call different wsgi_apps, and also
- # correctly set SCRIPT_NAME.
- warnings.warn("The ability to pass multiple apps is deprecated "
- "and will be removed in 3.2. You should explicitly "
- "include a WSGIPathInfoDispatcher instead.",
- DeprecationWarning)
- self.wsgi_app = WSGIPathInfoDispatcher(wsgi_app)
-
- self.bind_addr = bind_addr
- if not server_name:
- server_name = socket.gethostname()
- self.server_name = server_name
- self.request_queue_size = request_queue_size
-
- self.timeout = timeout
- self.shutdown_timeout = shutdown_timeout
-
- def _get_numthreads(self):
- return self.requests.min
- def _set_numthreads(self, value):
- self.requests.min = value
- numthreads = property(_get_numthreads, _set_numthreads)
-
- def __str__(self):
- return "%s.%s(%r)" % (self.__module__, self.__class__.__name__,
- self.bind_addr)
-
- def _get_bind_addr(self):
- return self._bind_addr
- def _set_bind_addr(self, value):
- if isinstance(value, tuple) and value[0] in ('', None):
- # Despite the socket module docs, using '' does not
- # allow AI_PASSIVE to work. Passing None instead
- # returns '0.0.0.0' like we want. In other words:
- # host AI_PASSIVE result
- # '' Y 192.168.x.y
- # '' N 192.168.x.y
- # None Y 0.0.0.0
- # None N 127.0.0.1
- # But since you can get the same effect with an explicit
- # '0.0.0.0', we deny both the empty string and None as values.
- raise ValueError("Host values of '' or None are not allowed. "
- "Use '0.0.0.0' (IPv4) or '::' (IPv6) instead "
- "to listen on all active interfaces.")
- self._bind_addr = value
- bind_addr = property(_get_bind_addr, _set_bind_addr,
- doc="""The interface on which to listen for connections.
-
- For TCP sockets, a (host, port) tuple. Host values may be any IPv4
- or IPv6 address, or any valid hostname. The string 'localhost' is a
- synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
- The string '0.0.0.0' is a special IPv4 entry meaning "any active
- interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
- IPv6. The empty string or None are not allowed.
-
- For UNIX sockets, supply the filename as a string.""")
-
- def start(self):
- """Run the server forever."""
- # We don't have to trap KeyboardInterrupt or SystemExit here,
- # because cherrpy.server already does so, calling self.stop() for us.
- # If you're using this server with another framework, you should
- # trap those exceptions in whatever code block calls start().
- self._interrupt = None
-
- # Select the appropriate socket
- if isinstance(self.bind_addr, basestring):
- # AF_UNIX socket
-
- # So we can reuse the socket...
- try: os.unlink(self.bind_addr)
- except: pass
-
- # So everyone can access the socket...
- try: os.chmod(self.bind_addr, 0777)
- except: pass
-
- info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
- else:
- # AF_INET or AF_INET6 socket
- # Get the correct address family for our host (allows IPv6 addresses)
- host, port = self.bind_addr
- try:
- info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
- socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
- except socket.gaierror:
- # Probably a DNS issue. Assume IPv4.
- info = [(socket.AF_INET, socket.SOCK_STREAM, 0, "", self.bind_addr)]
-
- self.socket = None
- msg = "No socket could be created"
- for res in info:
- af, socktype, proto, canonname, sa = res
- try:
- self.bind(af, socktype, proto)
- except socket.error, msg:
- if self.socket:
- self.socket.close()
- self.socket = None
- continue
- break
- if not self.socket:
- raise socket.error, msg
-
- # Timeout so KeyboardInterrupt can be caught on Win32
- self.socket.settimeout(1)
- self.socket.listen(self.request_queue_size)
-
- # Create worker threads
- self.requests.start()
-
- self.ready = True
- while self.ready:
- self.tick()
- if self.interrupt:
- while self.interrupt is True:
- # Wait for self.stop() to complete. See _set_interrupt.
- time.sleep(0.1)
- if self.interrupt:
- raise self.interrupt
-
- def bind(self, family, type, proto=0):
- """Create (or recreate) the actual socket object."""
- self.socket = socket.socket(family, type, proto)
- prevent_socket_inheritance(self.socket)
- self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- if self.nodelay:
- self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
- if self.ssl_certificate and self.ssl_private_key:
- if SSL is None:
- raise ImportError("You must install pyOpenSSL to use HTTPS.")
-
- # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- ctx.use_privatekey_file(self.ssl_private_key)
- ctx.use_certificate_file(self.ssl_certificate)
- self.socket = SSLConnection(ctx, self.socket)
- self.populate_ssl_environ()
-
- # If listening on the IPV6 any address ('::' = IN6ADDR_ANY),
- # activate dual-stack. See http://www.cherrypy.org/ticket/871.
- if (not isinstance(self.bind_addr, basestring)
- and self.bind_addr[0] == '::' and family == socket.AF_INET6):
- try:
- self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
- except (AttributeError, socket.error):
- # Apparently, the socket option is not available in
- # this machine's TCP stack
- pass
-
- self.socket.bind(self.bind_addr)
-
- def tick(self):
- """Accept a new connection and put it on the Queue."""
- try:
- s, addr = self.socket.accept()
- prevent_socket_inheritance(s)
- if not self.ready:
- return
- if hasattr(s, 'settimeout'):
- s.settimeout(self.timeout)
-
- environ = self.environ.copy()
- # SERVER_SOFTWARE is common for IIS. It's also helpful for
- # us to pass a default value for the "Server" response header.
- if environ.get("SERVER_SOFTWARE") is None:
- environ["SERVER_SOFTWARE"] = "%s WSGI Server" % self.version
- # set a non-standard environ entry so the WSGI app can know what
- # the *real* server protocol is (and what features to support).
- # See http://www.faqs.org/rfcs/rfc2145.html.
- environ["ACTUAL_SERVER_PROTOCOL"] = self.protocol
- environ["SERVER_NAME"] = self.server_name
-
- if isinstance(self.bind_addr, basestring):
- # AF_UNIX. This isn't really allowed by WSGI, which doesn't
- # address unix domain sockets. But it's better than nothing.
- environ["SERVER_PORT"] = ""
- else:
- environ["SERVER_PORT"] = str(self.bind_addr[1])
- # optional values
- # Until we do DNS lookups, omit REMOTE_HOST
- environ["REMOTE_ADDR"] = addr[0]
- environ["REMOTE_PORT"] = str(addr[1])
-
- conn = self.ConnectionClass(s, self.wsgi_app, environ)
- self.requests.put(conn)
- except socket.timeout:
- # The only reason for the timeout in start() is so we can
- # notice keyboard interrupts on Win32, which don't interrupt
- # accept() by default
- return
- except socket.error, x:
- if x.args[0] in socket_error_eintr:
- # I *think* this is right. EINTR should occur when a signal
- # is received during the accept() call; all docs say retry
- # the call, and I *think* I'm reading it right that Python
- # will then go ahead and poll for and handle the signal
- # elsewhere. See http://www.cherrypy.org/ticket/707.
- return
- if x.args[0] in socket_errors_nonblocking:
- # Just try again. See http://www.cherrypy.org/ticket/479.
- return
- if x.args[0] in socket_errors_to_ignore:
- # Our socket was closed.
- # See http://www.cherrypy.org/ticket/686.
- return
- raise
-
- def _get_interrupt(self):
- return self._interrupt
- def _set_interrupt(self, interrupt):
- self._interrupt = True
- self.stop()
- self._interrupt = interrupt
- interrupt = property(_get_interrupt, _set_interrupt,
- doc="Set this to an Exception instance to "
- "interrupt the server.")
-
- def stop(self):
- """Gracefully shutdown a server that is serving forever."""
- self.ready = False
-
- sock = getattr(self, "socket", None)
- if sock:
- if not isinstance(self.bind_addr, basestring):
- # Touch our own socket to make accept() return immediately.
- try:
- host, port = sock.getsockname()[:2]
- except socket.error, x:
- if x.args[0] not in socket_errors_to_ignore:
- raise
- else:
- # Note that we're explicitly NOT using AI_PASSIVE,
- # here, because we want an actual IP to touch.
- # localhost won't work if we've bound to a public IP,
- # but it will if we bound to '0.0.0.0' (INADDR_ANY).
- for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
- socket.SOCK_STREAM):
- af, socktype, proto, canonname, sa = res
- s = None
- try:
- s = socket.socket(af, socktype, proto)
- # See http://groups.google.com/group/cherrypy-users/
- # browse_frm/thread/bbfe5eb39c904fe0
- s.settimeout(1.0)
- s.connect((host, port))
- s.close()
- except socket.error:
- if s:
- s.close()
- if hasattr(sock, "close"):
- sock.close()
- self.socket = None
-
- self.requests.stop(self.shutdown_timeout)
-
- def populate_ssl_environ(self):
- """Create WSGI environ entries to be merged into each request."""
- cert = open(self.ssl_certificate, 'rb').read()
- cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
- ssl_environ = {
- "wsgi.url_scheme": "https",
- "HTTPS": "on",
- # pyOpenSSL doesn't provide access to any of these AFAICT
-## 'SSL_PROTOCOL': 'SSLv2',
-## SSL_CIPHER string The cipher specification name
-## SSL_VERSION_INTERFACE string The mod_ssl program version
-## SSL_VERSION_LIBRARY string The OpenSSL program version
- }
-
- # Server certificate attributes
- ssl_environ.update({
- 'SSL_SERVER_M_VERSION': cert.get_version(),
- 'SSL_SERVER_M_SERIAL': cert.get_serial_number(),
-## 'SSL_SERVER_V_START': Validity of server's certificate (start time),
-## 'SSL_SERVER_V_END': Validity of server's certificate (end time),
- })
-
- for prefix, dn in [("I", cert.get_issuer()),
- ("S", cert.get_subject())]:
- # X509Name objects don't seem to have a way to get the
- # complete DN string. Use str() and slice it instead,
- # because str(dn) == "<X509Name object '/C=US/ST=...'>"
- dnstr = str(dn)[18:-2]
-
- wsgikey = 'SSL_SERVER_%s_DN' % prefix
- ssl_environ[wsgikey] = dnstr
-
- # The DN should be of the form: /k1=v1/k2=v2, but we must allow
- # for any value to contain slashes itself (in a URL).
- while dnstr:
- pos = dnstr.rfind("=")
- dnstr, value = dnstr[:pos], dnstr[pos + 1:]
- pos = dnstr.rfind("/")
- dnstr, key = dnstr[:pos], dnstr[pos + 1:]
- if key and value:
- wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key)
- ssl_environ[wsgikey] = value
-
- self.environ.update(ssl_environ)
-
diff --git a/module/network/Browser.py b/module/network/Browser.py
deleted file mode 100644
index d68a23687..000000000
--- a/module/network/Browser.py
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from logging import getLogger
-
-from HTTPRequest import HTTPRequest
-from HTTPDownload import HTTPDownload
-
-
-class Browser(object):
- __slots__ = ("log", "options", "bucket", "cj", "_size", "http", "dl")
-
- def __init__(self, bucket=None, options={}):
- self.log = getLogger("log")
-
- self.options = options #holds pycurl options
- self.bucket = bucket
-
- self.cj = None # needs to be setted later
- self._size = 0
-
- self.renewHTTPRequest()
- self.dl = None
-
-
- def renewHTTPRequest(self):
- if hasattr(self, "http"): self.http.close()
- self.http = HTTPRequest(self.cj, self.options)
-
- def setLastURL(self, val):
- self.http.lastURL = val
-
- # tunnel some attributes from HTTP Request to Browser
- lastEffectiveURL = property(lambda self: self.http.lastEffectiveURL)
- lastURL = property(lambda self: self.http.lastURL, setLastURL)
- code = property(lambda self: self.http.code)
- cookieJar = property(lambda self: self.cj)
-
- def setCookieJar(self, cj):
- self.cj = cj
- self.http.cj = cj
-
- @property
- def speed(self):
- if self.dl:
- return self.dl.speed
- return 0
-
- @property
- def size(self):
- if self._size:
- return self._size
- if self.dl:
- return self.dl.size
- return 0
-
- @property
- def arrived(self):
- if self.dl:
- return self.dl.arrived
- return 0
-
- @property
- def percent(self):
- if not self.size: return 0
- return (self.arrived * 100) / self.size
-
- def clearCookies(self):
- if self.cj:
- self.cj.clear()
- self.http.clearCookies()
-
- def clearReferer(self):
- self.http.lastURL = None
-
- def abortDownloads(self):
- self.http.abort = True
- if self.dl:
- self._size = self.dl.size
- self.dl.abort = True
-
- def httpDownload(self, url, filename, get={}, post={}, ref=True, cookies=True, chunks=1, resume=False,
- progressNotify=None, disposition=False):
- """ this can also download ftp """
- self._size = 0
- self.dl = HTTPDownload(url, filename, get, post, self.lastEffectiveURL if ref else None,
- self.cj if cookies else None, self.bucket, self.options, progressNotify, disposition)
- name = self.dl.download(chunks, resume)
- self._size = self.dl.size
-
- self.dl = None
-
- return name
-
- def load(self, *args, **kwargs):
- """ retrieves page """
- return self.http.load(*args, **kwargs)
-
- def putHeader(self, name, value):
- """ add a header to the request """
- self.http.putHeader(name, value)
-
- def addAuth(self, pwd):
- """Adds user and pw for http auth
-
- :param pwd: string, user:password
- """
- self.options["auth"] = pwd
- self.renewHTTPRequest() #we need a new request
-
- def removeAuth(self):
- if "auth" in self.options: del self.options["auth"]
- self.renewHTTPRequest()
-
- def setOption(self, name, value):
- """Adds an option to the request, see HTTPRequest for existing ones"""
- self.options[name] = value
-
- def deleteOption(self, name):
- if name in self.options: del self.options[name]
-
- def clearHeaders(self):
- self.http.clearHeaders()
-
- def close(self):
- """ cleanup """
- if hasattr(self, "http"):
- self.http.close()
- del self.http
- if hasattr(self, "dl"):
- del self.dl
- if hasattr(self, "cj"):
- del self.cj
-
-if __name__ == "__main__":
- browser = Browser()#proxies={"socks5": "localhost:5000"})
- ip = "http://www.whatismyip.com/automation/n09230945.asp"
- #browser.getPage("http://google.com/search?q=bar")
- #browser.getPage("https://encrypted.google.com/")
- #print browser.getPage(ip)
- #print browser.getRedirectLocation("http://google.com/")
- #browser.getPage("https://encrypted.google.com/")
- #browser.getPage("http://google.com/search?q=bar")
-
- browser.httpDownload("http://speedtest.netcologne.de/test_10mb.bin", "test_10mb.bin")
-
diff --git a/module/network/Bucket.py b/module/network/Bucket.py
deleted file mode 100644
index 69da277ae..000000000
--- a/module/network/Bucket.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/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
-"""
-
-from time import time
-from threading import Lock
-
-class Bucket:
- def __init__(self):
- self.rate = 0
- self.tokens = 0
- self.timestamp = time()
- self.lock = Lock()
-
- def __nonzero__(self):
- return False if self.rate < 10240 else True
-
- def setRate(self, rate):
- self.lock.acquire()
- self.rate = int(rate)
- self.lock.release()
-
- def consumed(self, amount):
- """ return time the process have to sleep, after consumed specified amount """
- if self.rate < 10240: return 0 #min. 10kb, may become unresponsive otherwise
- self.lock.acquire()
-
- self.calc_tokens()
- self.tokens -= amount
-
- if self.tokens < 0:
- time = -self.tokens/float(self.rate)
- else:
- time = 0
-
-
- self.lock.release()
- return time
-
- def calc_tokens(self):
- if self.tokens < self.rate:
- now = time()
- delta = self.rate * (now - self.timestamp)
- self.tokens = min(self.rate, self.tokens + delta)
- self.timestamp = now
-
diff --git a/module/network/CookieJar.py b/module/network/CookieJar.py
deleted file mode 100644
index c05812334..000000000
--- a/module/network/CookieJar.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# -*- 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: mkaay, RaNaN
-"""
-
-from time import time
-
-class CookieJar():
- def __init__(self, pluginname, account=None):
- self.cookies = {}
- self.plugin = pluginname
- self.account = account
-
- def addCookies(self, clist):
- for c in clist:
- name = c.split("\t")[5]
- self.cookies[name] = c
-
- def getCookies(self):
- return self.cookies.values()
-
- def parseCookie(self, name):
- if name in self.cookies:
- return self.cookies[name].split("\t")[6]
- else:
- return None
-
- def getCookie(self, name):
- return self.parseCookie(name)
-
- def setCookie(self, domain, name, value, path="/", exp=time()+3600*24*180):
- s = ".%s TRUE %s FALSE %s %s %s" % (domain, path, exp, name, value)
- self.cookies[name] = s
-
- def clear(self):
- self.cookies = {}
diff --git a/module/network/HTTPChunk.py b/module/network/HTTPChunk.py
deleted file mode 100644
index b637aef32..000000000
--- a/module/network/HTTPChunk.py
+++ /dev/null
@@ -1,293 +0,0 @@
-#!/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
-"""
-from os import remove, stat, fsync
-from os.path import exists
-from time import sleep
-from re import search
-from module.utils import fs_encode
-import codecs
-import pycurl
-
-from HTTPRequest import HTTPRequest
-
-class WrongFormat(Exception):
- pass
-
-
-class ChunkInfo():
- def __init__(self, name):
- self.name = unicode(name)
- self.size = 0
- self.resume = False
- self.chunks = []
-
- def __repr__(self):
- ret = "ChunkInfo: %s, %s\n" % (self.name, self.size)
- for i, c in enumerate(self.chunks):
- ret += "%s# %s\n" % (i, c[1])
-
- return ret
-
- def setSize(self, size):
- self.size = int(size)
-
- def addChunk(self, name, range):
- self.chunks.append((name, range))
-
- def clear(self):
- self.chunks = []
-
- def createChunks(self, chunks):
- self.clear()
- chunk_size = self.size / chunks
-
- current = 0
- for i in range(chunks):
- end = self.size - 1 if (i == chunks - 1) else current + chunk_size
- self.addChunk("%s.chunk%s" % (self.name, i), (current, end))
- current += chunk_size + 1
-
-
- def save(self):
- fs_name = fs_encode("%s.chunks" % self.name)
- fh = codecs.open(fs_name, "w", "utf_8")
- fh.write("name:%s\n" % self.name)
- fh.write("size:%s\n" % self.size)
- for i, c in enumerate(self.chunks):
- fh.write("#%d:\n" % i)
- fh.write("\tname:%s\n" % c[0])
- fh.write("\trange:%i-%i\n" % c[1])
- fh.close()
-
- @staticmethod
- def load(name):
- fs_name = fs_encode("%s.chunks" % name)
- if not exists(fs_name):
- raise IOError()
- fh = codecs.open(fs_name, "r", "utf_8")
- name = fh.readline()[:-1]
- size = fh.readline()[:-1]
- if name.startswith("name:") and size.startswith("size:"):
- name = name[5:]
- size = size[5:]
- else:
- fh.close()
- raise WrongFormat()
- ci = ChunkInfo(name)
- ci.loaded = True
- ci.setSize(size)
- while True:
- if not fh.readline(): #skip line
- break
- name = fh.readline()[1:-1]
- range = fh.readline()[1:-1]
- if name.startswith("name:") and range.startswith("range:"):
- name = name[5:]
- range = range[6:].split("-")
- else:
- raise WrongFormat()
-
- ci.addChunk(name, (long(range[0]), long(range[1])))
- fh.close()
- return ci
-
- def remove(self):
- fs_name = fs_encode("%s.chunks" % self.name)
- if exists(fs_name): remove(fs_name)
-
- def getCount(self):
- return len(self.chunks)
-
- def getChunkName(self, index):
- return self.chunks[index][0]
-
- def getChunkRange(self, index):
- return self.chunks[index][1]
-
-
-class HTTPChunk(HTTPRequest):
- def __init__(self, id, parent, range=None, resume=False):
- self.id = id
- self.p = parent # HTTPDownload instance
- self.range = range # tuple (start, end)
- self.resume = resume
- self.log = parent.log
-
- self.size = range[1] - range[0] if range else -1
- self.arrived = 0
- self.lastURL = self.p.referer
-
- self.c = pycurl.Curl()
-
- self.header = ""
- self.headerParsed = False #indicates if the header has been processed
-
- self.fp = None #file handle
-
- self.initHandle()
- self.setInterface(self.p.options)
-
- self.BOMChecked = False # check and remove byte order mark
-
- self.rep = None
-
- self.sleep = 0.000
- self.lastSize = 0
-
- def __repr__(self):
- return "<HTTPChunk id=%d, size=%d, arrived=%d>" % (self.id, self.size, self.arrived)
-
- @property
- def cj(self):
- return self.p.cj
-
- def getHandle(self):
- """ returns a Curl handle ready to use for perform/multiperform """
-
- self.setRequestContext(self.p.url, self.p.get, self.p.post, self.p.referer, self.p.cj)
- self.c.setopt(pycurl.WRITEFUNCTION, self.writeBody)
- self.c.setopt(pycurl.HEADERFUNCTION, self.writeHeader)
-
- # request all bytes, since some servers in russia seems to have a defect arihmetic unit
-
- fs_name = fs_encode(self.p.info.getChunkName(self.id))
- if self.resume:
- self.fp = open(fs_name, "ab")
- self.arrived = self.fp.tell()
- if not self.arrived:
- self.arrived = stat(fs_name).st_size
-
- if self.range:
- #do nothing if chunk already finished
- if self.arrived + self.range[0] >= self.range[1]: return None
-
- if self.id == len(self.p.info.chunks) - 1: #as last chunk dont set end range, so we get everything
- range = "%i-" % (self.arrived + self.range[0])
- else:
- range = "%i-%i" % (self.arrived + self.range[0], min(self.range[1] + 1, self.p.size - 1))
-
- self.log.debug("Chunked resume with range %s" % range)
- self.c.setopt(pycurl.RANGE, range)
- else:
- self.log.debug("Resume File from %i" % self.arrived)
- self.c.setopt(pycurl.RESUME_FROM, self.arrived)
-
- else:
- if self.range:
- if self.id == len(self.p.info.chunks) - 1: # see above
- range = "%i-" % self.range[0]
- else:
- range = "%i-%i" % (self.range[0], min(self.range[1] + 1, self.p.size - 1))
-
- self.log.debug("Chunked with range %s" % range)
- self.c.setopt(pycurl.RANGE, range)
-
- self.fp = open(fs_name, "wb")
-
- return self.c
-
- def writeHeader(self, buf):
- self.header += buf
- #@TODO forward headers?, this is possibly unneeeded, when we just parse valid 200 headers
- # as first chunk, we will parse the headers
- if not self.range and self.header.endswith("\r\n\r\n"):
- self.parseHeader()
- elif not self.range and buf.startswith("150") and "data connection" in buf: #ftp file size parsing
- size = search(r"(\d+) bytes", buf)
- if size:
- self.p.size = int(size.group(1))
- self.p.chunkSupport = True
-
- self.headerParsed = True
-
- def writeBody(self, buf):
- #ignore BOM, it confuses unrar
- if not self.BOMChecked:
- if [ord(b) for b in buf[:3]] == [239, 187, 191]:
- buf = buf[3:]
- self.BOMChecked = True
-
- size = len(buf)
-
- self.arrived += size
-
- self.fp.write(buf)
-
- if self.p.bucket:
- sleep(self.p.bucket.consumed(size))
- else:
- # Avoid small buffers, increasing sleep time slowly if buffer size gets smaller
- # otherwise reduce sleep time percentual (values are based on tests)
- # So in general cpu time is saved without reducing bandwith too much
-
- if size < self.lastSize:
- self.sleep += 0.002
- else:
- self.sleep *= 0.7
-
- self.lastSize = size
-
- sleep(self.sleep)
-
- if self.range and self.arrived > self.size:
- return 0 #close if we have enough data
-
-
- def parseHeader(self):
- """parse data from recieved header"""
- for orgline in self.decodeResponse(self.header).splitlines():
- line = orgline.strip().lower()
- if line.startswith("accept-ranges") and "bytes" in line:
- self.p.chunkSupport = True
-
- if line.startswith("content-disposition") and "filename=" in line:
- name = orgline.partition("filename=")[2]
- name = name.replace('"', "").replace("'", "").replace(";", "").strip()
- self.p.nameDisposition = name
- self.log.debug("Content-Disposition: %s" % name)
-
- if not self.resume and line.startswith("content-length"):
- self.p.size = int(line.split(":")[1])
-
- self.headerParsed = True
-
- def stop(self):
- """The download will not proceed after next call of writeBody"""
- self.range = [0,0]
- self.size = 0
-
- def resetRange(self):
- """ Reset the range, so the download will load all data available """
- self.range = None
-
- def setRange(self, range):
- self.range = range
- self.size = range[1] - range[0]
-
- def flushFile(self):
- """ flush and close file """
- self.fp.flush()
- fsync(self.fp.fileno()) #make sure everything was written to disk
- self.fp.close() #needs to be closed, or merging chunks will fail
-
- def close(self):
- """ closes everything, unusable after this """
- if self.fp: self.fp.close()
- self.c.close()
- if hasattr(self, "p"): del self.p \ No newline at end of file
diff --git a/module/network/HTTPDownload.py b/module/network/HTTPDownload.py
deleted file mode 100644
index fe8075539..000000000
--- a/module/network/HTTPDownload.py
+++ /dev/null
@@ -1,340 +0,0 @@
-#!/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
-"""
-
-from os import remove, fsync
-from os.path import dirname
-from time import sleep, time
-from shutil import move
-from logging import getLogger
-
-import pycurl
-
-from HTTPChunk import ChunkInfo, HTTPChunk
-from HTTPRequest import BadHeader
-
-from module.plugins.Plugin import Abort
-from module.utils import save_join, fs_encode
-
-class HTTPDownload():
- """ loads a url http + ftp """
-
- def __init__(self, url, filename, get={}, post={}, referer=None, cj=None, bucket=None,
- options={}, progressNotify=None, disposition=False):
- self.url = url
- self.filename = filename #complete file destination, not only name
- self.get = get
- self.post = post
- self.referer = referer
- self.cj = cj #cookiejar if cookies are needed
- self.bucket = bucket
- self.options = options
- self.disposition = disposition
- # all arguments
-
- self.abort = False
- self.size = 0
- self.nameDisposition = None #will be parsed from content disposition
-
- self.chunks = []
-
- self.log = getLogger("log")
-
- try:
- self.info = ChunkInfo.load(filename)
- self.info.resume = True #resume is only possible with valid info file
- self.size = self.info.size
- self.infoSaved = True
- except IOError:
- self.info = ChunkInfo(filename)
-
- self.chunkSupport = None
- self.m = pycurl.CurlMulti()
-
- #needed for speed calculation
- self.lastArrived = []
- self.speeds = []
- self.lastSpeeds = [0, 0]
-
- self.progressNotify = progressNotify
-
- @property
- def speed(self):
- last = [sum(x) for x in self.lastSpeeds if x]
- return (sum(self.speeds) + sum(last)) / (1 + len(last))
-
- @property
- def arrived(self):
- return sum([c.arrived for c in self.chunks])
-
- @property
- def percent(self):
- if not self.size: return 0
- return (self.arrived * 100) / self.size
-
- def _copyChunks(self):
- init = fs_encode(self.info.getChunkName(0)) #initial chunk name
-
- if self.info.getCount() > 1:
- fo = open(init, "rb+") #first chunkfile
- for i in range(1, self.info.getCount()):
- #input file
- fo.seek(
- self.info.getChunkRange(i - 1)[1] + 1) #seek to beginning of chunk, to get rid of overlapping chunks
- fname = fs_encode("%s.chunk%d" % (self.filename, i))
- fi = open(fname, "rb")
- buf = 32 * 1024
- while True: #copy in chunks, consumes less memory
- data = fi.read(buf)
- if not data:
- break
- fo.write(data)
- fi.close()
- if fo.tell() < self.info.getChunkRange(i)[1]:
- fo.close()
- remove(init)
- self.info.remove() #there are probably invalid chunks
- raise Exception("Downloaded content was smaller than expected. Try to reduce download connections.")
- remove(fname) #remove chunk
- fo.close()
-
- if self.nameDisposition and self.disposition:
- self.filename = save_join(dirname(self.filename), self.nameDisposition)
-
- move(init, fs_encode(self.filename))
- self.info.remove() #remove info file
-
- def download(self, chunks=1, resume=False):
- """ returns new filename or None """
-
- chunks = max(1, chunks)
- resume = self.info.resume and resume
-
- try:
- self._download(chunks, resume)
- except pycurl.error, e:
- #code 33 - no resume
- code = e.args[0]
- if code == 33:
- # try again without resume
- self.log.debug("Errno 33 -> Restart without resume")
-
- #remove old handles
- for chunk in self.chunks:
- self.closeChunk(chunk)
-
- return self._download(chunks, False)
- else:
- raise
- finally:
- self.close()
-
- if self.nameDisposition and self.disposition: return self.nameDisposition
- return None
-
- def _download(self, chunks, resume):
- if not resume:
- self.info.clear()
- self.info.addChunk("%s.chunk0" % self.filename, (0, 0)) #create an initial entry
-
- self.chunks = []
-
- init = HTTPChunk(0, self, None, resume) #initial chunk that will load complete file (if needed)
-
- self.chunks.append(init)
- self.m.add_handle(init.getHandle())
-
- lastFinishCheck = 0
- lastTimeCheck = 0
- chunksDone = set() # list of curl handles that are finished
- chunksCreated = False
- done = False
- if self.info.getCount() > 1: # This is a resume, if we were chunked originally assume still can
- self.chunkSupport = True
-
- while 1:
- #need to create chunks
- if not chunksCreated and self.chunkSupport and self.size: #will be setted later by first chunk
-
- if not resume:
- self.info.setSize(self.size)
- self.info.createChunks(chunks)
- self.info.save()
-
- chunks = self.info.getCount()
-
- init.setRange(self.info.getChunkRange(0))
-
- for i in range(1, chunks):
- c = HTTPChunk(i, self, self.info.getChunkRange(i), resume)
-
- handle = c.getHandle()
- if handle:
- self.chunks.append(c)
- self.m.add_handle(handle)
- else:
- #close immediatly
- self.log.debug("Invalid curl handle -> closed")
- c.close()
-
- chunksCreated = True
-
- while 1:
- ret, num_handles = self.m.perform()
- if ret != pycurl.E_CALL_MULTI_PERFORM:
- break
-
- t = time()
-
- # reduce these calls
- while lastFinishCheck + 0.5 < t:
- # list of failed curl handles
- failed = []
- ex = None # save only last exception, we can only raise one anyway
-
- num_q, ok_list, err_list = self.m.info_read()
- for c in ok_list:
- chunk = self.findChunk(c)
- try: # check if the header implies success, else add it to failed list
- chunk.verifyHeader()
- except BadHeader, e:
- self.log.debug("Chunk %d failed: %s" % (chunk.id + 1, str(e)))
- failed.append(chunk)
- ex = e
- else:
- chunksDone.add(c)
-
- for c in err_list:
- curl, errno, msg = c
- chunk = self.findChunk(curl)
- #test if chunk was finished
- if errno != 23 or "0 !=" not in msg:
- failed.append(chunk)
- ex = pycurl.error(errno, msg)
- self.log.debug("Chunk %d failed: %s" % (chunk.id + 1, str(ex)))
- continue
-
- try: # check if the header implies success, else add it to failed list
- chunk.verifyHeader()
- except BadHeader, e:
- self.log.debug("Chunk %d failed: %s" % (chunk.id + 1, str(e)))
- failed.append(chunk)
- ex = e
- else:
- chunksDone.add(curl)
- if not num_q: # no more infos to get
-
- # check if init is not finished so we reset download connections
- # note that other chunks are closed and downloaded with init too
- if failed and init not in failed and init.c not in chunksDone:
- self.log.error(_("Download chunks failed, fallback to single connection | %s" % (str(ex))))
-
- #list of chunks to clean and remove
- to_clean = filter(lambda x: x is not init, self.chunks)
- for chunk in to_clean:
- self.closeChunk(chunk)
- self.chunks.remove(chunk)
- remove(fs_encode(self.info.getChunkName(chunk.id)))
-
- #let first chunk load the rest and update the info file
- init.resetRange()
- self.info.clear()
- self.info.addChunk("%s.chunk0" % self.filename, (0, self.size))
- self.info.save()
- elif failed:
- raise ex
-
- lastFinishCheck = t
-
- if len(chunksDone) >= len(self.chunks):
- if len(chunksDone) > len(self.chunks):
- self.log.warning("Finished download chunks size incorrect, please report bug.")
- done = True #all chunks loaded
-
- break
-
- if done:
- break #all chunks loaded
-
- # calc speed once per second, averaging over 3 seconds
- if lastTimeCheck + 1 < t:
- diff = [c.arrived - (self.lastArrived[i] if len(self.lastArrived) > i else 0) for i, c in
- enumerate(self.chunks)]
-
- self.lastSpeeds[1] = self.lastSpeeds[0]
- self.lastSpeeds[0] = self.speeds
- self.speeds = [float(a) / (t - lastTimeCheck) for a in diff]
- self.lastArrived = [c.arrived for c in self.chunks]
- lastTimeCheck = t
- self.updateProgress()
-
- if self.abort:
- raise Abort()
-
- #sleep(0.003) #supress busy waiting - limits dl speed to (1 / x) * buffersize
- self.m.select(1)
-
- for chunk in self.chunks:
- chunk.flushFile() #make sure downloads are written to disk
-
- self._copyChunks()
-
- def updateProgress(self):
- if self.progressNotify:
- self.progressNotify(self.percent)
-
- def findChunk(self, handle):
- """ linear search to find a chunk (should be ok since chunk size is usually low) """
- for chunk in self.chunks:
- if chunk.c == handle: return chunk
-
- def closeChunk(self, chunk):
- try:
- self.m.remove_handle(chunk.c)
- except pycurl.error, e:
- self.log.debug("Error removing chunk: %s" % str(e))
- finally:
- chunk.close()
-
- def close(self):
- """ cleanup """
- for chunk in self.chunks:
- self.closeChunk(chunk)
-
- self.chunks = []
- if hasattr(self, "m"):
- self.m.close()
- del self.m
- if hasattr(self, "cj"):
- del self.cj
- if hasattr(self, "info"):
- del self.info
-
-if __name__ == "__main__":
- url = "http://speedtest.netcologne.de/test_100mb.bin"
-
- from Bucket import Bucket
-
- bucket = Bucket()
- bucket.setRate(200 * 1024)
- bucket = None
-
- print "starting"
-
- dwnld = HTTPDownload(url, "test_100mb.bin", bucket=bucket)
- dwnld.download(chunks=3, resume=True)
diff --git a/module/network/HTTPRequest.py b/module/network/HTTPRequest.py
deleted file mode 100644
index 4747d937f..000000000
--- a/module/network/HTTPRequest.py
+++ /dev/null
@@ -1,306 +0,0 @@
-#!/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
-"""
-
-import pycurl
-
-from codecs import getincrementaldecoder, lookup, BOM_UTF8
-from urllib import quote, urlencode
-from httplib import responses
-from logging import getLogger
-from cStringIO import StringIO
-
-from module.plugins.Plugin import Abort
-
-def myquote(url):
- return quote(url.encode('utf_8') if isinstance(url, unicode) else url, safe="%/:=&?~#+!$,;'@()*[]")
-
-def myurlencode(data):
- data = dict(data)
- return urlencode(dict((x.encode('utf_8') if isinstance(x, unicode) else x, \
- y.encode('utf_8') if isinstance(y, unicode) else y ) for x, y in data.iteritems()))
-
-bad_headers = range(400, 404) + range(405, 418) + range(500, 506)
-
-class BadHeader(Exception):
- def __init__(self, code, content=""):
- Exception.__init__(self, "Bad server response: %s %s" % (code, responses[int(code)]))
- self.code = code
- self.content = content
-
-
-class HTTPRequest():
- def __init__(self, cookies=None, options=None):
- self.c = pycurl.Curl()
- self.rep = StringIO()
-
- self.cj = cookies #cookiejar
-
- self.lastURL = None
- self.lastEffectiveURL = None
- self.abort = False
- self.code = 0 # last http code
-
- self.header = ""
-
- self.headers = [] #temporary request header
-
- self.initHandle()
- self.setInterface(options)
-
- self.c.setopt(pycurl.WRITEFUNCTION, self.write)
- self.c.setopt(pycurl.HEADERFUNCTION, self.writeHeader)
-
- self.log = getLogger("log")
-
-
- def initHandle(self):
- """ sets common options to curl handle """
- self.c.setopt(pycurl.FOLLOWLOCATION, 1)
- self.c.setopt(pycurl.MAXREDIRS, 5)
- self.c.setopt(pycurl.CONNECTTIMEOUT, 30)
- self.c.setopt(pycurl.NOSIGNAL, 1)
- self.c.setopt(pycurl.NOPROGRESS, 1)
- if hasattr(pycurl, "AUTOREFERER"):
- self.c.setopt(pycurl.AUTOREFERER, 1)
- self.c.setopt(pycurl.SSL_VERIFYPEER, 0)
- self.c.setopt(pycurl.LOW_SPEED_TIME, 30)
- self.c.setopt(pycurl.LOW_SPEED_LIMIT, 5)
-
- #self.c.setopt(pycurl.VERBOSE, 1)
-
- self.c.setopt(pycurl.USERAGENT,
- "Mozilla/5.0 (Windows NT 6.1; Win64; x64;en; rv:5.0) Gecko/20110619 Firefox/5.0")
- if pycurl.version_info()[7]:
- self.c.setopt(pycurl.ENCODING, "gzip, deflate")
- self.c.setopt(pycurl.HTTPHEADER, ["Accept: */*",
- "Accept-Language: en-US,en",
- "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7",
- "Connection: keep-alive",
- "Keep-Alive: 300",
- "Expect:"])
-
- def setInterface(self, options):
-
- interface, proxy, ipv6 = options["interface"], options["proxies"], options["ipv6"]
-
- if interface and interface.lower() != "none":
- self.c.setopt(pycurl.INTERFACE, str(interface))
-
- if proxy:
- if proxy["type"] == "socks4":
- self.c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_SOCKS4)
- elif proxy["type"] == "socks5":
- self.c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_SOCKS5)
- else:
- self.c.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_HTTP)
-
- self.c.setopt(pycurl.PROXY, str(proxy["address"]))
- self.c.setopt(pycurl.PROXYPORT, proxy["port"])
-
- if proxy["username"]:
- self.c.setopt(pycurl.PROXYUSERPWD, str("%s:%s" % (proxy["username"], proxy["password"])))
-
- if ipv6:
- self.c.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_WHATEVER)
- else:
- self.c.setopt(pycurl.IPRESOLVE, pycurl.IPRESOLVE_V4)
-
- if "auth" in options:
- self.c.setopt(pycurl.USERPWD, str(options["auth"]))
-
- if "timeout" in options:
- self.c.setopt(pycurl.LOW_SPEED_TIME, options["timeout"])
-
-
- def addCookies(self):
- """ put cookies from curl handle to cj """
- if self.cj:
- self.cj.addCookies(self.c.getinfo(pycurl.INFO_COOKIELIST))
-
- def getCookies(self):
- """ add cookies from cj to curl handle """
- if self.cj:
- for c in self.cj.getCookies():
- self.c.setopt(pycurl.COOKIELIST, c)
- return
-
- def clearCookies(self):
- self.c.setopt(pycurl.COOKIELIST, "")
-
- def setRequestContext(self, url, get, post, referer, cookies, multipart=False):
- """ sets everything needed for the request """
-
- url = myquote(url)
-
- if get:
- get = urlencode(get)
- url = "%s?%s" % (url, get)
-
- self.c.setopt(pycurl.URL, url)
- self.c.lastUrl = url
-
- if post:
- self.c.setopt(pycurl.POST, 1)
- if not multipart:
- if type(post) == unicode:
- post = str(post) #unicode not allowed
- elif type(post) == str:
- pass
- else:
- post = myurlencode(post)
-
- self.c.setopt(pycurl.POSTFIELDS, post)
- else:
- post = [(x, y.encode('utf8') if type(y) == unicode else y ) for x, y in post.iteritems()]
- self.c.setopt(pycurl.HTTPPOST, post)
- else:
- self.c.setopt(pycurl.POST, 0)
-
- if referer and self.lastURL:
- self.c.setopt(pycurl.REFERER, str(self.lastURL))
-
- if cookies:
- self.c.setopt(pycurl.COOKIEFILE, "")
- self.c.setopt(pycurl.COOKIEJAR, "")
- self.getCookies()
-
-
- def load(self, url, get={}, post={}, referer=True, cookies=True, just_header=False, multipart=False, decode=False):
- """ load and returns a given page """
-
- self.setRequestContext(url, get, post, referer, cookies, multipart)
-
- self.header = ""
-
- self.c.setopt(pycurl.HTTPHEADER, self.headers)
-
- if just_header:
- self.c.setopt(pycurl.FOLLOWLOCATION, 0)
- self.c.setopt(pycurl.NOBODY, 1)
- self.c.perform()
- rep = self.header
-
- self.c.setopt(pycurl.FOLLOWLOCATION, 1)
- self.c.setopt(pycurl.NOBODY, 0)
-
- else:
- self.c.perform()
- rep = self.getResponse()
-
- self.c.setopt(pycurl.POSTFIELDS, "")
- self.lastEffectiveURL = self.c.getinfo(pycurl.EFFECTIVE_URL)
- self.code = self.verifyHeader()
-
- self.addCookies()
-
- if decode:
- rep = self.decodeResponse(rep)
-
- return rep
-
- def verifyHeader(self):
- """ raise an exceptions on bad headers """
- code = int(self.c.getinfo(pycurl.RESPONSE_CODE))
- if code in bad_headers:
- #404 will NOT raise an exception
- raise BadHeader(code, self.getResponse())
- return code
-
- def checkHeader(self):
- """ check if header indicates failure"""
- return int(self.c.getinfo(pycurl.RESPONSE_CODE)) not in bad_headers
-
- def getResponse(self):
- """ retrieve response from string io """
- if self.rep is None: return ""
- value = self.rep.getvalue()
- self.rep.close()
- self.rep = StringIO()
- return value
-
- def decodeResponse(self, rep):
- """ decode with correct encoding, relies on header """
- header = self.header.splitlines()
- encoding = "utf8" # default encoding
-
- for line in header:
- line = line.lower().replace(" ", "")
- if not line.startswith("content-type:") or\
- ("text" not in line and "application" not in line):
- continue
-
- none, delemiter, charset = line.rpartition("charset=")
- if delemiter:
- charset = charset.split(";")
- if charset:
- encoding = charset[0]
-
- try:
- #self.log.debug("Decoded %s" % encoding )
- if lookup(encoding).name == 'utf-8' and rep.startswith(BOM_UTF8):
- encoding = 'utf-8-sig'
-
- decoder = getincrementaldecoder(encoding)("replace")
- rep = decoder.decode(rep, True)
-
- #TODO: html_unescape as default
-
- except LookupError:
- self.log.debug("No Decoder foung for %s" % encoding)
- except Exception:
- self.log.debug("Error when decoding string from %s." % encoding)
-
- return rep
-
- def write(self, buf):
- """ writes response """
- if self.rep.tell() > 1000000 or self.abort:
- rep = self.getResponse()
- if self.abort: raise Abort()
- f = open("response.dump", "wb")
- f.write(rep)
- f.close()
- raise Exception("Loaded Url exceeded limit")
-
- self.rep.write(buf)
-
- def writeHeader(self, buf):
- """ writes header """
- self.header += buf
-
- def putHeader(self, name, value):
- self.headers.append("%s: %s" % (name, value))
-
- def clearHeaders(self):
- self.headers = []
-
- def close(self):
- """ cleanup, unusable after this """
- self.rep.close()
- if hasattr(self, "cj"):
- del self.cj
- if hasattr(self, "c"):
- self.c.close()
- del self.c
-
-if __name__ == "__main__":
- url = "http://pyload.org"
- c = HTTPRequest()
- print c.load(url)
-
diff --git a/module/network/RequestFactory.py b/module/network/RequestFactory.py
deleted file mode 100644
index 5b1528281..000000000
--- a/module/network/RequestFactory.py
+++ /dev/null
@@ -1,126 +0,0 @@
-# -*- 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: mkaay, RaNaN
-"""
-
-from threading import Lock
-
-from Browser import Browser
-from Bucket import Bucket
-from HTTPRequest import HTTPRequest
-from CookieJar import CookieJar
-
-from XDCCRequest import XDCCRequest
-
-class RequestFactory():
- def __init__(self, core):
- self.lock = Lock()
- self.core = core
- self.bucket = Bucket()
- self.updateBucket()
- self.cookiejars = {}
-
- def iface(self):
- return self.core.config["download"]["interface"]
-
- def getRequest(self, pluginName, account=None, type="HTTP"):
- self.lock.acquire()
-
- if type == "XDCC":
- return XDCCRequest(proxies=self.getProxies())
-
- req = Browser(self.bucket, self.getOptions())
-
- if account:
- cj = self.getCookieJar(pluginName, account)
- req.setCookieJar(cj)
- else:
- req.setCookieJar(CookieJar(pluginName))
-
- self.lock.release()
- return req
-
- def getHTTPRequest(self, **kwargs):
- """ returns a http request, dont forget to close it ! """
- options = self.getOptions()
- options.update(kwargs) # submit kwargs as additional options
- return HTTPRequest(CookieJar(None), options)
-
- def getURL(self, *args, **kwargs):
- """ see HTTPRequest for argument list """
- h = HTTPRequest(None, self.getOptions())
- try:
- rep = h.load(*args, **kwargs)
- finally:
- h.close()
-
- return rep
-
- def getCookieJar(self, pluginName, account=None):
- if (pluginName, account) in self.cookiejars:
- return self.cookiejars[(pluginName, account)]
-
- cj = CookieJar(pluginName, account)
- self.cookiejars[(pluginName, account)] = cj
- return cj
-
- def getProxies(self):
- """ returns a proxy list for the request classes """
- if not self.core.config["proxy"]["proxy"]:
- return {}
- else:
- type = "http"
- setting = self.core.config["proxy"]["type"].lower()
- if setting == "socks4": type = "socks4"
- elif setting == "socks5": type = "socks5"
-
- username = None
- if self.core.config["proxy"]["username"] and self.core.config["proxy"]["username"].lower() != "none":
- username = self.core.config["proxy"]["username"]
-
- pw = None
- if self.core.config["proxy"]["password"] and self.core.config["proxy"]["password"].lower() != "none":
- pw = self.core.config["proxy"]["password"]
-
- return {
- "type": type,
- "address": self.core.config["proxy"]["address"],
- "port": self.core.config["proxy"]["port"],
- "username": username,
- "password": pw,
- }
-
- def getOptions(self):
- """returns options needed for pycurl"""
- return {"interface": self.iface(),
- "proxies": self.getProxies(),
- "ipv6": self.core.config["download"]["ipv6"]}
-
- def updateBucket(self):
- """ set values in the bucket according to settings"""
- if not self.core.config["download"]["limit_speed"]:
- self.bucket.setRate(-1)
- else:
- self.bucket.setRate(self.core.config["download"]["max_speed"] * 1024)
-
-# needs pyreq in global namespace
-def getURL(*args, **kwargs):
- return pyreq.getURL(*args, **kwargs)
-
-
-def getRequest(*args, **kwargs):
- return pyreq.getHTTPRequest()
diff --git a/module/network/XDCCRequest.py b/module/network/XDCCRequest.py
deleted file mode 100644
index f03798c17..000000000
--- a/module/network/XDCCRequest.py
+++ /dev/null
@@ -1,162 +0,0 @@
-#!/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: jeix
-"""
-
-import socket
-import re
-
-from os import remove
-from os.path import exists
-
-from time import time
-
-import struct
-from select import select
-
-from module.plugins.Plugin import Abort
-
-
-class XDCCRequest():
- def __init__(self, timeout=30, proxies={}):
-
- self.proxies = proxies
- self.timeout = timeout
-
- self.filesize = 0
- self.recv = 0
- self.speed = 0
-
- self.abort = False
-
-
- def createSocket(self):
- # proxytype = None
- # proxy = None
- # if self.proxies.has_key("socks5"):
- # proxytype = socks.PROXY_TYPE_SOCKS5
- # proxy = self.proxies["socks5"]
- # elif self.proxies.has_key("socks4"):
- # proxytype = socks.PROXY_TYPE_SOCKS4
- # proxy = self.proxies["socks4"]
- # if proxytype:
- # sock = socks.socksocket()
- # t = _parse_proxy(proxy)
- # sock.setproxy(proxytype, addr=t[3].split(":")[0], port=int(t[3].split(":")[1]), username=t[1], password=t[2])
- # else:
- # sock = socket.socket()
- # return sock
-
- return socket.socket()
-
- def download(self, ip, port, filename, irc, progressNotify=None):
-
- ircbuffer = ""
- lastUpdate = time()
- cumRecvLen = 0
-
- dccsock = self.createSocket()
-
- dccsock.settimeout(self.timeout)
- dccsock.connect((ip, port))
-
- if exists(filename):
- i = 0
- nameParts = filename.rpartition(".")
- while True:
- newfilename = "%s-%d%s%s" % (nameParts[0], i, nameParts[1], nameParts[2])
- i += 1
-
- if not exists(newfilename):
- filename = newfilename
- break
-
- fh = open(filename, "wb")
-
- # recv loop for dcc socket
- while True:
- if self.abort:
- dccsock.close()
- fh.close()
- remove(filename)
- raise Abort()
-
- self._keepAlive(irc, ircbuffer)
-
- data = dccsock.recv(4096)
- dataLen = len(data)
- self.recv += dataLen
-
- cumRecvLen += dataLen
-
- now = time()
- timespan = now - lastUpdate
- if timespan > 1:
- self.speed = cumRecvLen / timespan
- cumRecvLen = 0
- lastUpdate = now
-
- if progressNotify:
- progressNotify(self.percent)
-
-
- if not data:
- break
-
- fh.write(data)
-
- # acknowledge data by sending number of recceived bytes
- dccsock.send(struct.pack('!I', self.recv))
-
- dccsock.close()
- fh.close()
-
- return filename
-
- def _keepAlive(self, sock, readbuffer):
- fdset = select([sock], [], [], 0)
- if sock not in fdset[0]:
- return
-
- readbuffer += sock.recv(1024)
- temp = readbuffer.split("\n")
- readbuffer = temp.pop()
-
- for line in temp:
- line = line.rstrip()
- first = line.split()
- if first[0] == "PING":
- sock.send("PONG %s\r\n" % first[1])
-
- def abortDownloads(self):
- self.abort = True
-
- @property
- def size(self):
- return self.filesize
-
- @property
- def arrived(self):
- return self.recv
-
- @property
- def percent(self):
- if not self.filesize: return 0
- return (self.recv * 100) / self.filesize
-
- def close(self):
- pass
diff --git a/module/network/__init__.py b/module/network/__init__.py
deleted file mode 100644
index 8b1378917..000000000
--- a/module/network/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/module/plugins/Account.py b/module/plugins/Account.py
deleted file mode 100644
index c147404e0..000000000
--- a/module/plugins/Account.py
+++ /dev/null
@@ -1,292 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from random import choice
-from time import time
-from traceback import print_exc
-from threading import RLock
-
-from Plugin import Base
-from module.utils import compare_time, parseFileSize, lock
-
-class WrongPassword(Exception):
- pass
-
-
-class Account(Base):
- """
- Base class for every Account plugin.
- Just overwrite `login` and cookies will be stored and account becomes accessible in\
- associated hoster plugin. Plugin should also provide `loadAccountInfo`
- """
- __name__ = "Account"
- __version__ = "0.2"
- __type__ = "account"
- __description__ = """Account Plugin"""
- __author_name__ = ("mkaay")
- __author_mail__ = ("mkaay@mkaay.de")
-
- #: after that time [in minutes] pyload will relogin the account
- login_timeout = 600
- #: account data will be reloaded after this time
- info_threshold = 600
-
-
- def __init__(self, manager, accounts):
- Base.__init__(self, manager.core)
-
- self.manager = manager
- self.accounts = {}
- self.infos = {} # cache for account information
- self.lock = RLock()
-
- self.timestamps = {}
- self.setAccounts(accounts)
- self.init()
-
- def init(self):
- pass
-
- def login(self, user, data, req):
- """login into account, the cookies will be saved so user can be recognized
-
- :param user: loginname
- :param data: data dictionary
- :param req: `Request` instance
- """
- pass
-
- @lock
- def _login(self, user, data):
- # set timestamp for login
- self.timestamps[user] = time()
-
- req = self.getAccountRequest(user)
- try:
- self.login(user, data, req)
- except WrongPassword:
- self.logWarning(
- _("Could not login with account %(user)s | %(msg)s") % {"user": user
- , "msg": _("Wrong Password")})
- data["valid"] = False
-
- except Exception, e:
- self.logWarning(
- _("Could not login with account %(user)s | %(msg)s") % {"user": user
- , "msg": e})
- data["valid"] = False
- if self.core.debug:
- print_exc()
- finally:
- if req: req.close()
-
- def relogin(self, user):
- req = self.getAccountRequest(user)
- if req:
- req.cj.clear()
- req.close()
- if user in self.infos:
- del self.infos[user] #delete old information
-
- self._login(user, self.accounts[user])
-
- def setAccounts(self, accounts):
- self.accounts = accounts
- for user, data in self.accounts.iteritems():
- self._login(user, data)
- self.infos[user] = {}
-
- def updateAccounts(self, user, password=None, options={}):
- """ updates account and return true if anything changed """
-
- if user in self.accounts:
- self.accounts[user]["valid"] = True #do not remove or accounts will not login
- if password:
- self.accounts[user]["password"] = password
- self.relogin(user)
- return True
- if options:
- before = self.accounts[user]["options"]
- self.accounts[user]["options"].update(options)
- return self.accounts[user]["options"] != before
- else:
- self.accounts[user] = {"password": password, "options": options, "valid": True}
- self._login(user, self.accounts[user])
- return True
-
- def removeAccount(self, user):
- if user in self.accounts:
- del self.accounts[user]
- if user in self.infos:
- del self.infos[user]
- if user in self.timestamps:
- del self.timestamps[user]
-
- @lock
- def getAccountInfo(self, name, force=False):
- """retrieve account infos for an user, do **not** overwrite this method!\\
- just use it to retrieve infos in hoster plugins. see `loadAccountInfo`
-
- :param name: username
- :param force: reloads cached account information
- :return: dictionary with information
- """
- data = Account.loadAccountInfo(self, name)
-
- if force or name not in self.infos:
- self.logDebug("Get Account Info for %s" % name)
- req = self.getAccountRequest(name)
-
- try:
- infos = self.loadAccountInfo(name, req)
- if not type(infos) == dict:
- raise Exception("Wrong return format")
- except Exception, e:
- infos = {"error": str(e)}
-
- if req: req.close()
-
- self.logDebug("Account Info: %s" % str(infos))
-
- infos["timestamp"] = time()
- self.infos[name] = infos
- elif "timestamp" in self.infos[name] and self.infos[name][
- "timestamp"] + self.info_threshold * 60 < time():
- self.logDebug("Reached timeout for account data")
- self.scheduleRefresh(name)
-
- data.update(self.infos[name])
- return data
-
- def isPremium(self, user):
- info = self.getAccountInfo(user)
- return info["premium"]
-
- def loadAccountInfo(self, name, req=None):
- """this should be overwritten in account plugin,\
- and retrieving account information for user
-
- :param name:
- :param req: `Request` instance
- :return:
- """
- return {
- "validuntil": None, # -1 for unlimited
- "login": name,
- #"password": self.accounts[name]["password"], #@XXX: security
- "options": self.accounts[name]["options"],
- "valid": self.accounts[name]["valid"],
- "trafficleft": None, # in kb, -1 for unlimited
- "maxtraffic": None,
- "premium": True, #useful for free accounts
- "timestamp": 0, #time this info was retrieved
- "type": self.__name__,
- }
-
- def getAllAccounts(self, force=False):
- return [self.getAccountInfo(user, force) for user, data in self.accounts.iteritems()]
-
- def getAccountRequest(self, user=None):
- if not user:
- user, data = self.selectAccount()
- if not user:
- return None
-
- req = self.core.requestFactory.getRequest(self.__name__, user)
- return req
-
- def getAccountCookies(self, user=None):
- if not user:
- user, data = self.selectAccount()
- if not user:
- return None
-
- cj = self.core.requestFactory.getCookieJar(self.__name__, user)
- return cj
-
- def getAccountData(self, user):
- return self.accounts[user]
-
- def selectAccount(self):
- """ returns an valid account name and data"""
- usable = []
- for user, data in self.accounts.iteritems():
- if not data["valid"]: continue
-
- if "time" in data["options"] and data["options"]["time"]:
- time_data = ""
- try:
- time_data = data["options"]["time"][0]
- start, end = time_data.split("-")
- if not compare_time(start.split(":"), end.split(":")):
- continue
- except:
- self.logWarning(_("Your Time %s has wrong format, use: 1:22-3:44") % time_data)
-
- if user in self.infos:
- if "validuntil" in self.infos[user]:
- if self.infos[user]["validuntil"] > 0 and time() > self.infos[user]["validuntil"]:
- continue
- if "trafficleft" in self.infos[user]:
- if self.infos[user]["trafficleft"] == 0:
- continue
-
- usable.append((user, data))
-
- if not usable: return None, None
- return choice(usable)
-
- def canUse(self):
- return False if self.selectAccount() == (None, None) else True
-
- def parseTraffic(self, string): #returns kbyte
- return parseFileSize(string) / 1024
-
- def wrongPassword(self):
- raise WrongPassword
-
- def empty(self, user):
- if user in self.infos:
- self.logWarning(_("Account %s has not enough traffic, checking again in 30min") % user)
-
- self.infos[user].update({"trafficleft": 0})
- self.scheduleRefresh(user, 30 * 60)
-
- def expired(self, user):
- if user in self.infos:
- self.logWarning(_("Account %s is expired, checking again in 1h") % user)
-
- self.infos[user].update({"validuntil": time() - 1})
- self.scheduleRefresh(user, 60 * 60)
-
- def scheduleRefresh(self, user, time=0, force=True):
- """ add task to refresh account info to sheduler """
- self.logDebug("Scheduled Account refresh for %s in %s seconds." % (user, time))
- self.core.scheduler.addJob(time, self.getAccountInfo, [user, force])
-
- @lock
- def checkLogin(self, user):
- """ checks if user is still logged in """
- if user in self.timestamps:
- if self.timestamps[user] + self.login_timeout * 60 < time():
- self.logDebug("Reached login timeout for %s" % user)
- self.relogin(user)
- return False
-
- return True
diff --git a/module/plugins/AccountManager.py b/module/plugins/AccountManager.py
deleted file mode 100644
index fc521d36c..000000000
--- a/module/plugins/AccountManager.py
+++ /dev/null
@@ -1,185 +0,0 @@
-#!/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
-"""
-
-from os.path import exists
-from shutil import copy
-
-from threading import Lock
-
-from module.PullEvents import AccountUpdateEvent
-from module.utils import chmod, lock
-
-ACC_VERSION = 1
-
-class AccountManager():
- """manages all accounts"""
-
- #----------------------------------------------------------------------
- def __init__(self, core):
- """Constructor"""
-
- self.core = core
- self.lock = Lock()
-
- self.initPlugins()
- self.saveAccounts() # save to add categories to conf
-
- def initPlugins(self):
- self.accounts = {} # key = ( plugin )
- self.plugins = {}
-
- self.initAccountPlugins()
- self.loadAccounts()
-
-
- def getAccountPlugin(self, plugin):
- """get account instance for plugin or None if anonymous"""
- if plugin in self.accounts:
- if plugin not in self.plugins:
- self.plugins[plugin] = self.core.pluginManager.loadClass("accounts", plugin)(self, self.accounts[plugin])
-
- return self.plugins[plugin]
- else:
- return None
-
- def getAccountPlugins(self):
- """ get all account instances"""
-
- plugins = []
- for plugin in self.accounts.keys():
- plugins.append(self.getAccountPlugin(plugin))
-
- return plugins
- #----------------------------------------------------------------------
- def loadAccounts(self):
- """loads all accounts available"""
-
- if not exists("accounts.conf"):
- f = open("accounts.conf", "wb")
- f.write("version: " + str(ACC_VERSION))
- f.close()
-
- f = open("accounts.conf", "rb")
- content = f.readlines()
- version = content[0].split(":")[1].strip() if content else ""
- f.close()
-
- if not version or int(version) < ACC_VERSION:
- copy("accounts.conf", "accounts.backup")
- f = open("accounts.conf", "wb")
- f.write("version: " + str(ACC_VERSION))
- f.close()
- self.core.log.warning(_("Account settings deleted, due to new config format."))
- return
-
-
-
- plugin = ""
- name = ""
-
- for line in content[1:]:
- line = line.strip()
-
- if not line: continue
- if line.startswith("#"): continue
- if line.startswith("version"): continue
-
- if line.endswith(":") and line.count(":") == 1:
- plugin = line[:-1]
- self.accounts[plugin] = {}
-
- elif line.startswith("@"):
- try:
- option = line[1:].split()
- self.accounts[plugin][name]["options"][option[0]] = [] if len(option) < 2 else ([option[1]] if len(option) < 3 else option[1:])
- except:
- pass
-
- elif ":" in line:
- name, sep, pw = line.partition(":")
- self.accounts[plugin][name] = {"password": pw, "options": {}, "valid": True}
- #----------------------------------------------------------------------
- def saveAccounts(self):
- """save all account information"""
-
- f = open("accounts.conf", "wb")
- f.write("version: " + str(ACC_VERSION) + "\n")
-
- for plugin, accounts in self.accounts.iteritems():
- f.write("\n")
- f.write(plugin+":\n")
-
- for name,data in accounts.iteritems():
- f.write("\n\t%s:%s\n" % (name,data["password"]) )
- if data["options"]:
- for option, values in data["options"].iteritems():
- f.write("\t@%s %s\n" % (option, " ".join(values)))
-
- f.close()
- chmod(f.name, 0600)
-
-
- #----------------------------------------------------------------------
- def initAccountPlugins(self):
- """init names"""
- for name in self.core.pluginManager.getAccountPlugins():
- self.accounts[name] = {}
-
- @lock
- def updateAccount(self, plugin , user, password=None, options={}):
- """add or update account"""
- if plugin in self.accounts:
- p = self.getAccountPlugin(plugin)
- updated = p.updateAccounts(user, password, options)
- #since accounts is a ref in plugin self.accounts doesnt need to be updated here
-
- self.saveAccounts()
- if updated: p.scheduleRefresh(user, force=False)
-
- @lock
- def removeAccount(self, plugin, user):
- """remove account"""
-
- if plugin in self.accounts:
- p = self.getAccountPlugin(plugin)
- p.removeAccount(user)
-
- self.saveAccounts()
-
- @lock
- def getAccountInfos(self, force=True, refresh=False):
- data = {}
-
- if refresh:
- self.core.scheduler.addJob(0, self.core.accountManager.getAccountInfos)
- force = False
-
- for p in self.accounts.keys():
- if self.accounts[p]:
- p = self.getAccountPlugin(p)
- data[p.__name__] = p.getAllAccounts(force)
- else:
- data[p] = []
- e = AccountUpdateEvent()
- self.core.pullManager.addEvent(e)
- return data
-
- def sendChange(self):
- e = AccountUpdateEvent()
- self.core.pullManager.addEvent(e)
diff --git a/module/plugins/Container.py b/module/plugins/Container.py
deleted file mode 100644
index c233d3710..000000000
--- a/module/plugins/Container.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from module.plugins.Crypter import Crypter
-
-from os.path import join, exists, basename
-from os import remove
-import re
-
-class Container(Crypter):
- __name__ = "Container"
- __version__ = "0.1"
- __pattern__ = None
- __type__ = "container"
- __description__ = """Base container plugin"""
- __author_name__ = ("mkaay")
- __author_mail__ = ("mkaay@mkaay.de")
-
-
- def preprocessing(self, thread):
- """prepare"""
-
- self.setup()
- self.thread = thread
-
- self.loadToDisk()
-
- self.decrypt(self.pyfile)
- self.deleteTmp()
-
- self.createPackages()
-
-
- def loadToDisk(self):
- """loads container to disk if its stored remotely and overwrite url,
- or check existent on several places at disk"""
-
- if self.pyfile.url.startswith("http"):
- self.pyfile.name = re.findall("([^\/=]+)", self.pyfile.url)[-1]
- content = self.load(self.pyfile.url)
- self.pyfile.url = join(self.config["general"]["download_folder"], self.pyfile.name)
- f = open(self.pyfile.url, "wb" )
- f.write(content)
- f.close()
-
- else:
- self.pyfile.name = basename(self.pyfile.url)
- if not exists(self.pyfile.url):
- if exists(join(pypath, self.pyfile.url)):
- self.pyfile.url = join(pypath, self.pyfile.url)
- else:
- self.fail(_("File not exists."))
-
-
- def deleteTmp(self):
- if self.pyfile.name.startswith("tmp_"):
- remove(self.pyfile.url)
-
-
diff --git a/module/plugins/Crypter.py b/module/plugins/Crypter.py
deleted file mode 100644
index d1549fe80..000000000
--- a/module/plugins/Crypter.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from module.plugins.Plugin import Plugin
-
-class Crypter(Plugin):
- __name__ = "Crypter"
- __version__ = "0.1"
- __pattern__ = None
- __type__ = "container"
- __description__ = """Base crypter plugin"""
- __author_name__ = ("mkaay")
- __author_mail__ = ("mkaay@mkaay.de")
-
- def __init__(self, pyfile):
- Plugin.__init__(self, pyfile)
-
- #: Put all packages here. It's a list of tuples like: ( name, [list of links], folder )
- self.packages = []
-
- #: List of urls, pyLoad will generate packagenames
- self.urls = []
-
- self.multiDL = True
- self.limitDL = 0
-
-
- def preprocessing(self, thread):
- """prepare"""
- self.setup()
- self.thread = thread
-
- self.decrypt(self.pyfile)
-
- self.createPackages()
-
-
- def decrypt(self, pyfile):
- raise NotImplementedError
-
- def createPackages(self):
- """ create new packages from self.packages """
- for pack in self.packages:
-
- self.log.debug("Parsed package %(name)s with %(len)d links" % { "name" : pack[0], "len" : len(pack[1]) } )
-
- links = [x.decode("utf-8") for x in pack[1]]
-
- pid = self.core.api.addPackage(pack[0], links, self.pyfile.package().queue)
-
- if self.pyfile.package().password:
- self.core.api.setPackageData(pid, {"password": self.pyfile.package().password})
-
- if self.urls:
- self.core.api.generateAndAddPackages(self.urls)
-
diff --git a/module/plugins/Hook.py b/module/plugins/Hook.py
deleted file mode 100644
index 5efd08bae..000000000
--- a/module/plugins/Hook.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# -*- 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: mkaay
- @interface-version: 0.2
-"""
-
-from traceback import print_exc
-
-from Plugin import Base
-
-class Expose(object):
- """ used for decoration to declare rpc services """
-
- def __new__(cls, f, *args, **kwargs):
- hookManager.addRPC(f.__module__, f.func_name, f.func_doc)
- return f
-
-def threaded(f):
- def run(*args,**kwargs):
- hookManager.startThread(f, *args, **kwargs)
- return run
-
-class Hook(Base):
- """
- Base class for hook plugins.
- """
- __name__ = "Hook"
- __version__ = "0.2"
- __type__ = "hook"
- __threaded__ = []
- __config__ = [ ("name", "type", "desc" , "default") ]
- __description__ = """interface for hook"""
- __author_name__ = ("mkaay", "RaNaN")
- __author_mail__ = ("mkaay@mkaay.de", "RaNaN@pyload.org")
-
- #: automatically register event listeners for functions, attribute will be deleted dont use it yourself
- event_map = None
-
- # Alternative to event_map
- #: List of events the plugin can handle, name the functions exactly like eventname.
- event_list = None # dont make duplicate entries in event_map
-
-
- #: periodic call interval in secondc
- interval = 60
-
- def __init__(self, core, manager):
- Base.__init__(self, core)
-
- #: Provide information in dict here, usable by API `getInfo`
- self.info = None
-
- #: Callback of periodical job task, used by hookmanager
- self.cb = None
-
- #: `HookManager`
- self.manager = manager
-
- #register events
- if self.event_map:
- for event, funcs in self.event_map.iteritems():
- if type(funcs) in (list, tuple):
- for f in funcs:
- self.manager.addEvent(event, getattr(self,f))
- else:
- self.manager.addEvent(event, getattr(self,funcs))
-
- #delete for various reasons
- self.event_map = None
-
- if self.event_list:
- for f in self.event_list:
- self.manager.addEvent(f, getattr(self,f))
-
- self.event_list = None
-
- self.initPeriodical()
- self.setup()
-
- def initPeriodical(self):
- if self.interval >=1:
- self.cb = self.core.scheduler.addJob(0, self._periodical, threaded=False)
-
- def _periodical(self):
- try:
- if self.isActivated(): self.periodical()
- except Exception, e:
- self.core.log.error(_("Error executing hooks: %s") % str(e))
- if self.core.debug:
- print_exc()
-
- self.cb = self.core.scheduler.addJob(self.interval, self._periodical, threaded=False)
-
-
- def __repr__(self):
- return "<Hook %s>" % self.__name__
-
- def setup(self):
- """ more init stuff if needed """
- pass
-
- def unload(self):
- """ called when hook was deactivated """
- pass
-
- def isActivated(self):
- """ checks if hook is activated"""
- return self.config.getPlugin(self.__name__, "activated")
-
-
- #event methods - overwrite these if needed
- def coreReady(self):
- pass
-
- def coreExiting(self):
- pass
-
- def downloadPreparing(self, pyfile):
- pass
-
- def downloadFinished(self, pyfile):
- pass
-
- def downloadFailed(self, pyfile):
- pass
-
- def packageFinished(self, pypack):
- pass
-
- def beforeReconnecting(self, ip):
- pass
-
- def afterReconnecting(self, ip):
- pass
-
- def periodical(self):
- pass
-
- def newCaptchaTask(self, task):
- """ new captcha task for the plugin, it MUST set the handler and timeout or will be ignored """
- pass
-
- def captchaCorrect(self, task):
- pass
-
- def captchaInvalid(self, task):
- pass \ No newline at end of file
diff --git a/module/plugins/Hoster.py b/module/plugins/Hoster.py
deleted file mode 100644
index 814a70949..000000000
--- a/module/plugins/Hoster.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from module.plugins.Plugin import Plugin
-
-def getInfo(self):
- #result = [ .. (name, size, status, url) .. ]
- return
-
-class Hoster(Plugin):
- __name__ = "Hoster"
- __version__ = "0.1"
- __pattern__ = None
- __type__ = "hoster"
- __description__ = """Base hoster plugin"""
- __author_name__ = ("mkaay")
- __author_mail__ = ("mkaay@mkaay.de")
diff --git a/module/plugins/Plugin.py b/module/plugins/Plugin.py
deleted file mode 100644
index 15bf3971f..000000000
--- a/module/plugins/Plugin.py
+++ /dev/null
@@ -1,617 +0,0 @@
-# -*- 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, spoob, mkaay
-"""
-
-from time import time, sleep
-from random import randint
-
-import os
-from os import remove, makedirs, chmod, stat
-from os.path import exists, join
-
-if os.name != "nt":
- from os import chown
- from pwd import getpwnam
- from grp import getgrnam
-
-from itertools import islice
-
-from module.utils import save_join, save_path, fs_encode, fs_decode
-
-def chunks(iterable, size):
- it = iter(iterable)
- item = list(islice(it, size))
- while item:
- yield item
- item = list(islice(it, size))
-
-
-class Abort(Exception):
- """ raised when aborted """
-
-
-class Fail(Exception):
- """ raised when failed """
-
-
-class Reconnect(Exception):
- """ raised when reconnected """
-
-
-class Retry(Exception):
- """ raised when start again from beginning """
-
-
-class SkipDownload(Exception):
- """ raised when download should be skipped """
-
-
-class Base(object):
- """
- A Base class with log/config/db methods *all* plugin types can use
- """
-
- def __init__(self, core):
- #: Core instance
- self.core = core
- #: logging instance
- self.log = core.log
- #: core config
- self.config = core.config
-
- #log functions
- def logInfo(self, *args):
- self.log.info("%s: %s" % (self.__name__, " | ".join([a if isinstance(a, basestring) else str(a) for a in args])))
-
- def logWarning(self, *args):
- self.log.warning("%s: %s" % (self.__name__, " | ".join([a if isinstance(a, basestring) else str(a) for a in args])))
-
- def logError(self, *args):
- self.log.error("%s: %s" % (self.__name__, " | ".join([a if isinstance(a, basestring) else str(a) for a in args])))
-
- def logDebug(self, *args):
- self.log.debug("%s: %s" % (self.__name__, " | ".join([a if isinstance(a, basestring) else str(a) for a in args])))
-
-
- def setConf(self, option, value):
- """ see `setConfig` """
- self.core.config.setPlugin(self.__name__, option, value)
-
- def setConfig(self, option, value):
- """ Set config value for current plugin
-
- :param option:
- :param value:
- :return:
- """
- self.setConf(option, value)
-
- def getConf(self, option):
- """ see `getConfig` """
- return self.core.config.getPlugin(self.__name__, option)
-
- def getConfig(self, option):
- """ Returns config value for current plugin
-
- :param option:
- :return:
- """
- return self.getConf(option)
-
- def setStorage(self, key, value):
- """ Saves a value persistently to the database """
- self.core.db.setStorage(self.__name__, key, value)
-
- def store(self, key, value):
- """ same as `setStorage` """
- self.core.db.setStorage(self.__name__, key, value)
-
- def getStorage(self, key=None, default=None):
- """ Retrieves saved value or dict of all saved entries if key is None """
- if key is not None:
- return self.core.db.getStorage(self.__name__, key) or default
- return self.core.db.getStorage(self.__name__, key)
-
- def retrieve(self, *args, **kwargs):
- """ same as `getStorage` """
- return self.getStorage(*args, **kwargs)
-
- def delStorage(self, key):
- """ Delete entry in db """
- self.core.db.delStorage(self.__name__, key)
-
-
-class Plugin(Base):
- """
- Base plugin for hoster/crypter.
- Overwrite `process` / `decrypt` in your subclassed plugin.
- """
- __name__ = "Plugin"
- __version__ = "0.4"
- __pattern__ = None
- __type__ = "hoster"
- __config__ = [("name", "type", "desc", "default")]
- __description__ = """Base Plugin"""
- __author_name__ = ("RaNaN", "spoob", "mkaay")
- __author_mail__ = ("RaNaN@pyload.org", "spoob@pyload.org", "mkaay@mkaay.de")
-
- def __init__(self, pyfile):
- Base.__init__(self, pyfile.m.core)
-
- self.wantReconnect = False
- #: enables simultaneous processing of multiple downloads
- self.multiDL = True
- self.limitDL = 0
- #: chunk limit
- self.chunkLimit = 1
- self.resumeDownload = False
-
- #: time() + wait in seconds
- self.waitUntil = 0
- self.waiting = False
-
- self.ocr = None #captcha reader instance
- #: account handler instance, see :py:class:`Account`
- self.account = pyfile.m.core.accountManager.getAccountPlugin(self.__name__)
-
- #: premium status
- self.premium = False
- #: username/login
- self.user = None
-
- if self.account and not self.account.canUse(): self.account = None
- if self.account:
- self.user, data = self.account.selectAccount()
- #: Browser instance, see `network.Browser`
- self.req = self.account.getAccountRequest(self.user)
- self.chunkLimit = -1 # chunk limit, -1 for unlimited
- #: enables resume (will be ignored if server dont accept chunks)
- self.resumeDownload = True
- self.multiDL = True #every hoster with account should provide multiple downloads
- #: premium status
- self.premium = self.account.isPremium(self.user)
- else:
- self.req = pyfile.m.core.requestFactory.getRequest(self.__name__)
-
- #: associated pyfile instance, see `PyFile`
- self.pyfile = pyfile
- self.thread = None # holds thread in future
-
- #: location where the last call to download was saved
- self.lastDownload = ""
- #: re match of the last call to `checkDownload`
- self.lastCheck = None
- #: js engine, see `JsEngine`
- self.js = self.core.js
- self.cTask = None #captcha task
-
- self.retries = 0 # amount of retries already made
- self.html = None # some plugins store html code here
-
- self.init()
-
- def getChunkCount(self):
- if self.chunkLimit <= 0:
- return self.config["download"]["chunks"]
- return min(self.config["download"]["chunks"], self.chunkLimit)
-
- def __call__(self):
- return self.__name__
-
- def init(self):
- """initialize the plugin (in addition to `__init__`)"""
- pass
-
- def setup(self):
- """ setup for enviroment and other things, called before downloading (possibly more than one time)"""
- pass
-
- def preprocessing(self, thread):
- """ handles important things to do before starting """
- self.thread = thread
-
- if self.account:
- self.account.checkLogin(self.user)
- else:
- self.req.clearCookies()
-
- self.setup()
-
- self.pyfile.setStatus("starting")
-
- return self.process(self.pyfile)
-
-
- def process(self, pyfile):
- """the 'main' method of every plugin, you **have to** overwrite it"""
- raise NotImplementedError
-
- def resetAccount(self):
- """ dont use account and retry download """
- self.account = None
- self.req = self.core.requestFactory.getRequest(self.__name__)
- self.retry()
-
- def checksum(self, local_file=None):
- """
- return codes:
- 0 - checksum ok
- 1 - checksum wrong
- 5 - can't get checksum
- 10 - not implemented
- 20 - unknown error
- """
- #@TODO checksum check hook
-
- return True, 10
-
-
- def setWait(self, seconds, reconnect=False):
- """Set a specific wait time later used with `wait`
-
- :param seconds: wait time in seconds
- :param reconnect: True if a reconnect would avoid wait time
- """
- if reconnect:
- self.wantReconnect = True
- self.pyfile.waitUntil = time() + int(seconds)
-
- def wait(self):
- """ waits the time previously set """
- self.waiting = True
- self.pyfile.setStatus("waiting")
-
- while self.pyfile.waitUntil > time():
- self.thread.m.reconnecting.wait(2)
-
- if self.pyfile.abort: raise Abort
- if self.thread.m.reconnecting.isSet():
- self.waiting = False
- self.wantReconnect = False
- raise Reconnect
-
- self.waiting = False
- self.pyfile.setStatus("starting")
-
- def fail(self, reason):
- """ fail and give reason """
- raise Fail(reason)
-
- def offline(self):
- """ fail and indicate file is offline """
- raise Fail("offline")
-
- def tempOffline(self):
- """ fail and indicates file ist temporary offline, the core may take consequences """
- raise Fail("temp. offline")
-
- def retry(self, max_tries=3, wait_time=1, reason=""):
- """Retries and begin again from the beginning
-
- :param max_tries: number of maximum retries
- :param wait_time: time to wait in seconds
- :param reason: reason for retrying, will be passed to fail if max_tries reached
- """
- if 0 < max_tries <= self.retries:
- if not reason: reason = "Max retries reached"
- raise Fail(reason)
-
- self.wantReconnect = False
- self.setWait(wait_time)
- self.wait()
-
- self.retries += 1
- raise Retry(reason)
-
- def invalidCaptcha(self):
- if self.cTask:
- self.cTask.invalid()
-
- def correctCaptcha(self):
- if self.cTask:
- self.cTask.correct()
-
- def decryptCaptcha(self, url, get={}, post={}, cookies=False, forceUser=False, imgtype='jpg',
- result_type='textual'):
- """ Loads a captcha and decrypts it with ocr, plugin, user input
-
- :param url: url of captcha image
- :param get: get part for request
- :param post: post part for request
- :param cookies: True if cookies should be enabled
- :param forceUser: if True, ocr is not used
- :param imgtype: Type of the Image
- :param result_type: 'textual' if text is written on the captcha\
- or 'positional' for captcha where the user have to click\
- on a specific region on the captcha
-
- :return: result of decrypting
- """
-
- img = self.load(url, get=get, post=post, cookies=cookies)
-
- id = ("%.2f" % time())[-6:].replace(".", "")
- temp_file = open(join("tmp", "tmpCaptcha_%s_%s.%s" % (self.__name__, id, imgtype)), "wb")
- temp_file.write(img)
- temp_file.close()
-
- has_plugin = self.__name__ in self.core.pluginManager.captchaPlugins
-
- if self.core.captcha:
- Ocr = self.core.pluginManager.loadClass("captcha", self.__name__)
- else:
- Ocr = None
-
- if Ocr and not forceUser:
- sleep(randint(3000, 5000) / 1000.0)
- if self.pyfile.abort: raise Abort
-
- ocr = Ocr()
- result = ocr.get_captcha(temp_file.name)
- else:
- captchaManager = self.core.captchaManager
- task = captchaManager.newTask(img, imgtype, temp_file.name, result_type)
- self.cTask = task
- captchaManager.handleCaptcha(task)
-
- while task.isWaiting():
- if self.pyfile.abort:
- captchaManager.removeTask(task)
- raise Abort
- sleep(1)
-
- captchaManager.removeTask(task)
-
- if task.error and has_plugin: #ignore default error message since the user could use OCR
- self.fail(_("Pil and tesseract not installed and no Client connected for captcha decrypting"))
- elif task.error:
- self.fail(task.error)
- elif not task.result:
- self.fail(_("No captcha result obtained in appropiate time by any of the plugins."))
-
- result = task.result
- self.log.debug("Received captcha result: %s" % str(result))
-
- if not self.core.debug:
- try:
- remove(temp_file.name)
- except:
- pass
-
- return result
-
-
- def load(self, url, get={}, post={}, ref=True, cookies=True, just_header=False, decode=False):
- """Load content at url and returns it
-
- :param url:
- :param get:
- :param post:
- :param ref:
- :param cookies:
- :param just_header: if True only the header will be retrieved and returned as dict
- :param decode: Wether to decode the output according to http header, should be True in most cases
- :return: Loaded content
- """
- if self.pyfile.abort: raise Abort
- #utf8 vs decode -> please use decode attribute in all future plugins
- if type(url) == unicode: url = str(url)
-
- res = self.req.load(url, get, post, ref, cookies, just_header, decode=decode)
-
- if self.core.debug:
- from inspect import currentframe
-
- frame = currentframe()
- if not exists(join("tmp", self.__name__)):
- makedirs(join("tmp", self.__name__))
-
- f = open(
- join("tmp", self.__name__, "%s_line%s.dump.html" % (frame.f_back.f_code.co_name, frame.f_back.f_lineno))
- , "wb")
- del frame # delete the frame or it wont be cleaned
-
- try:
- tmp = res.encode("utf8")
- except:
- tmp = res
-
- f.write(tmp)
- f.close()
-
- if just_header:
- #parse header
- header = {"code": self.req.code}
- for line in res.splitlines():
- line = line.strip()
- if not line or ":" not in line: continue
-
- key, none, value = line.partition(":")
- key = key.lower().strip()
- value = value.strip()
-
- if key in header:
- if type(header[key]) == list:
- header[key].append(value)
- else:
- header[key] = [header[key], value]
- else:
- header[key] = value
- res = header
-
- return res
-
- def download(self, url, get={}, post={}, ref=True, cookies=True, disposition=False):
- """Downloads the content at url to download folder
-
- :param url:
- :param get:
- :param post:
- :param ref:
- :param cookies:
- :param disposition: if True and server provides content-disposition header\
- the filename will be changed if needed
- :return: The location where the file was saved
- """
-
- self.checkForSameFiles()
-
- self.pyfile.setStatus("downloading")
-
- download_folder = self.config['general']['download_folder']
-
- location = save_join(download_folder, self.pyfile.package().folder)
-
- if not exists(location):
- makedirs(location, int(self.core.config["permission"]["folder"], 8))
-
- if self.core.config["permission"]["change_dl"] and os.name != "nt":
- try:
- uid = getpwnam(self.config["permission"]["user"])[2]
- gid = getgrnam(self.config["permission"]["group"])[2]
-
- chown(location, uid, gid)
- except Exception, e:
- self.log.warning(_("Setting User and Group failed: %s") % str(e))
-
- # convert back to unicode
- location = fs_decode(location)
- name = save_path(self.pyfile.name)
-
- filename = join(location, name)
-
- self.core.hookManager.dispatchEvent("downloadStarts", self.pyfile, url, filename)
-
- try:
- newname = self.req.httpDownload(url, filename, get=get, post=post, ref=ref, cookies=cookies,
- chunks=self.getChunkCount(), resume=self.resumeDownload,
- progressNotify=self.pyfile.setProgress, disposition=disposition)
- finally:
- self.pyfile.size = self.req.size
-
- if disposition and newname and newname != name: #triple check, just to be sure
- self.log.info("%(name)s saved as %(newname)s" % {"name": name, "newname": newname})
- self.pyfile.name = newname
- filename = join(location, newname)
-
- fs_filename = fs_encode(filename)
-
- if self.core.config["permission"]["change_file"]:
- chmod(fs_filename, int(self.core.config["permission"]["file"], 8))
-
- if self.core.config["permission"]["change_dl"] and os.name != "nt":
- try:
- uid = getpwnam(self.config["permission"]["user"])[2]
- gid = getgrnam(self.config["permission"]["group"])[2]
-
- chown(fs_filename, uid, gid)
- except Exception, e:
- self.log.warning(_("Setting User and Group failed: %s") % str(e))
-
- self.lastDownload = filename
- return self.lastDownload
-
- def checkDownload(self, rules, api_size=0, max_size=50000, delete=True, read_size=0):
- """ checks the content of the last downloaded file, re match is saved to `lastCheck`
-
- :param rules: dict with names and rules to match (compiled regexp or strings)
- :param api_size: expected file size
- :param max_size: if the file is larger then it wont be checked
- :param delete: delete if matched
- :param read_size: amount of bytes to read from files larger then max_size
- :return: dictionary key of the first rule that matched
- """
- lastDownload = fs_encode(self.lastDownload)
- if not exists(lastDownload): return None
-
- size = stat(lastDownload)
- size = size.st_size
-
- if api_size and api_size <= size: return None
- elif size > max_size and not read_size: return None
- self.log.debug("Download Check triggered")
- f = open(lastDownload, "rb")
- content = f.read(read_size if read_size else -1)
- f.close()
- #produces encoding errors, better log to other file in the future?
- #self.log.debug("Content: %s" % content)
- for name, rule in rules.iteritems():
- if type(rule) in (str, unicode):
- if rule in content:
- if delete:
- remove(lastDownload)
- return name
- elif hasattr(rule, "search"):
- m = rule.search(content)
- if m:
- if delete:
- remove(lastDownload)
- self.lastCheck = m
- return name
-
-
- def getPassword(self):
- """ get the password the user provided in the package"""
- password = self.pyfile.package().password
- if not password: return ""
- return password
-
-
- def checkForSameFiles(self, starting=False):
- """ checks if same file was/is downloaded within same package
-
- :param starting: indicates that the current download is going to start
- :raises SkipDownload:
- """
-
- pack = self.pyfile.package()
-
- for pyfile in self.core.files.cache.values():
- if pyfile != self.pyfile and pyfile.name == self.pyfile.name and pyfile.package().folder == pack.folder:
- if pyfile.status in (0, 12): #finished or downloading
- raise SkipDownload(pyfile.pluginname)
- elif pyfile.status in (
- 5, 7) and starting: #a download is waiting/starting and was appenrently started before
- raise SkipDownload(pyfile.pluginname)
-
- download_folder = self.config['general']['download_folder']
- location = save_join(download_folder, pack.folder, self.pyfile.name)
-
- if starting and self.core.config['download']['skip_existing'] and exists(location):
- size = os.stat(location).st_size
- if size >= self.pyfile.size:
- raise SkipDownload("File exists.")
-
- pyfile = self.core.db.findDuplicates(self.pyfile.id, self.pyfile.package().folder, self.pyfile.name)
- if pyfile:
- if exists(location):
- raise SkipDownload(pyfile[0])
-
- self.log.debug("File %s not skipped, because it does not exists." % self.pyfile.name)
-
- def clean(self):
- """ clean everything and remove references """
- if hasattr(self, "pyfile"):
- del self.pyfile
- if hasattr(self, "req"):
- self.req.close()
- del self.req
- if hasattr(self, "thread"):
- del self.thread
- if hasattr(self, "html"):
- del self.html
diff --git a/module/plugins/PluginManager.py b/module/plugins/PluginManager.py
deleted file mode 100644
index f3f5f47bc..000000000
--- a/module/plugins/PluginManager.py
+++ /dev/null
@@ -1,380 +0,0 @@
-# -*- 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: mkaay, RaNaN
-"""
-
-import re
-import sys
-
-from os import listdir, makedirs
-from os.path import isfile, join, exists, abspath
-from sys import version_info
-from itertools import chain
-from traceback import print_exc
-
-from module.lib.SafeEval import const_eval as literal_eval
-from module.ConfigParser import IGNORE
-
-class PluginManager:
- ROOT = "module.plugins."
- USERROOT = "userplugins."
- TYPES = ("crypter", "container", "hoster", "captcha", "accounts", "hooks", "internal")
-
- PATTERN = re.compile(r'__pattern__.*=.*r("|\')([^"\']+)')
- VERSION = re.compile(r'__version__.*=.*("|\')([0-9.]+)')
- CONFIG = re.compile(r'__config__.*=.*\[([^\]]+)', re.MULTILINE)
- DESC = re.compile(r'__description__.?=.?("|"""|\')([^"\']+)')
-
-
- def __init__(self, core):
- self.core = core
-
- #self.config = self.core.config
- self.log = core.log
-
- self.plugins = {}
- self.createIndex()
-
- #register for import hook
- sys.meta_path.append(self)
-
-
- def createIndex(self):
- """create information for all plugins available"""
-
- sys.path.append(abspath(""))
-
- if not exists("userplugins"):
- makedirs("userplugins")
- if not exists(join("userplugins", "__init__.py")):
- f = open(join("userplugins", "__init__.py"), "wb")
- f.close()
-
- self.plugins["crypter"] = self.crypterPlugins = self.parse("crypter", pattern=True)
- self.plugins["container"] = self.containerPlugins = self.parse("container", pattern=True)
- self.plugins["hoster"] = self.hosterPlugins = self.parse("hoster", pattern=True)
-
- self.plugins["captcha"] = self.captchaPlugins = self.parse("captcha")
- self.plugins["accounts"] = self.accountPlugins = self.parse("accounts")
- self.plugins["hooks"] = self.hookPlugins = self.parse("hooks")
- self.plugins["internal"] = self.internalPlugins = self.parse("internal")
-
- self.log.debug("created index of plugins")
-
- def parse(self, folder, pattern=False, home={}):
- """
- returns dict with information
- home contains parsed plugins from module.
-
- {
- name : {path, version, config, (pattern, re), (plugin, class)}
- }
-
- """
- plugins = {}
- if home:
- pfolder = join("userplugins", folder)
- if not exists(pfolder):
- makedirs(pfolder)
- if not exists(join(pfolder, "__init__.py")):
- f = open(join(pfolder, "__init__.py"), "wb")
- f.close()
-
- else:
- pfolder = join(pypath, "module", "plugins", folder)
-
- for f in listdir(pfolder):
- if (isfile(join(pfolder, f)) and f.endswith(".py") or f.endswith("_25.pyc") or f.endswith(
- "_26.pyc") or f.endswith("_27.pyc")) and not f.startswith("_"):
- data = open(join(pfolder, f))
- content = data.read()
- data.close()
-
- if f.endswith("_25.pyc") and version_info[0:2] != (2, 5):
- continue
- elif f.endswith("_26.pyc") and version_info[0:2] != (2, 6):
- continue
- elif f.endswith("_27.pyc") and version_info[0:2] != (2, 7):
- continue
-
- name = f[:-3]
- if name[-1] == ".": name = name[:-4]
-
- version = self.VERSION.findall(content)
- if version:
- version = float(version[0][1])
- else:
- version = 0
-
- # home contains plugins from pyload root
- if home and name in home:
- if home[name]["v"] >= version:
- continue
-
- if name in IGNORE or (folder, name) in IGNORE:
- continue
-
- plugins[name] = {}
- plugins[name]["v"] = version
-
- module = f.replace(".pyc", "").replace(".py", "")
-
- # the plugin is loaded from user directory
- plugins[name]["user"] = True if home else False
- plugins[name]["name"] = module
-
- if pattern:
- pattern = self.PATTERN.findall(content)
-
- if pattern:
- pattern = pattern[0][1]
- else:
- pattern = "^unmachtable$"
-
- plugins[name]["pattern"] = pattern
-
- try:
- plugins[name]["re"] = re.compile(pattern)
- except:
- self.log.error(_("%s has a invalid pattern.") % name)
-
-
- # internals have no config
- if folder == "internal":
- self.core.config.deleteConfig(name)
- continue
-
- config = self.CONFIG.findall(content)
- if config:
- config = literal_eval(config[0].strip().replace("\n", "").replace("\r", ""))
- desc = self.DESC.findall(content)
- desc = desc[0][1] if desc else ""
-
- if type(config[0]) == tuple:
- config = [list(x) for x in config]
- else:
- config = [list(config)]
-
- if folder == "hooks":
- append = True
- for item in config:
- if item[0] == "activated": append = False
-
- # activated flag missing
- if append: config.append(["activated", "bool", "Activated", False])
-
- try:
- self.core.config.addPluginConfig(name, config, desc)
- except:
- self.log.error("Invalid config in %s: %s" % (name, config))
-
- elif folder == "hooks": #force config creation
- desc = self.DESC.findall(content)
- desc = desc[0][1] if desc else ""
- config = (["activated", "bool", "Activated", False],)
-
- try:
- self.core.config.addPluginConfig(name, config, desc)
- except:
- self.log.error("Invalid config in %s: %s" % (name, config))
-
- if not home:
- temp = self.parse(folder, pattern, plugins)
- plugins.update(temp)
-
- return plugins
-
-
- def parseUrls(self, urls):
- """parse plugins for given list of urls"""
-
- last = None
- res = [] # tupels of (url, plugin)
-
- for url in urls:
- if type(url) not in (str, unicode, buffer): continue
- found = False
-
- if last and last[1]["re"].match(url):
- res.append((url, last[0]))
- continue
-
- for name, value in chain(self.crypterPlugins.iteritems(), self.hosterPlugins.iteritems(),
- self.containerPlugins.iteritems()):
- if value["re"].match(url):
- res.append((url, name))
- last = (name, value)
- found = True
- break
-
- if not found:
- res.append((url, "BasePlugin"))
-
- return res
-
- def findPlugin(self, name, pluginlist=("hoster", "crypter", "container")):
- for ptype in pluginlist:
- if name in self.plugins[ptype]:
- return self.plugins[ptype][name], ptype
- return None, None
-
- def getPlugin(self, name, original=False):
- """return plugin module from hoster|decrypter|container"""
- plugin, type = self.findPlugin(name)
-
- if not plugin:
- self.log.warning("Plugin %s not found." % name)
- plugin = self.hosterPlugins["BasePlugin"]
-
- if "new_module" in plugin and not original:
- return plugin["new_module"]
-
- return self.loadModule(type, name)
-
- def getPluginName(self, name):
- """ used to obtain new name if other plugin was injected"""
- plugin, type = self.findPlugin(name)
-
- if "new_name" in plugin:
- return plugin["new_name"]
-
- return name
-
- def loadModule(self, type, name):
- """ Returns loaded module for plugin
-
- :param type: plugin type, subfolder of module.plugins
- :param name:
- """
- plugins = self.plugins[type]
- if name in plugins:
- if "module" in plugins[name]: return plugins[name]["module"]
- try:
- module = __import__(self.ROOT + "%s.%s" % (type, plugins[name]["name"]), globals(), locals(),
- plugins[name]["name"])
- plugins[name]["module"] = module #cache import, maybe unneeded
- return module
- except Exception, e:
- self.log.error(_("Error importing %(name)s: %(msg)s") % {"name": name, "msg": str(e)})
- if self.core.debug:
- print_exc()
-
- def loadClass(self, type, name):
- """Returns the class of a plugin with the same name"""
- module = self.loadModule(type, name)
- if module: return getattr(module, name)
-
- def getAccountPlugins(self):
- """return list of account plugin names"""
- return self.accountPlugins.keys()
-
- def find_module(self, fullname, path=None):
- #redirecting imports if necesarry
- if fullname.startswith(self.ROOT) or fullname.startswith(self.USERROOT): #seperate pyload plugins
- if fullname.startswith(self.USERROOT): user = 1
- else: user = 0 #used as bool and int
-
- split = fullname.split(".")
- if len(split) != 4 - user: return
- type, name = split[2 - user:4 - user]
-
- if type in self.plugins and name in self.plugins[type]:
- #userplugin is a newer version
- if not user and self.plugins[type][name]["user"]:
- return self
- #imported from userdir, but pyloads is newer
- if user and not self.plugins[type][name]["user"]:
- return self
-
-
- def load_module(self, name, replace=True):
- if name not in sys.modules: #could be already in modules
- if replace:
- if self.ROOT in name:
- newname = name.replace(self.ROOT, self.USERROOT)
- else:
- newname = name.replace(self.USERROOT, self.ROOT)
- else: newname = name
-
- base, plugin = newname.rsplit(".", 1)
-
- self.log.debug("Redirected import %s -> %s" % (name, newname))
-
- module = __import__(newname, globals(), locals(), [plugin])
- #inject under new an old name
- sys.modules[name] = module
- sys.modules[newname] = module
-
- return sys.modules[name]
-
-
- def reloadPlugins(self, type_plugins):
- """ reloads and reindexes plugins """
- if not type_plugins: return False
-
- self.log.debug("Request reload of plugins: %s" % type_plugins)
-
- as_dict = {}
- for t,n in type_plugins:
- if t in as_dict:
- as_dict[t].append(n)
- else:
- as_dict[t] = [n]
-
- # we do not reload hooks or internals, would cause to much side effects
- if "hooks" in as_dict or "internal" in as_dict:
- return False
-
- for type in as_dict.iterkeys():
- for plugin in as_dict[type]:
- if plugin in self.plugins[type]:
- if "module" in self.plugins[type][plugin]:
- self.log.debug("Reloading %s" % plugin)
- reload(self.plugins[type][plugin]["module"])
-
- #index creation
- self.plugins["crypter"] = self.crypterPlugins = self.parse("crypter", pattern=True)
- self.plugins["container"] = self.containerPlugins = self.parse("container", pattern=True)
- self.plugins["hoster"] = self.hosterPlugins = self.parse("hoster", pattern=True)
- self.plugins["captcha"] = self.captchaPlugins = self.parse("captcha")
- self.plugins["accounts"] = self.accountPlugins = self.parse("accounts")
-
- if "accounts" in as_dict: #accounts needs to be reloaded
- self.core.accountManager.initPlugins()
- self.core.scheduler.addJob(0, self.core.accountManager.getAccountInfos)
-
- return True
-
-
-
-if __name__ == "__main__":
- _ = lambda x: x
- pypath = "/home/christian/Projekte/pyload-0.4/module/plugins"
-
- from time import time
-
- p = PluginManager(None)
-
- a = time()
-
- test = ["http://www.youtube.com/watch?v=%s" % x for x in range(0, 100)]
- print p.parseUrls(test)
-
- b = time()
-
- print b - a, "s"
-
diff --git a/module/plugins/__init__.py b/module/plugins/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/plugins/__init__.py
+++ /dev/null
diff --git a/module/plugins/accounts/AlldebridCom.py b/module/plugins/accounts/AlldebridCom.py
deleted file mode 100644
index 1fb5e4b54..000000000
--- a/module/plugins/accounts/AlldebridCom.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-import xml.dom.minidom as dom
-
-from time import time
-from urllib import urlencode
-
-from BeautifulSoup import BeautifulSoup
-
-from module.plugins.Account import Account
-
-
-class AlldebridCom(Account):
- __name__ = "AlldebridCom"
- __type__ = "account"
- __version__ = "0.22"
-
- __description__ = """AllDebrid.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Andy Voigt", "spamsales@online.de")]
-
-
- def loadAccountInfo(self, user, req):
- data = self.getAccountData(user)
- page = req.load("http://www.alldebrid.com/account/")
- soup = BeautifulSoup(page)
- #Try to parse expiration date directly from the control panel page (better accuracy)
- try:
- time_text = soup.find('div', attrs={'class': 'remaining_time_text'}).strong.string
- self.logDebug("Account expires in: %s" % time_text)
- p = re.compile('\d+')
- exp_data = p.findall(time_text)
- exp_time = time() + int(exp_data[0]) * 24 * 60 * 60 + int(
- exp_data[1]) * 60 * 60 + (int(exp_data[2]) - 1) * 60
- #Get expiration date from API
- except:
- data = self.getAccountData(user)
- page = req.load("http://www.alldebrid.com/api.php",
- get={'action': "info_user", 'login': user, 'pw': data['password']})
- self.logDebug(page)
- xml = dom.parseString(page)
- exp_time = time() + int(xml.getElementsByTagName("date")[0].childNodes[0].nodeValue) * 24 * 60 * 60
- account_info = {"validuntil": exp_time, "trafficleft": -1}
- return account_info
-
-
- def login(self, user, data, req):
- urlparams = urlencode({'action': 'login', 'login_login': user, 'login_password': data['password']})
- page = req.load("http://www.alldebrid.com/register/?%s" % urlparams)
-
- if "This login doesn't exist" in page:
- self.wrongPassword()
-
- if "The password is not valid" in page:
- self.wrongPassword()
-
- if "Invalid captcha" in page:
- self.wrongPassword()
diff --git a/module/plugins/accounts/BayfilesCom.py b/module/plugins/accounts/BayfilesCom.py
deleted file mode 100644
index b2295c3f1..000000000
--- a/module/plugins/accounts/BayfilesCom.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from time import time
-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
-
-
-class BayfilesCom(Account):
- __name__ = "BayfilesCom"
- __type__ = "account"
- __version__ = "0.03"
-
- __description__ = """Bayfiles.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- def loadAccountInfo(self, user, req):
- for _i in xrange(2):
- res = json_loads(req.load("http://api.bayfiles.com/v1/account/info"))
- self.logDebug(res)
- if not res['error']:
- break
- self.logWarning(res['error'])
- self.relogin(user)
-
- return {"premium": bool(res['premium']), "trafficleft": -1,
- "validuntil": res['expires'] if res['expires'] >= int(time()) else -1}
-
-
- def login(self, user, data, req):
- res = json_loads(req.load("http://api.bayfiles.com/v1/account/login/%s/%s" % (user, data['password'])))
- self.logDebug(res)
- if res['error']:
- self.logError(res['error'])
- self.wrongPassword()
diff --git a/module/plugins/accounts/BillionuploadsCom.py b/module/plugins/accounts/BillionuploadsCom.py
deleted file mode 100644
index 11af36591..000000000
--- a/module/plugins/accounts/BillionuploadsCom.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class BillionuploadsCom(XFSAccount):
- __name__ = "BillionuploadsCom"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Billionuploads.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "billionuploads.com"
diff --git a/module/plugins/accounts/BitshareCom.py b/module/plugins/accounts/BitshareCom.py
deleted file mode 100644
index 8da3764ff..000000000
--- a/module/plugins/accounts/BitshareCom.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-
-
-class BitshareCom(Account):
- __name__ = "BitshareCom"
- __type__ = "account"
- __version__ = "0.12"
-
- __description__ = """Bitshare account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Paul King", None)]
-
-
- def loadAccountInfo(self, user, req):
- page = req.load("http://bitshare.com/mysettings.html")
-
- if "\"http://bitshare.com/myupgrade.html\">Free" in page:
- return {"validuntil": -1, "trafficleft": -1, "premium": False}
-
- if not '<input type="checkbox" name="directdownload" checked="checked" />' in page:
- self.logWarning(_("Activate direct Download in your Bitshare Account"))
-
- return {"validuntil": -1, "trafficleft": -1, "premium": True}
-
-
- def login(self, user, data, req):
- page = req.load("http://bitshare.com/login.html",
- post={"user": user, "password": data['password'], "submit": "Login"}, cookies=True)
- if "login" in req.lastEffectiveURL:
- self.wrongPassword()
diff --git a/module/plugins/accounts/CatShareNet.py b/module/plugins/accounts/CatShareNet.py
deleted file mode 100644
index c33219685..000000000
--- a/module/plugins/accounts/CatShareNet.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import mktime, strptime
-
-from module.plugins.Account import Account
-
-
-class CatShareNet(Account):
- __name__ = "CatShareNet"
- __type__ = "account"
- __version__ = "0.01"
-
- __description__ = """CatShareNet account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("prOq", None)]
-
-
- PREMIUM_PATTERN = r'class="nav-collapse collapse pull-right">[\s\w<>=-."/:]*\sz.</a></li>\s*<li><a href="/premium">.*\s*<span style="color: red">(.*?)</span>[\s\w<>/]*href="/logout"'
- VALID_UNTIL_PATTERN = r'<div class="span6 pull-right">[\s\w<>=-":;]*<span style="font-size:13px;">.*?<strong>(.*?)</strong></span>'
-
-
- def loadAccountInfo(self, user, req):
- premium = False
- validuntil = -1
-
- html = req.load("http://catshare.net/", decode=True)
-
- try:
- m = re.search(self.PREMIUM_PATTERN, html)
- if "Premium" in m.group(1):
- premium = True
- except:
- pass
-
- try:
- m = re.search(self.VALID_UNTIL_PATTERN, html)
- expiredate = m.group(1)
- if "-" not in expiredate:
- validuntil = mktime(strptime(expiredate, "%d.%m.%Y"))
- except:
- pass
-
- return {'premium': premium, 'trafficleft': -1, 'validuntil': validuntil}
-
-
- def login(self, user, data, req):
- html = req.load("http://catshare.net/login",
- post={'user_email': user,
- 'user_password': data['password'],
- 'remindPassword': 0,
- 'user[submit]': "Login"})
-
- if not '<a href="/logout">Wyloguj</a>' in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/CramitIn.py b/module/plugins/accounts/CramitIn.py
deleted file mode 100644
index a9e2274a2..000000000
--- a/module/plugins/accounts/CramitIn.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class CramitIn(XFSAccount):
- __name__ = "CramitIn"
- __type__ = "account"
- __version__ = "0.03"
-
- __description__ = """Cramit.in account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- HOSTER_DOMAIN = "cramit.in"
diff --git a/module/plugins/accounts/CzshareCom.py b/module/plugins/accounts/CzshareCom.py
deleted file mode 100644
index 844ec9999..000000000
--- a/module/plugins/accounts/CzshareCom.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from time import mktime, strptime
-import re
-
-from module.plugins.Account import Account
-
-
-class CzshareCom(Account):
- __name__ = "CzshareCom"
- __type__ = "account"
- __version__ = "0.14"
-
- __description__ = """Czshare.com account plugin, now Sdilej.cz"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- CREDIT_LEFT_PATTERN = r'<tr class="active">\s*<td>([\d ,]+) (KiB|MiB|GiB)</td>\s*<td>([^<]*)</td>\s*</tr>'
-
-
- def loadAccountInfo(self, user, req):
- html = req.load("http://sdilej.cz/prehled_kreditu/")
-
- m = re.search(self.CREDIT_LEFT_PATTERN, html)
- if m is None:
- return {"validuntil": 0, "trafficleft": 0}
- else:
- credits = float(m.group(1).replace(' ', '').replace(',', '.'))
- credits = credits * 1024 ** {'KiB': 0, 'MiB': 1, 'GiB': 2}[m.group(2)]
- validuntil = mktime(strptime(m.group(3), '%d.%m.%y %H:%M'))
- return {"validuntil": validuntil, "trafficleft": credits}
-
-
- def login(self, user, data, req):
- html = req.load('https://sdilej.cz/index.php', post={
- "Prihlasit": "Prihlasit",
- "login-password": data['password'],
- "login-name": user
- })
-
- if '<div class="login' in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/DebridItaliaCom.py b/module/plugins/accounts/DebridItaliaCom.py
deleted file mode 100644
index e9d4964cc..000000000
--- a/module/plugins/accounts/DebridItaliaCom.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import mktime, strptime
-
-from module.plugins.Account import Account
-
-
-class DebridItaliaCom(Account):
- __name__ = "DebridItaliaCom"
- __type__ = "account"
- __version__ = "0.11"
-
- __description__ = """Debriditalia.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- WALID_UNTIL_PATTERN = r'Premium valid till: (.+?) \|'
-
-
- def loadAccountInfo(self, user, req):
- info = {"premium": False, "validuntil": None, "trafficleft": None}
- html = req.load("http://debriditalia.com/")
-
- if 'Account premium not activated' not in html:
- m = re.search(self.WALID_UNTIL_PATTERN, html)
- if m:
- validuntil = int(mktime(strptime(m.group(1), "%d/%m/%Y %H:%M")))
- info = {"premium": True, "validuntil": validuntil, "trafficleft": -1}
- else:
- self.logError(_("Unable to retrieve account information"))
-
- return info
-
-
- def login(self, user, data, req):
- html = req.load("http://debriditalia.com/login.php",
- get={'u': user, 'p': data['password']})
-
- if 'NO' in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/DepositfilesCom.py b/module/plugins/accounts/DepositfilesCom.py
deleted file mode 100644
index 437265c3f..000000000
--- a/module/plugins/accounts/DepositfilesCom.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import strptime, mktime
-
-from module.plugins.Account import Account
-
-
-class DepositfilesCom(Account):
- __name__ = "DepositfilesCom"
- __type__ = "account"
- __version__ = "0.3"
-
- __description__ = """Depositfiles.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("mkaay", "mkaay@mkaay.de"),
- ("stickell", "l.stickell@yahoo.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- def loadAccountInfo(self, user, req):
- html = req.load("https://dfiles.eu/de/gold/")
- validuntil = re.search(r"Sie haben Gold Zugang bis: <b>(.*?)</b></div>", html).group(1)
-
- validuntil = int(mktime(strptime(validuntil, "%Y-%m-%d %H:%M:%S")))
-
- return {"validuntil": validuntil, "trafficleft": -1}
-
-
- def login(self, user, data, req):
- html = req.load("https://dfiles.eu/de/login.php", get={"return": "/de/gold/payment.php"},
- post={"login": user, "password": data['password']})
- if r'<div class="error_message">Sie haben eine falsche Benutzername-Passwort-Kombination verwendet.</div>' in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/EasybytezCom.py b/module/plugins/accounts/EasybytezCom.py
deleted file mode 100644
index 93d3e2c19..000000000
--- a/module/plugins/accounts/EasybytezCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class EasybytezCom(XFSAccount):
- __name__ = "EasybytezCom"
- __type__ = "account"
- __version__ = "0.12"
-
- __description__ = """EasyBytez.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("guidobelix", "guidobelix@hotmail.it")]
-
-
- HOSTER_DOMAIN = "easybytez.com"
diff --git a/module/plugins/accounts/EuroshareEu.py b/module/plugins/accounts/EuroshareEu.py
deleted file mode 100644
index c75f8ee33..000000000
--- a/module/plugins/accounts/EuroshareEu.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from time import mktime, strptime
-import re
-
-from module.plugins.Account import Account
-
-
-class EuroshareEu(Account):
- __name__ = "EuroshareEu"
- __type__ = "account"
- __version__ = "0.01"
-
- __description__ = """Euroshare.eu account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- def loadAccountInfo(self, user, req):
- self.relogin(user)
- html = req.load("http://euroshare.eu/customer-zone/settings/")
-
- m = re.search('id="input_expire_date" value="(\d+\.\d+\.\d+ \d+:\d+)"', html)
- if m is None:
- premium, validuntil = False, -1
- else:
- premium = True
- validuntil = mktime(strptime(m.group(1), "%d.%m.%Y %H:%M"))
-
- return {"validuntil": validuntil, "trafficleft": -1, "premium": premium}
-
-
- def login(self, user, data, req):
- html = req.load('http://euroshare.eu/customer-zone/login/', post={
- "trvale": "1",
- "login": user,
- "password": data['password']
- }, decode=True)
-
- if u">Nesprávne prihlasovacie meno alebo heslo" in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/FastixRu.py b/module/plugins/accounts/FastixRu.py
deleted file mode 100644
index ced49125a..000000000
--- a/module/plugins/accounts/FastixRu.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
-
-
-class FastixRu(Account):
- __name__ = "FastixRu"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Fastix account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Massimo Rosamilia", "max@spiritix.eu")]
-
-
- def loadAccountInfo(self, user, req):
- data = self.getAccountData(user)
- page = json_loads(req.load("http://fastix.ru/api_v2/", get={'apikey': data['api'], 'sub': "getaccountdetails"}))
-
- points = page['points']
- kb = float(points) * 1024 ** 2 / 1000
-
- if points > 0:
- account_info = {"validuntil": -1, "trafficleft": kb}
- else:
- account_info = {"validuntil": None, "trafficleft": None, "premium": False}
- return account_info
-
-
- def login(self, user, data, req):
- page = req.load("http://fastix.ru/api_v2/",
- get={'sub': "get_apikey", 'email': user, 'password': data['password']})
- api = json_loads(page)
- api = api['apikey']
- data['api'] = api
- if "error_code" in page:
- self.wrongPassword()
diff --git a/module/plugins/accounts/FastshareCz.py b/module/plugins/accounts/FastshareCz.py
deleted file mode 100644
index d6e94f2e3..000000000
--- a/module/plugins/accounts/FastshareCz.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Account import Account
-
-
-class FastshareCz(Account):
- __name__ = "FastshareCz"
- __type__ = "account"
- __version__ = "0.05"
-
- __description__ = """Fastshare.cz account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- CREDIT_PATTERN = r'My account\s*\((.+?)\)'
-
-
- def loadAccountInfo(self, user, req):
- validuntil = None
- trafficleft = None
- premium = None
-
- html = req.load("http://www.fastshare.cz/user", decode=True)
-
- m = re.search(self.CREDIT_PATTERN, html)
- if m:
- trafficleft = self.parseTraffic(m.group(1))
-
- if trafficleft:
- premium = True
- validuntil = -1
- else:
- premium = False
-
- return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium}
-
-
- def login(self, user, data, req):
- req.cj.setCookie("fastshare.cz", "lang", "en")
-
- req.load('http://www.fastshare.cz/login') # Do not remove or it will not login
-
- html = req.load("http://www.fastshare.cz/sql.php",
- post={'login': user, 'heslo': data['password']},
- decode=True)
-
- if ">Wrong username or password" in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/File4safeCom.py b/module/plugins/accounts/File4safeCom.py
deleted file mode 100644
index 20053d895..000000000
--- a/module/plugins/accounts/File4safeCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class File4safeCom(XFSAccount):
- __name__ = "File4safeCom"
- __type__ = "account"
- __version__ = "0.04"
-
- __description__ = """File4safe.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- HOSTER_DOMAIN = "file4safe.com"
-
- LOGIN_FAIL_PATTERN = r'input_login'
diff --git a/module/plugins/accounts/FileParadoxIn.py b/module/plugins/accounts/FileParadoxIn.py
deleted file mode 100644
index c12d99d6a..000000000
--- a/module/plugins/accounts/FileParadoxIn.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class FileParadoxIn(XFSAccount):
- __name__ = "FileParadoxIn"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """FileParadox.in account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "fileparadox.in"
diff --git a/module/plugins/accounts/FilecloudIo.py b/module/plugins/accounts/FilecloudIo.py
deleted file mode 100644
index c001d4513..000000000
--- a/module/plugins/accounts/FilecloudIo.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
-
-
-class FilecloudIo(Account):
- __name__ = "FilecloudIo"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """FilecloudIo account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- def loadAccountInfo(self, user, req):
- # It looks like the first API request always fails, so we retry 5 times, it should work on the second try
- for _i in xrange(5):
- rep = req.load("https://secure.filecloud.io/api-fetch_apikey.api",
- post={"username": user, "password": self.accounts[user]['password']})
- rep = json_loads(rep)
- if rep['status'] == 'ok':
- break
- elif rep['status'] == 'error' and rep['message'] == 'no such user or wrong password':
- self.logError(_("Wrong username or password"))
- return {"valid": False, "premium": False}
- else:
- return {"premium": False}
-
- akey = rep['akey']
- self.accounts[user]['akey'] = akey # Saved for hoster plugin
- rep = req.load("http://api.filecloud.io/api-fetch_account_details.api",
- post={"akey": akey})
- rep = json_loads(rep)
-
- if rep['is_premium'] == 1:
- return {"validuntil": int(rep['premium_until']), "trafficleft": -1}
- else:
- return {"premium": False}
-
-
- def login(self, user, data, req):
- req.cj.setCookie("secure.filecloud.io", "lang", "en")
- html = req.load('https://secure.filecloud.io/user-login.html')
-
- if not hasattr(self, "form_data"):
- self.form_data = {}
-
- self.form_data['username'] = user
- self.form_data['password'] = data['password']
-
- html = req.load('https://secure.filecloud.io/user-login_p.html',
- post=self.form_data,
- multipart=True)
-
- self.logged_in = True if "you have successfully logged in - filecloud.io" in html else False
- self.form_data = {}
diff --git a/module/plugins/accounts/FilefactoryCom.py b/module/plugins/accounts/FilefactoryCom.py
deleted file mode 100644
index a61db30c8..000000000
--- a/module/plugins/accounts/FilefactoryCom.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from time import mktime, strptime
-
-from pycurl import REFERER
-
-from module.plugins.Account import Account
-
-
-class FilefactoryCom(Account):
- __name__ = "FilefactoryCom"
- __type__ = "account"
- __version__ = "0.14"
-
- __description__ = """Filefactory.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- VALID_UNTIL_PATTERN = r'Premium valid until: <strong>(?P<d>\d{1,2})\w{1,2} (?P<m>\w{3}), (?P<y>\d{4})</strong>'
-
-
- def loadAccountInfo(self, user, req):
- html = req.load("http://www.filefactory.com/account/")
-
- m = re.search(self.VALID_UNTIL_PATTERN, html)
- if m:
- premium = True
- validuntil = re.sub(self.VALID_UNTIL_PATTERN, '\g<d> \g<m> \g<y>', m.group(0))
- validuntil = mktime(strptime(validuntil, "%d %b %Y"))
- else:
- premium = False
- validuntil = -1
-
- return {"premium": premium, "trafficleft": -1, "validuntil": validuntil}
-
-
- def login(self, user, data, req):
- req.http.c.setopt(REFERER, "http://www.filefactory.com/member/login.php")
-
- html = req.load("http://www.filefactory.com/member/signin.php", post={
- "loginEmail": user,
- "loginPassword": data['password'],
- "Submit": "Sign In"})
-
- if req.lastEffectiveURL != "http://www.filefactory.com/account/":
- self.wrongPassword()
diff --git a/module/plugins/accounts/FilejungleCom.py b/module/plugins/accounts/FilejungleCom.py
deleted file mode 100644
index a3ec7af64..000000000
--- a/module/plugins/accounts/FilejungleCom.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from time import mktime, strptime
-
-from module.plugins.Account import Account
-
-
-class FilejungleCom(Account):
- __name__ = "FilejungleCom"
- __type__ = "account"
- __version__ = "0.11"
-
- __description__ = """Filejungle.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- login_timeout = 60
-
- URL = "http://filejungle.com/"
- TRAFFIC_LEFT_PATTERN = r'"/extend_premium\.php">Until (\d+ \w+ \d+)<br'
- LOGIN_FAILED_PATTERN = r'<span htmlfor="loginUser(Name|Password)" generated="true" class="fail_info">'
-
-
- def loadAccountInfo(self, user, req):
- html = req.load(self.URL + "dashboard.php")
- m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
- if m:
- premium = True
- validuntil = mktime(strptime(m.group(1), "%d %b %Y"))
- else:
- premium = False
- validuntil = -1
-
- return {"premium": premium, "trafficleft": -1, "validuntil": validuntil}
-
-
- def login(self, user, data, req):
- html = req.load(self.URL + "login.php", post={
- "loginUserName": user,
- "loginUserPassword": data['password'],
- "loginFormSubmit": "Login",
- "recaptcha_challenge_field": "",
- "recaptcha_response_field": "",
- "recaptcha_shortencode_field": ""})
-
- if re.search(self.LOGIN_FAILED_PATTERN, html):
- self.wrongPassword()
diff --git a/module/plugins/accounts/FileomCom.py b/module/plugins/accounts/FileomCom.py
deleted file mode 100644
index 7c743f56a..000000000
--- a/module/plugins/accounts/FileomCom.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class FileomCom(XFSAccount):
- __name__ = "FileomCom"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Fileom.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "fileom.com"
diff --git a/module/plugins/accounts/FilerNet.py b/module/plugins/accounts/FilerNet.py
deleted file mode 100644
index cb913a27e..000000000
--- a/module/plugins/accounts/FilerNet.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-import time
-
-from module.plugins.Account import Account
-
-
-class FilerNet(Account):
- __name__ = "FilerNet"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Filer.net account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- TOKEN_PATTERN = r'_csrf_token" value="([^"]+)" />'
- WALID_UNTIL_PATTERN = r'Der Premium-Zugang ist gÃŒltig bis (.+)\.\s*</td>'
- TRAFFIC_PATTERN = r'Traffic</th>\s*<td>([^<]+)</td>'
- FREE_PATTERN = r'Account Status</th>\s*<td>\s*Free'
-
-
- def loadAccountInfo(self, user, req):
- html = req.load("https://filer.net/profile")
-
- # Free user
- if re.search(self.FREE_PATTERN, html):
- return {"premium": False, "validuntil": None, "trafficleft": None}
-
- until = re.search(self.WALID_UNTIL_PATTERN, html)
- traffic = re.search(self.TRAFFIC_PATTERN, html)
- if until and traffic:
- validuntil = int(time.mktime(time.strptime(until.group(1), "%d.%m.%Y %H:%M:%S")))
- trafficleft = self.parseTraffic(traffic.group(1))
- return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft}
- else:
- self.logError(_("Unable to retrieve account information"))
- return {"premium": False, "validuntil": None, "trafficleft": None}
-
-
- def login(self, user, data, req):
- html = req.load("https://filer.net/login")
- token = re.search(self.TOKEN_PATTERN, html).group(1)
- html = req.load("https://filer.net/login_check",
- post={"_username": user, "_password": data['password'],
- "_remember_me": "on", "_csrf_token": token, "_target_path": "https://filer.net/"})
- if 'Logout' not in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/FilerioCom.py b/module/plugins/accounts/FilerioCom.py
deleted file mode 100644
index 4c6755293..000000000
--- a/module/plugins/accounts/FilerioCom.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class FilerioCom(XFSAccount):
- __name__ = "FilerioCom"
- __type__ = "account"
- __version__ = "0.03"
-
- __description__ = """FileRio.in account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- HOSTER_DOMAIN = "filerio.in"
diff --git a/module/plugins/accounts/FilesMailRu.py b/module/plugins/accounts/FilesMailRu.py
deleted file mode 100644
index 365fa86dc..000000000
--- a/module/plugins/accounts/FilesMailRu.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-
-
-class FilesMailRu(Account):
- __name__ = "FilesMailRu"
- __type__ = "account"
- __version__ = "0.1"
-
- __description__ = """Filesmail.ru account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org")]
-
-
- def loadAccountInfo(self, user, req):
- return {"validuntil": None, "trafficleft": None}
-
-
- def login(self, user, data, req):
- user, domain = user.split("@")
-
- page = req.load("http://swa.mail.ru/cgi-bin/auth", None,
- {"Domain": domain, "Login": user, "Password": data['password'],
- "Page": "http://files.mail.ru/"}, cookies=True)
-
- if "НеверМПе ОЌя пПльзПвателя ОлО парПль" in page: # @TODO seems not to work
- self.wrongPassword()
diff --git a/module/plugins/accounts/FileserveCom.py b/module/plugins/accounts/FileserveCom.py
deleted file mode 100644
index 411ba85b1..000000000
--- a/module/plugins/accounts/FileserveCom.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from time import mktime, strptime
-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
-
-
-class FileserveCom(Account):
- __name__ = "FileserveCom"
- __type__ = "account"
- __version__ = "0.2"
-
- __description__ = """Fileserve.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("mkaay", "mkaay@mkaay.de")]
-
-
- def loadAccountInfo(self, user, req):
- data = self.getAccountData(user)
-
- page = req.load("http://app.fileserve.com/api/login/", post={"username": user, "password": data['password'],
- "submit": "Submit+Query"})
- res = json_loads(page)
-
- if res['type'] == "premium":
- validuntil = mktime(strptime(res['expireTime'], "%Y-%m-%d %H:%M:%S"))
- return {"trafficleft": res['traffic'], "validuntil": validuntil}
- else:
- return {"premium": False, "trafficleft": None, "validuntil": None}
-
-
- def login(self, user, data, req):
- page = req.load("http://app.fileserve.com/api/login/", post={"username": user, "password": data['password'],
- "submit": "Submit+Query"})
- res = json_loads(page)
-
- if not res['type']:
- self.wrongPassword()
-
- #login at fileserv page
- req.load("http://www.fileserve.com/login.php",
- post={"loginUserName": user, "loginUserPassword": data['password'], "autoLogin": "checked",
- "loginFormSubmit": "Login"})
diff --git a/module/plugins/accounts/FourSharedCom.py b/module/plugins/accounts/FourSharedCom.py
deleted file mode 100644
index ec19f83f5..000000000
--- a/module/plugins/accounts/FourSharedCom.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.common.json_layer import json_loads
-from module.plugins.Account import Account
-
-
-class FourSharedCom(Account):
- __name__ = "FourSharedCom"
- __type__ = "account"
- __version__ = "0.03"
-
- __description__ = """FourShared.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- def loadAccountInfo(self, user, req):
- # Free mode only for now
- return {"premium": False}
-
-
- def login(self, user, data, req):
- req.cj.setCookie("4shared.com", "4langcookie", "en")
- res = req.load('http://www.4shared.com/web/login',
- post={'login': user,
- 'password': data['password'],
- 'remember': "on",
- '_remember': "on",
- 'returnTo': "http://www.4shared.com/account/home.jsp"})
-
- if 'Please log in to access your 4shared account' in res:
- self.wrongPassword()
diff --git a/module/plugins/accounts/FreakshareCom.py b/module/plugins/accounts/FreakshareCom.py
deleted file mode 100644
index 27e1e3a0a..000000000
--- a/module/plugins/accounts/FreakshareCom.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import strptime, mktime
-
-from module.plugins.Account import Account
-
-
-class FreakshareCom(Account):
- __name__ = "FreakshareCom"
- __type__ = "account"
- __version__ = "0.11"
-
- __description__ = """Freakshare.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org")]
-
-
- def loadAccountInfo(self, user, req):
- page = req.load("http://freakshare.com/")
-
- validuntil = r'ltig bis:</td>\s*<td><b>([\d.:-]+)</b></td>'
- validuntil = re.search(validuntil, page, re.M)
- validuntil = validuntil.group(1).strip()
- validuntil = mktime(strptime(validuntil, "%d.%m.%Y - %H:%M"))
-
- traffic = r'Traffic verbleibend:</td>\s*<td>([^<]+)'
- traffic = re.search(traffic, page, re.M)
- traffic = traffic.group(1).strip()
- traffic = self.parseTraffic(traffic)
-
- return {"validuntil": validuntil, "trafficleft": traffic}
-
-
- def login(self, user, data, req):
- req.load("http://freakshare.com/index.php?language=EN")
-
- page = req.load("http://freakshare.com/login.html", None,
- {"submit": "Login", "user": user, "pass": data['password']}, cookies=True)
-
- if ">Wrong Username or Password" in page:
- self.wrongPassword()
diff --git a/module/plugins/accounts/FreeWayMe.py b/module/plugins/accounts/FreeWayMe.py
deleted file mode 100644
index cfba1ba06..000000000
--- a/module/plugins/accounts/FreeWayMe.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
-
-
-class FreeWayMe(Account):
- __name__ = "FreeWayMe"
- __type__ = "account"
- __version__ = "0.11"
-
- __description__ = """FreeWayMe account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Nicolas Giese", "james@free-way.me")]
-
-
- def loadAccountInfo(self, user, req):
- status = self.getAccountStatus(user, req)
- if not status:
- return False
- self.logDebug(status)
-
- account_info = {"validuntil": -1, "premium": False}
- if status['premium'] == "Free":
- account_info['trafficleft'] = int(status['guthaben']) * 1024
- elif status['premium'] == "Spender":
- account_info['trafficleft'] = -1
- elif status['premium'] == "Flatrate":
- account_info = {"validuntil": int(status['Flatrate']),
- "trafficleft": -1,
- "premium": True}
-
- return account_info
-
-
- def getpw(self, user):
- return self.accounts[user]['password']
-
-
- def login(self, user, data, req):
- status = self.getAccountStatus(user, req)
-
- # Check if user and password are valid
- if not status:
- self.wrongPassword()
-
-
- def getAccountStatus(self, user, req):
- answer = req.load("https://www.free-way.me/ajax/jd.php",
- get={"id": 4, "user": user, "pass": self.accounts[user]['password']})
- self.logDebug("Login: %s" % answer)
- if answer == "Invalid login":
- self.wrongPassword()
- return False
- return json_loads(answer)
diff --git a/module/plugins/accounts/FshareVn.py b/module/plugins/accounts/FshareVn.py
deleted file mode 100644
index d1b05209c..000000000
--- a/module/plugins/accounts/FshareVn.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from time import mktime, strptime
-from pycurl import REFERER
-import re
-
-from module.plugins.Account import Account
-
-
-class FshareVn(Account):
- __name__ = "FshareVn"
- __type__ = "account"
- __version__ = "0.07"
-
- __description__ = """Fshare.vn account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- VALID_UNTIL_PATTERN = ur'<dt>Thời hạn dùng:</dt>\s*<dd>([^<]+)</dd>'
- LIFETIME_PATTERN = ur'<dt>Lần đăng nhập trước:</dt>\s*<dd>[^<]+</dd>'
- TRAFFIC_LEFT_PATTERN = ur'<dt>Tổng Dung Lượng Tài Khoản</dt>\s*<dd[^>]*>([\d.]+) ([kKMG])B</dd>'
- DIRECT_DOWNLOAD_PATTERN = ur'<input type="checkbox"\s*([^=>]*)[^>]*/>Kích hoạt download trực tiếp</dt>'
-
-
- def loadAccountInfo(self, user, req):
- html = req.load("http://www.fshare.vn/account_info.php", decode=True)
-
- if re.search(self.LIFETIME_PATTERN, html):
- self.logDebug("Lifetime membership detected")
- trafficleft = self.getTrafficLeft()
- return {"validuntil": -1, "trafficleft": trafficleft, "premium": True}
-
- m = re.search(self.VALID_UNTIL_PATTERN, html)
- if m:
- premium = True
- validuntil = mktime(strptime(m.group(1), '%I:%M:%S %p %d-%m-%Y'))
- trafficleft = self.getTrafficLeft()
- else:
- premium = False
- validuntil = None
- trafficleft = None
-
- return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium}
-
-
- def login(self, user, data, req):
- req.http.c.setopt(REFERER, "https://www.fshare.vn/login.php")
-
- html = req.load('https://www.fshare.vn/login.php', post={
- "login_password": data['password'],
- "login_useremail": user,
- "url_refe": "http://www.fshare.vn/index.php"
- }, referer=True, decode=True)
-
- if not re.search(r'<img\s+alt="VIP"', html):
- self.wrongPassword()
-
-
- def getTrafficLeft(self):
- m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
- return float(m.group(1)) * 1024 ** {'k': 0, 'K': 0, 'M': 1, 'G': 2}[m.group(2)] if m else 0
diff --git a/module/plugins/accounts/Ftp.py b/module/plugins/accounts/Ftp.py
deleted file mode 100644
index f978d2fa0..000000000
--- a/module/plugins/accounts/Ftp.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-
-
-class Ftp(Account):
- __name__ = "Ftp"
- __type__ = "account"
- __version__ = "0.01"
-
- __description__ = """Ftp dummy account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- info_threshold = 1000000
- login_timeout = 1000000
diff --git a/module/plugins/accounts/HellshareCz.py b/module/plugins/accounts/HellshareCz.py
deleted file mode 100644
index 685740a5c..000000000
--- a/module/plugins/accounts/HellshareCz.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-import time
-
-from module.plugins.Account import Account
-
-
-class HellshareCz(Account):
- __name__ = "HellshareCz"
- __type__ = "account"
- __version__ = "0.14"
-
- __description__ = """Hellshare.cz account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- CREDIT_LEFT_PATTERN = r'<div class="credit-link">\s*<table>\s*<tr>\s*<th>(\d+|\d\d\.\d\d\.)</th>'
-
-
- def loadAccountInfo(self, user, req):
- self.relogin(user)
- html = req.load("http://www.hellshare.com/")
-
- m = re.search(self.CREDIT_LEFT_PATTERN, html)
- if m is None:
- trafficleft = None
- validuntil = None
- premium = False
- else:
- credit = m.group(1)
- premium = True
- try:
- if "." in credit:
- #Time-based account
- vt = [int(x) for x in credit.split('.')[:2]]
- lt = time.localtime()
- year = lt.tm_year + int(vt[1] < lt.tm_mon or (vt[1] == lt.tm_mon and vt[0] < lt.tm_mday))
- validuntil = time.mktime(time.strptime("%s%d 23:59:59" % (credit, year), "%d.%m.%Y %H:%M:%S"))
- trafficleft = -1
- else:
- #Traffic-based account
- trafficleft = int(credit) * 1024
- validuntil = -1
- except Exception, e:
- self.logError(_("Unable to parse credit info"), e)
- validuntil = -1
- trafficleft = -1
-
- return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium}
-
-
- def login(self, user, data, req):
- html = req.load('http://www.hellshare.com/')
- if req.lastEffectiveURL != 'http://www.hellshare.com/':
- #Switch to English
- self.logDebug("Switch lang - URL: %s" % req.lastEffectiveURL)
- json = req.load("%s?do=locRouter-show" % req.lastEffectiveURL)
- hash = re.search(r"(\-\-[0-9a-f]+\-)", json).group(1)
- self.logDebug("Switch lang - HASH: %s" % hash)
- html = req.load('http://www.hellshare.com/%s/' % hash)
-
- if re.search(self.CREDIT_LEFT_PATTERN, html):
- self.logDebug("Already logged in")
- return
-
- html = req.load('http://www.hellshare.com/login?do=loginForm-submit', post={
- "login": "Log in",
- "password": data['password'],
- "username": user,
- "perm_login": "on"
- })
-
- if "<p>You input a wrong user name or wrong password</p>" in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/Http.py b/module/plugins/accounts/Http.py
deleted file mode 100644
index 07e46eb07..000000000
--- a/module/plugins/accounts/Http.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-
-
-class Http(Account):
- __name__ = "Http"
- __type__ = "account"
- __version__ = "0.01"
-
- __description__ = """Http dummy account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- info_threshold = 1000000
- login_timeout = 1000000
diff --git a/module/plugins/accounts/HugefilesNet.py b/module/plugins/accounts/HugefilesNet.py
deleted file mode 100644
index 5da3bbc37..000000000
--- a/module/plugins/accounts/HugefilesNet.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class HugefilesNet(XFSAccount):
- __name__ = "HugefilesNet"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Hugefiles.net account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "hugefiles.net"
diff --git a/module/plugins/accounts/HundredEightyUploadCom.py b/module/plugins/accounts/HundredEightyUploadCom.py
deleted file mode 100644
index 39f91a8af..000000000
--- a/module/plugins/accounts/HundredEightyUploadCom.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class HundredEightyUploadCom(XFSAccount):
- __name__ = "HundredEightyUploadCom"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """180upload.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "180upload.com"
diff --git a/module/plugins/accounts/JunocloudMe.py b/module/plugins/accounts/JunocloudMe.py
deleted file mode 100644
index b0fc160f3..000000000
--- a/module/plugins/accounts/JunocloudMe.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class JunocloudMe(XFSAccount):
- __name__ = "JunocloudMe"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Junocloud.me account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("guidobelix", "guidobelix@hotmail.it")]
-
-
- HOSTER_DOMAIN = "junocloud.me"
diff --git a/module/plugins/accounts/Keep2shareCc.py b/module/plugins/accounts/Keep2shareCc.py
deleted file mode 100644
index ffae02ae4..000000000
--- a/module/plugins/accounts/Keep2shareCc.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import gmtime, mktime, strptime
-
-from module.plugins.Account import Account
-
-
-class Keep2shareCc(Account):
- __name__ = "Keep2shareCc"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Keep2share.cc account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("aeronaut", "aeronaut@pianoguy.de")]
-
-
- VALID_UNTIL_PATTERN = r'Premium expires: <b>(.+?)</b>'
- TRAFFIC_LEFT_PATTERN = r'Available traffic \(today\):<b><a href="/user/statistic.html">(.+?)</a>'
-
- LOGIN_FAIL_PATTERN = r'Please fix the following input errors'
-
-
- def loadAccountInfo(self, user, req):
- validuntil = None
- trafficleft = None
- premium = None
-
- html = req.load("http://keep2share.cc/site/profile.html", decode=True)
-
- m = re.search(self.VALID_UNTIL_PATTERN, html)
- if m:
- expiredate = m.group(1).strip()
- self.logDebug("Expire date: " + expiredate)
-
- try:
- validuntil = mktime(strptime(expiredate, "%Y.%m.%d"))
-
- except Exception, e:
- self.logError(e)
-
- else:
- if validuntil > mktime(gmtime()):
- premium = True
- else:
- premium = False
- validuntil = None
-
- m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
- if m:
- try:
- trafficleft = self.parseTraffic(m.group(1))
-
- except Exception, e:
- self.logError(e)
-
- return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium}
-
-
- def login(self, user, data, req):
- req.cj.setCookie("keep2share.cc", "lang", "en")
-
- html = req.load("http://keep2share.cc/login.html",
- post={'LoginForm[username]': user, 'LoginForm[password]': data['password']})
-
- if re.search(self.LOGIN_FAIL_PATTERN, html):
- self.wrongPassword()
diff --git a/module/plugins/accounts/LetitbitNet.py b/module/plugins/accounts/LetitbitNet.py
deleted file mode 100644
index b8244a06d..000000000
--- a/module/plugins/accounts/LetitbitNet.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-# from module.common.json_layer import json_loads, json_dumps
-
-
-class LetitbitNet(Account):
- __name__ = "LetitbitNet"
- __type__ = "account"
- __version__ = "0.01"
-
- __description__ = """Letitbit.net account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- def loadAccountInfo(self, user, req):
- ## DISABLED BECAUSE IT GET 'key exausted' EVEN IF VALID ##
- # api_key = self.accounts[user]['password']
- # json_data = [api_key, ['key/info']]
- # api_rep = req.load('http://api.letitbit.net/json', post={'r': json_dumps(json_data)})
- # self.logDebug("API Key Info: " + api_rep)
- # api_rep = json_loads(api_rep)
- #
- # if api_rep['status'] == 'FAIL':
- # self.logWarning(api_rep['data'])
- # return {'valid': False, 'premium': False}
-
- return {"premium": True}
-
-
- def login(self, user, data, req):
- # API_KEY is the username and the PREMIUM_KEY is the password
- self.logInfo(_("You must use your API KEY as username and the PREMIUM KEY as password"))
diff --git a/module/plugins/accounts/LinestorageCom.py b/module/plugins/accounts/LinestorageCom.py
deleted file mode 100644
index cf8dd3f3e..000000000
--- a/module/plugins/accounts/LinestorageCom.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class LinestorageCom(XFSAccount):
- __name__ = "LinestorageCom"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Linestorage.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "linestorage.com"
diff --git a/module/plugins/accounts/LinksnappyCom.py b/module/plugins/accounts/LinksnappyCom.py
deleted file mode 100644
index 9fcdac9c7..000000000
--- a/module/plugins/accounts/LinksnappyCom.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from hashlib import md5
-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
-
-
-class LinksnappyCom(Account):
- __name__ = "LinksnappyCom"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Linksnappy.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- def loadAccountInfo(self, user, req):
- data = self.getAccountData(user)
- r = req.load('http://gen.linksnappy.com/lseAPI.php',
- get={'act': 'USERDETAILS', 'username': user, 'password': md5(data['password']).hexdigest()})
- self.logDebug("JSON data: " + r)
- j = json_loads(r)
-
- if j['error']:
- return {"premium": False}
-
- validuntil = j['return']['expire']
- if validuntil == 'lifetime':
- validuntil = -1
- elif validuntil == 'expired':
- return {"premium": False}
- else:
- validuntil = float(validuntil)
-
- if 'trafficleft' not in j['return'] or isinstance(j['return']['trafficleft'], str):
- trafficleft = -1
- else:
- trafficleft = int(j['return']['trafficleft']) * 1024
-
- return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft}
-
-
- def login(self, user, data, req):
- r = req.load('http://gen.linksnappy.com/lseAPI.php',
- get={'act': 'USERDETAILS', 'username': user, 'password': md5(data['password']).hexdigest()})
-
- if 'Invalid Account Details' in r:
- self.wrongPassword()
diff --git a/module/plugins/accounts/LomafileCom.py b/module/plugins/accounts/LomafileCom.py
deleted file mode 100644
index cfd21679c..000000000
--- a/module/plugins/accounts/LomafileCom.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class LomafileCom(XFSAccount):
- __name__ = "LomafileCom"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Lomafile.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("guidobelix", "guidobelix@hotmail.it")]
-
-
- HOSTER_DOMAIN = "lomafile.com"
diff --git a/module/plugins/accounts/MegaDebridEu.py b/module/plugins/accounts/MegaDebridEu.py
deleted file mode 100644
index e7e22048b..000000000
--- a/module/plugins/accounts/MegaDebridEu.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
-
-
-class MegaDebridEu(Account):
- __name__ = "MegaDebridEu"
- __type__ = "account"
- __version__ = "0.2"
-
- __description__ = """mega-debrid.eu account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("D.Ducatel", "dducatel@je-geek.fr")]
-
-
- # Define the base URL of MegaDebrid api
- API_URL = "https://www.mega-debrid.eu/api.php"
-
-
- def loadAccountInfo(self, user, req):
- data = self.getAccountData(user)
- jsonResponse = req.load(self.API_URL,
- get={'action': 'connectUser', 'login': user, 'password': data['password']})
- res = json_loads(jsonResponse)
-
- if res['response_code'] == "ok":
- return {"premium": True, "validuntil": float(res['vip_end']), "status": True}
- else:
- self.logError(res)
- return {"status": False, "premium": False}
-
-
- def login(self, user, data, req):
- jsonResponse = req.load(self.API_URL,
- get={'action': 'connectUser', 'login': user, 'password': data['password']})
- res = json_loads(jsonResponse)
- if res['response_code'] != "ok":
- self.wrongPassword()
diff --git a/module/plugins/accounts/MegaRapidCz.py b/module/plugins/accounts/MegaRapidCz.py
deleted file mode 100644
index 41da7ac73..000000000
--- a/module/plugins/accounts/MegaRapidCz.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import mktime, strptime
-from module.plugins.Account import Account
-
-
-class MegaRapidCz(Account):
- __name__ = "MegaRapidCz"
- __type__ = "account"
- __version__ = "0.34"
-
- __description__ = """MegaRapid.cz account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("MikyWoW", "mikywow@seznam.cz"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- login_timeout = 60
-
- LIMITDL_PATTERN = ur'<td>Max. počet paralelních stahování: </td><td>(\d+)'
- VALID_UNTIL_PATTERN = ur'<td>Paušální stahování aktivní. Vyprší </td><td><strong>(.*?)</strong>'
- TRAFFIC_LEFT_PATTERN = r'<tr><td>Kredit</td><td>(.*?) GiB'
-
-
- def loadAccountInfo(self, user, req):
- html = req.load("http://megarapid.cz/mujucet/", decode=True)
-
- m = re.search(self.LIMITDL_PATTERN, html)
- if m:
- data = self.getAccountData(user)
- data['options']['limitDL'] = [int(m.group(1))]
-
- m = re.search(self.VALID_UNTIL_PATTERN, html)
- if m:
- validuntil = mktime(strptime(m.group(1), "%d.%m.%Y - %H:%M"))
- return {"premium": True, "trafficleft": -1, "validuntil": validuntil}
-
- m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
- if m:
- trafficleft = float(m.group(1)) * (1 << 20)
- return {"premium": True, "trafficleft": trafficleft, "validuntil": -1}
-
- return {"premium": False, "trafficleft": None, "validuntil": None}
-
-
- def login(self, user, data, req):
- htm = req.load("http://megarapid.cz/prihlaseni/")
- if "Heslo:" in htm:
- start = htm.index('id="inp_hash" name="hash" value="')
- htm = htm[start + 33:]
- hashes = htm[0:32]
- htm = req.load("http://megarapid.cz/prihlaseni/",
- post={"hash": hashes,
- "login": user,
- "pass1": data['password'],
- "remember": 0,
- "sbmt": u"Přihlásit"})
diff --git a/module/plugins/accounts/MegasharesCom.py b/module/plugins/accounts/MegasharesCom.py
deleted file mode 100644
index 6e0a4358e..000000000
--- a/module/plugins/accounts/MegasharesCom.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from time import mktime, strptime
-
-from module.plugins.Account import Account
-
-
-class MegasharesCom(Account):
- __name__ = "MegasharesCom"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Megashares.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- VALID_UNTIL_PATTERN = r'<p class="premium_info_box">Period Ends: (\w{3} \d{1,2}, \d{4})</p>'
-
-
- def loadAccountInfo(self, user, req):
- #self.relogin(user)
- html = req.load("http://d01.megashares.com/myms.php", decode=True)
-
- premium = False if '>Premium Upgrade<' in html else True
-
- validuntil = trafficleft = -1
- try:
- timestr = re.search(self.VALID_UNTIL_PATTERN, html).group(1)
- self.logDebug(timestr)
- validuntil = mktime(strptime(timestr, "%b %d, %Y"))
- except Exception, e:
- self.logError(e)
-
- return {"validuntil": validuntil, "trafficleft": -1, "premium": premium}
-
-
- def login(self, user, data, req):
- html = req.load('http://d01.megashares.com/myms_login.php', post={
- "httpref": "",
- "myms_login": "Login",
- "mymslogin_name": user,
- "mymspassword": data['password']
- }, decode=True)
-
- if not '<span class="b ml">%s</span>' % user in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/MovReelCom.py b/module/plugins/accounts/MovReelCom.py
deleted file mode 100644
index 6128cddc8..000000000
--- a/module/plugins/accounts/MovReelCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class MovReelCom(XFSAccount):
- __name__ = "MovReelCom"
- __type__ = "account"
- __version__ = "0.03"
-
- __description__ = """Movreel.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")]
-
-
- login_timeout = 60
- info_threshold = 30
-
- HOSTER_DOMAIN = "movreel.com"
diff --git a/module/plugins/accounts/MultishareCz.py b/module/plugins/accounts/MultishareCz.py
deleted file mode 100644
index 9eb0b50b2..000000000
--- a/module/plugins/accounts/MultishareCz.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Account import Account
-
-
-class MultishareCz(Account):
- __name__ = "MultishareCz"
- __type__ = "account"
- __version__ = "0.03"
-
- __description__ = """Multishare.cz account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- TRAFFIC_LEFT_PATTERN = r'<span class="profil-zvyrazneni">Kredit:</span>\s*<strong>(?P<S>[\d.,]+)&nbsp;(?P<U>[\w^_]+)</strong>'
- ACCOUNT_INFO_PATTERN = r'<input type="hidden" id="(u_ID|u_hash)" name="[^"]*" value="([^"]+)">'
-
-
- def loadAccountInfo(self, user, req):
- #self.relogin(user)
- html = req.load("http://www.multishare.cz/profil/", decode=True)
-
- m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
- trafficleft = self.parseTraffic(m.group('S'), m.group('U')) if m else 0
- self.premium = True if trafficleft else False
-
- html = req.load("http://www.multishare.cz/", decode=True)
- mms_info = dict(re.findall(self.ACCOUNT_INFO_PATTERN, html))
-
- return dict(mms_info, **{"validuntil": -1, "trafficleft": trafficleft})
-
-
- def login(self, user, data, req):
- html = req.load('http://www.multishare.cz/html/prihlaseni_process.php', post={
- "akce": "Přihlásit",
- "heslo": data['password'],
- "jmeno": user
- }, decode=True)
-
- if '<div class="akce-chyba akce">' in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/MyfastfileCom.py b/module/plugins/accounts/MyfastfileCom.py
deleted file mode 100644
index 65bb33854..000000000
--- a/module/plugins/accounts/MyfastfileCom.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from time import time
-
-from module.common.json_layer import json_loads
-from module.plugins.Account import Account
-
-
-class MyfastfileCom(Account):
- __name__ = "MyfastfileCom"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Myfastfile.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- def loadAccountInfo(self, user, req):
- if 'days_left' in self.json_data:
- validuntil = int(time() + self.json_data['days_left'] * 24 * 60 * 60)
- return {"premium": True, "validuntil": validuntil, "trafficleft": -1}
- else:
- self.logError(_("Unable to get account information"))
-
-
- def login(self, user, data, req):
- # Password to use is the API-Password written in http://myfastfile.com/myaccount
- html = req.load("http://myfastfile.com/api.php",
- get={"user": user, "pass": data['password']})
- self.logDebug("JSON data: " + html)
- self.json_data = json_loads(html)
- if self.json_data['status'] != 'ok':
- self.logError(_('Invalid login. The password to use is the API-Password you find in your "My Account" page'))
- self.wrongPassword()
diff --git a/module/plugins/accounts/NetloadIn.py b/module/plugins/accounts/NetloadIn.py
deleted file mode 100755
index 6565e6899..000000000
--- a/module/plugins/accounts/NetloadIn.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from time import time
-
-from module.plugins.Account import Account
-
-
-class NetloadIn(Account):
- __name__ = "NetloadIn"
- __type__ = "account"
- __version__ = "0.22"
-
- __description__ = """Netload.in account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("CryNickSystems", "webmaster@pcProfil.de")]
-
-
- def loadAccountInfo(self, user, req):
- page = req.load("http://netload.in/index.php", get={'id': 2, 'lang': "de"})
- left = r'>(\d+) (Tag|Tage), (\d+) Stunden<'
- left = re.search(left, page)
- if left:
- validuntil = time() + int(left.group(1)) * 24 * 60 * 60 + int(left.group(3)) * 60 * 60
- trafficleft = -1
- premium = True
- else:
- validuntil = None
- premium = False
- trafficleft = None
- return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium}
-
-
- def login(self, user, data, req):
- page = req.load("http://netload.in/index.php", None,
- {"txtuser": user, "txtpass": data['password'], "txtcheck": "login", "txtlogin": "Login"},
- cookies=True)
- if "password or it might be invalid!" in page:
- self.wrongPassword()
diff --git a/module/plugins/accounts/NosuploadCom.py b/module/plugins/accounts/NosuploadCom.py
deleted file mode 100644
index e523ee2f4..000000000
--- a/module/plugins/accounts/NosuploadCom.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class NosuploadCom(XFSAccount):
- __name__ = "NosuploadCom"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Nosupload.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "nosupload.com"
diff --git a/module/plugins/accounts/NovafileCom.py b/module/plugins/accounts/NovafileCom.py
deleted file mode 100644
index ab61bf0fc..000000000
--- a/module/plugins/accounts/NovafileCom.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class NovafileCom(XFSAccount):
- __name__ = "NovafileCom"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Novafile.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "novafile.com"
diff --git a/module/plugins/accounts/NowVideoAt.py b/module/plugins/accounts/NowVideoAt.py
deleted file mode 100644
index 234984b6b..000000000
--- a/module/plugins/accounts/NowVideoAt.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import gmtime, mktime, strptime
-
-from module.plugins.Account import Account
-
-
-class NowVideoAt(Account):
- __name__ = "NowVideoAt"
- __type__ = "account"
- __version__ = "0.01"
-
- __description__ = """NowVideo.at account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- VALID_UNTIL_PATTERN = r'>Your premium membership expires on: (.+?)<'
-
-
- def loadAccountInfo(self, user, req):
- validuntil = None
- trafficleft = -1
- premium = None
-
- html = req.load("http://www.nowvideo.at/premium.php")
-
- m = re.search(self.VALID_UNTIL_PATTERN, html)
- if m:
- expiredate = m.group(1).strip()
- self.logDebug("Expire date: " + expiredate)
-
- try:
- validuntil = mktime(strptime(expiredate, "%Y-%b-%d"))
-
- except Exception, e:
- self.logError(e)
-
- else:
- if validuntil > mktime(gmtime()):
- premium = True
- else:
- premium = False
- validuntil = -1
-
- return {"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium}
-
-
- def login(self, user, data, req):
- html = req.load("http://www.nowvideo.at/login.php",
- post={'user': user, 'pass': data['password']})
-
- if ">Invalid login details" is html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/OboomCom.py b/module/plugins/accounts/OboomCom.py
deleted file mode 100644
index 9bb45224c..000000000
--- a/module/plugins/accounts/OboomCom.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import time
-
-from beaker.crypto.pbkdf2 import PBKDF2
-
-from module.common.json_layer import json_loads
-from module.plugins.Account import Account
-
-
-class OboomCom(Account):
- __name__ = "OboomCom"
- __type__ = "account"
- __version__ = "0.21"
-
- __description__ = """Oboom.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stanley", "stanley.foerster@gmail.com")]
-
-
- def loadAccountData(self, user, req):
- passwd = self.getAccountData(user)['password']
- salt = passwd[::-1]
- pbkdf2 = PBKDF2(passwd, salt, 1000).hexread(16)
- result = json_loads(req.load("https://www.oboom.com/1/login", get={"auth": user, "pass": pbkdf2}))
- if not result[0] == 200:
- self.logWarning(_("Failed to log in: %s") % result[1])
- self.wrongPassword()
- return result[1]
-
-
- def loadAccountInfo(self, name, req):
- accountData = self.loadAccountData(name, req)
-
- userData = accountData['user']
-
- if userData['premium'] == "null":
- premium = False
- else:
- premium = True
-
- if userData['premium_unix'] == "null":
- validUntil = -1
- else:
- validUntil = int(userData['premium_unix'])
-
- traffic = userData['traffic']
-
- trafficLeft = traffic['current']
- maxTraffic = traffic['max']
-
- session = accountData['session']
-
- return {'premium' : premium,
- 'validuntil' : validUntil,
- 'trafficleft': trafficLeft / 1024, #@TODO: Remove / 1024 in 0.4.10
- 'maxtraffic' : maxTraffic / 1024, #@TODO: Remove / 1024 in 0.4.10
- 'session' : session}
-
-
- def login(self, user, data, req):
- self.loadAccountData(user, req)
diff --git a/module/plugins/accounts/OneFichierCom.py b/module/plugins/accounts/OneFichierCom.py
deleted file mode 100644
index 2f1c914c1..000000000
--- a/module/plugins/accounts/OneFichierCom.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import strptime, mktime
-
-from pycurl import REFERER
-
-from module.plugins.Account import Account
-
-
-class OneFichierCom(Account):
- __name__ = "OneFichierCom"
- __type__ = "account"
- __version__ = "0.11"
-
- __description__ = """1fichier.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Elrick69", "elrick69[AT]rocketmail[DOT]com"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- VALID_UNTIL_PATTERN = r'Your Premium Status will end the (\d+/\d+/\d+)'
-
-
- def loadAccountInfo(self, user, req):
- validuntil = None
- trafficleft = -1
- premium = None
-
- html = req.load("https://1fichier.com/console/abo.pl")
-
- m = re.search(self.VALID_UNTIL_PATTERN, html)
- if m:
- expiredate = m.group(1)
- self.logDebug("Expire date: " + expiredate)
-
- try:
- validuntil = mktime(strptime(expiredate, "%d/%m/%Y"))
- except Exception, e:
- self.logError(e)
- else:
- premium = True
-
- return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium or False}
-
-
- def login(self, user, data, req):
- req.http.c.setopt(REFERER, "https://1fichier.com/login.pl?lg=en")
-
- html = req.load("https://1fichier.com/login.pl?lg=en",
- post={'mail': user, 'pass': data['password'], 'It': "on", 'purge': "off", 'valider': "Send"})
-
- if '>Invalid email address' in html or '>Invalid password' in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/OverLoadMe.py b/module/plugins/accounts/OverLoadMe.py
deleted file mode 100644
index 7df4c9aef..000000000
--- a/module/plugins/accounts/OverLoadMe.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
-
-
-class OverLoadMe(Account):
- __name__ = "OverLoadMe"
- __type__ = "account"
- __version__ = "0.01"
-
- __description__ = """Over-Load.me account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("marley", "marley@over-load.me")]
-
-
- def loadAccountInfo(self, user, req):
- data = self.getAccountData(user)
- page = req.load("https://api.over-load.me/account.php", get={"user": user, "auth": data['password']}).strip()
- data = json_loads(page)
-
- # Check for premium
- if data['membership'] == "Free":
- return {"premium": False}
-
- account_info = {"validuntil": data['expirationunix'], "trafficleft": -1}
- return account_info
-
-
- def login(self, user, data, req):
- jsondata = req.load("https://api.over-load.me/account.php",
- get={"user": user, "auth": data['password']}).strip()
- data = json_loads(jsondata)
-
- if data['err'] == 1:
- self.wrongPassword()
diff --git a/module/plugins/accounts/PremiumTo.py b/module/plugins/accounts/PremiumTo.py
deleted file mode 100644
index c1a8c661a..000000000
--- a/module/plugins/accounts/PremiumTo.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-
-
-class PremiumTo(Account):
- __name__ = "PremiumTo"
- __type__ = "account"
- __version__ = "0.04"
-
- __description__ = """Premium.to account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- def loadAccountInfo(self, user, req):
- api_r = req.load("http://premium.to/api/straffic.php",
- get={'username': self.username, 'password': self.password})
- traffic = sum(map(int, api_r.split(';')))
-
- return {"trafficleft": int(traffic) / 1024, "validuntil": -1} #@TODO: Remove / 1024 in 0.4.10
-
-
- def login(self, user, data, req):
- self.username = user
- self.password = data['password']
- authcode = req.load("http://premium.to/api/getauthcode.php",
- get={'username': user, 'password': self.password}).strip()
-
- if "wrong username" in authcode:
- self.wrongPassword()
diff --git a/module/plugins/accounts/PremiumizeMe.py b/module/plugins/accounts/PremiumizeMe.py
deleted file mode 100644
index 6603165e0..000000000
--- a/module/plugins/accounts/PremiumizeMe.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-
-from module.common.json_layer import json_loads
-
-
-class PremiumizeMe(Account):
- __name__ = "PremiumizeMe"
- __type__ = "account"
- __version__ = "0.11"
-
- __description__ = """Premiumize.me account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Florian Franzen", "FlorianFranzen@gmail.com")]
-
-
- def loadAccountInfo(self, user, req):
- # Get user data from premiumize.me
- status = self.getAccountStatus(user, req)
- self.logDebug(status)
-
- # Parse account info
- account_info = {"validuntil": float(status['result']['expires']),
- "trafficleft": max(0, status['result']['trafficleft_bytes'])}
-
- if status['result']['type'] == 'free':
- account_info['premium'] = False
-
- return account_info
-
-
- def login(self, user, data, req):
- # Get user data from premiumize.me
- status = self.getAccountStatus(user, req)
-
- # Check if user and password are valid
- if status['status'] != 200:
- self.wrongPassword()
-
-
- def getAccountStatus(self, user, req):
- # Use premiumize.me API v1 (see https://secure.premiumize.me/?show=api)
- # to retrieve account info and return the parsed json answer
- answer = req.load("https://api.premiumize.me/pm-api/v1.php",
- get={'method' : "accountstatus",
- 'params[login]': user,
- 'params[pass]' : self.accounts[user]['password']})
- return json_loads(answer)
diff --git a/module/plugins/accounts/QuickshareCz.py b/module/plugins/accounts/QuickshareCz.py
deleted file mode 100644
index 18af5f736..000000000
--- a/module/plugins/accounts/QuickshareCz.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Account import Account
-
-
-class QuickshareCz(Account):
- __name__ = "QuickshareCz"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Quickshare.cz account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- TRAFFIC_LEFT_PATTERN = r'Stav kreditu: <strong>(.+?)</strong>'
-
-
- def loadAccountInfo(self, user, req):
- html = req.load("http://www.quickshare.cz/premium", decode=True)
-
- m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
- if m:
- trafficleft = self.parseTraffic(m.group(1))
- premium = True if trafficleft else False
- else:
- trafficleft = None
- premium = False
-
- return {"validuntil": -1, "trafficleft": trafficleft, "premium": premium}
-
-
- def login(self, user, data, req):
- html = req.load('http://www.quickshare.cz/html/prihlaseni_process.php', post={
- "akce": u'Přihlásit',
- "heslo": data['password'],
- "jmeno": user
- }, decode=True)
-
- if u'>TakovÜ uşivatel neexistuje.<' in html or u'>Špatné heslo.<' in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/RPNetBiz.py b/module/plugins/accounts/RPNetBiz.py
deleted file mode 100644
index 4daa56a75..000000000
--- a/module/plugins/accounts/RPNetBiz.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
-
-
-class RPNetBiz(Account):
- __name__ = "RPNetBiz"
- __type__ = "account"
- __version__ = "0.1"
-
- __description__ = """RPNet.biz account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Dman", "dmanugm@gmail.com")]
-
-
- def loadAccountInfo(self, user, req):
- # Get account information from rpnet.biz
- res = self.getAccountStatus(user, req)
- try:
- if res['accountInfo']['isPremium']:
- # Parse account info. Change the trafficleft later to support per host info.
- account_info = {"validuntil": int(res['accountInfo']['premiumExpiry']),
- "trafficleft": -1, "premium": True}
- else:
- account_info = {"validuntil": None, "trafficleft": None, "premium": False}
-
- except KeyError:
- #handle wrong password exception
- account_info = {"validuntil": None, "trafficleft": None, "premium": False}
-
- return account_info
-
-
- def login(self, user, data, req):
- # Get account information from rpnet.biz
- res = self.getAccountStatus(user, req)
-
- # If we have an error in the res, we have wrong login information
- if 'error' in res:
- self.wrongPassword()
-
-
- def getAccountStatus(self, user, req):
- # Using the rpnet API, check if valid premium account
- res = req.load("https://premium.rpnet.biz/client_api.php",
- get={"username": user, "password": self.accounts[user]['password'],
- "action": "showAccountInformation"})
- self.logDebug("JSON data: %s" % res)
-
- return json_loads(res)
diff --git a/module/plugins/accounts/RapidfileshareNet.py b/module/plugins/accounts/RapidfileshareNet.py
deleted file mode 100644
index c0dd7eaee..000000000
--- a/module/plugins/accounts/RapidfileshareNet.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class RapidfileshareNet(XFSAccount):
- __name__ = "RapidfileshareNet"
- __type__ = "account"
- __version__ = "0.05"
-
- __description__ = """Rapidfileshare.net account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("guidobelix", "guidobelix@hotmail.it")]
-
-
- HOSTER_DOMAIN = "rapidfileshare.net"
-
- TRAFFIC_LEFT_PATTERN = r'>Traffic available today:</TD><TD><label for="name">\s*(?P<S>[\d.,]+)\s*(?:(?P<U>[\w^_]+))?'
diff --git a/module/plugins/accounts/RapidgatorNet.py b/module/plugins/accounts/RapidgatorNet.py
deleted file mode 100644
index 9a0edbfa4..000000000
--- a/module/plugins/accounts/RapidgatorNet.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
-
-
-class RapidgatorNet(Account):
- __name__ = "RapidgatorNet"
- __type__ = "account"
- __version__ = "0.04"
-
- __description__ = """Rapidgator.net account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- API_URL = 'http://rapidgator.net/api/user'
-
-
- def loadAccountInfo(self, user, req):
- try:
- sid = self.getAccountData(user).get('SID')
- assert sid
-
- json = req.load("%s/info?sid=%s" % (self.API_URL, sid))
- self.logDebug("API:USERINFO", json)
- json = json_loads(json)
-
- if json['response_status'] == 200:
- if "reset_in" in json['response']:
- self.scheduleRefresh(user, json['response']['reset_in'])
-
- return {"validuntil": json['response']['expire_date'],
- "trafficleft": int(json['response']['traffic_left']),
- "premium": True}
- else:
- self.logError(json['response_details'])
- except Exception, e:
- self.logError(e)
-
- return {"validuntil": None, "trafficleft": None, "premium": False}
-
-
- def login(self, user, data, req):
- try:
- json = req.load('%s/login' % self.API_URL, post={"username": user, "password": data['password']})
- self.logDebug("API:LOGIN", json)
- json = json_loads(json)
-
- if json['response_status'] == 200:
- data['SID'] = str(json['response']['session_id'])
- return
- else:
- self.logError(json['response_details'])
- except Exception, e:
- self.logError(e)
-
- self.wrongPassword()
diff --git a/module/plugins/accounts/RarefileNet.py b/module/plugins/accounts/RarefileNet.py
deleted file mode 100644
index 577a6c8f6..000000000
--- a/module/plugins/accounts/RarefileNet.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class RarefileNet(XFSAccount):
- __name__ = "RarefileNet"
- __type__ = "account"
- __version__ = "0.04"
-
- __description__ = """RareFile.net account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- HOSTER_DOMAIN = "rarefile.net"
diff --git a/module/plugins/accounts/RealdebridCom.py b/module/plugins/accounts/RealdebridCom.py
deleted file mode 100644
index eeba34665..000000000
--- a/module/plugins/accounts/RealdebridCom.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import xml.dom.minidom as dom
-
-from module.plugins.Account import Account
-
-
-class RealdebridCom(Account):
- __name__ = "RealdebridCom"
- __type__ = "account"
- __version__ = "0.43"
-
- __description__ = """Real-Debrid.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Devirex Hazzard", "naibaf_11@yahoo.de")]
-
-
- def loadAccountInfo(self, user, req):
- if self.pin_code:
- return {"premium": False}
- page = req.load("https://real-debrid.com/api/account.php")
- xml = dom.parseString(page)
- account_info = {"validuntil": int(xml.getElementsByTagName("expiration")[0].childNodes[0].nodeValue),
- "trafficleft": -1}
-
- return account_info
-
-
- def login(self, user, data, req):
- self.pin_code = False
- page = req.load("https://real-debrid.com/ajax/login.php", get={"user": user, "pass": data['password']})
- if "Your login informations are incorrect" in page:
- self.wrongPassword()
- elif "PIN Code required" in page:
- self.logWarning(_("PIN code required. Please login to https://real-debrid.com using the PIN or disable the double authentication in your control panel on https://real-debrid.com"))
- self.pin_code = True
diff --git a/module/plugins/accounts/RehostTo.py b/module/plugins/accounts/RehostTo.py
deleted file mode 100644
index 16702e632..000000000
--- a/module/plugins/accounts/RehostTo.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-
-
-class RehostTo(Account):
- __name__ = "RehostTo"
- __type__ = "account"
- __version__ = "0.1"
-
- __description__ = """Rehost.to account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org")]
-
-
- def loadAccountInfo(self, user, req):
- data = self.getAccountData(user)
- page = req.load("http://rehost.to/api.php",
- get={'cmd': "login", 'user': user, 'pass': data['password']})
- data = [x.split("=") for x in page.split(",")]
- ses = data[0][1]
- long_ses = data[1][1]
-
- page = req.load("http://rehost.to/api.php",
- get={'cmd': "get_premium_credits", 'long_ses': long_ses})
- traffic, valid = page.split(",")
-
- account_info = {"trafficleft": int(traffic) * 1024,
- "validuntil": int(valid),
- "long_ses": long_ses,
- "ses": ses}
-
- return account_info
-
-
- def login(self, user, data, req):
- page = req.load("http://rehost.to/api.php",
- get={'cmd': "login", 'user': user, 'pass': data['password']})
-
- if "Login failed." in page:
- self.wrongPassword()
diff --git a/module/plugins/accounts/RyushareCom.py b/module/plugins/accounts/RyushareCom.py
deleted file mode 100644
index ca476366b..000000000
--- a/module/plugins/accounts/RyushareCom.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class RyushareCom(XFSAccount):
- __name__ = "RyushareCom"
- __type__ = "account"
- __version__ = "0.05"
-
- __description__ = """Ryushare.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("trance4us", None)]
-
-
- HOSTER_DOMAIN = "ryushare.com"
-
-
- def login(self, user, data, req):
- req.lastURL = "http://ryushare.com/login.python"
- html = req.load("http://ryushare.com/login.python",
- post={"login": user, "password": data['password'], "op": "login"})
- if 'Incorrect Login or Password' in html or '>Error<' in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/SecureUploadEu.py b/module/plugins/accounts/SecureUploadEu.py
deleted file mode 100644
index b335c94da..000000000
--- a/module/plugins/accounts/SecureUploadEu.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class SecureUploadEu(XFSAccount):
- __name__ = "SecureUploadEu"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """SecureUpload.eu account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "secureupload.eu"
diff --git a/module/plugins/accounts/SendmywayCom.py b/module/plugins/accounts/SendmywayCom.py
deleted file mode 100644
index 4fcbe0b7a..000000000
--- a/module/plugins/accounts/SendmywayCom.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class SendmywayCom(XFSAccount):
- __name__ = "SendmywayCom"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Sendmyway.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "sendmyway.com"
diff --git a/module/plugins/accounts/ShareonlineBiz.py b/module/plugins/accounts/ShareonlineBiz.py
deleted file mode 100644
index 976d2ff14..000000000
--- a/module/plugins/accounts/ShareonlineBiz.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-
-
-class ShareonlineBiz(Account):
- __name__ = "ShareonlineBiz"
- __type__ = "account"
- __version__ = "0.24"
-
- __description__ = """Share-online.biz account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("mkaay", "mkaay@mkaay.de"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- def getUserAPI(self, user, req):
- return req.load("http://api.share-online.biz/account.php",
- {"username": user, "password": self.accounts[user]['password'], "act": "userDetails"})
-
-
- def loadAccountInfo(self, user, req):
- html = self.getUserAPI(user, req)
-
- info = {}
- for line in html.splitlines():
- if "=" in line:
- key, value = line.split("=")
- info[key] = value
- self.logDebug(info)
-
- if "dl" in info and info['dl'].lower() != "not_available":
- req.cj.setCookie("share-online.biz", "dl", info['dl'])
- if "a" in info and info['a'].lower() != "not_available":
- req.cj.setCookie("share-online.biz", "a", info['a'])
-
- return {"validuntil": int(info['expire_date']) if "expire_date" in info else -1,
- "trafficleft": -1,
- "premium": True if ("dl" in info or "a" in info) and (info['group'] != "Sammler") else False}
-
-
- def login(self, user, data, req):
- html = self.getUserAPI(user, req)
- if "EXCEPTION" in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/SimplyPremiumCom.py b/module/plugins/accounts/SimplyPremiumCom.py
deleted file mode 100644
index 979ce73aa..000000000
--- a/module/plugins/accounts/SimplyPremiumCom.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.common.json_layer import json_loads
-from module.plugins.Account import Account
-
-
-class SimplyPremiumCom(Account):
- __name__ = "SimplyPremiumCom"
- __type__ = "account"
- __version__ = "0.01"
-
- __description__ = """Simply-Premium.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("EvolutionClip", "evolutionclip@live.de")]
-
-
- def loadAccountInfo(self, user, req):
- json_data = req.load('http://www.simply-premium.com/api/user.php?format=json')
- self.logDebug("JSON data: " + json_data)
- json_data = json_loads(json_data)
-
- if 'vip' in json_data['result'] and json_data['result']['vip'] == 0:
- return {"premium": False}
-
- #Time package
- validuntil = float(json_data['result']['timeend'])
- #Traffic package
- # {"trafficleft": int(traffic), "validuntil": -1}
- #trafficleft = int(json_data['result']['traffic'])
-
- #return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft}
- return {"premium": True, "validuntil": validuntil}
-
-
- def login(self, user, data, req):
- req.cj.setCookie("simply-premium.com", "lang", "EN")
-
- if data['password'] == '' or data['password'] == '0':
- post_data = {"key": user}
- else:
- post_data = {"login_name": user, "login_pass": data['password']}
-
- html = req.load("http://www.simply-premium.com/login.php", post=post_data)
-
- if 'logout' not in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/SimplydebridCom.py b/module/plugins/accounts/SimplydebridCom.py
deleted file mode 100644
index 7979fd5d5..000000000
--- a/module/plugins/accounts/SimplydebridCom.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from time import mktime, strptime
-
-from module.plugins.Account import Account
-
-
-class SimplydebridCom(Account):
- __name__ = "SimplydebridCom"
- __type__ = "account"
- __version__ = "0.1"
-
- __description__ = """Simply-Debrid.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Kagenoshin", "kagenoshin@gmx.ch")]
-
-
- def loadAccountInfo(self, user, req):
- get_data = {'login': 2, 'u': self.loginname, 'p': self.password}
- res = req.load("http://simply-debrid.com/api.php", get=get_data, decode=True)
- data = [x.strip() for x in res.split(";")]
- if str(data[0]) != "1":
- return {"premium": False}
- else:
- return {"trafficleft": -1, "validuntil": mktime(strptime(str(data[2]), "%d/%m/%Y"))}
-
-
- def login(self, user, data, req):
- self.loginname = user
- self.password = data['password']
- get_data = {'login': 1, 'u': self.loginname, 'p': self.password}
- res = req.load("http://simply-debrid.com/api.php", get=get_data, decode=True)
- if res != "02: loggin success":
- self.wrongPassword()
diff --git a/module/plugins/accounts/StahnuTo.py b/module/plugins/accounts/StahnuTo.py
deleted file mode 100644
index 44884f835..000000000
--- a/module/plugins/accounts/StahnuTo.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Account import Account
-
-
-class StahnuTo(Account):
- __name__ = "StahnuTo"
- __type__ = "account"
- __version__ = "0.03"
-
- __description__ = """StahnuTo account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- def loadAccountInfo(self, user, req):
- html = req.load("http://www.stahnu.to/")
-
- m = re.search(r'>VIP: (\d+.*)<', html)
- trafficleft = self.parseTraffic(m.group(1)) * 1024 if m else 0
-
- return {"premium": trafficleft > (512 * 1024), "trafficleft": trafficleft, "validuntil": -1}
-
-
- def login(self, user, data, req):
- html = req.load("http://www.stahnu.to/login.php", post={
- "username": user,
- "password": data['password'],
- "submit": "Login"})
-
- if not '<a href="logout.php">' in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/StreamcloudEu.py b/module/plugins/accounts/StreamcloudEu.py
deleted file mode 100644
index aa1eafcbd..000000000
--- a/module/plugins/accounts/StreamcloudEu.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class StreamcloudEu(XFSAccount):
- __name__ = "StreamcloudEu"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Streamcloud.eu account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "streamcloud.eu"
diff --git a/module/plugins/accounts/TurbobitNet.py b/module/plugins/accounts/TurbobitNet.py
deleted file mode 100644
index f87d234a7..000000000
--- a/module/plugins/accounts/TurbobitNet.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from time import mktime, strptime
-
-from module.plugins.Account import Account
-
-
-class TurbobitNet(Account):
- __name__ = "TurbobitNet"
- __type__ = "account"
- __version__ = "0.01"
-
- __description__ = """TurbobitNet account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- def loadAccountInfo(self, user, req):
- html = req.load("http://turbobit.net")
-
- m = re.search(r'<u>Turbo Access</u> to ([\d.]+)', html)
- if m:
- premium = True
- validuntil = mktime(strptime(m.group(1), "%d.%m.%Y"))
- else:
- premium = False
- validuntil = -1
-
- return {"premium": premium, "trafficleft": -1, "validuntil": validuntil}
-
-
- def login(self, user, data, req):
- req.cj.setCookie("turbobit.net", "user_lang", "en")
-
- html = req.load("http://turbobit.net/user/login", post={
- "user[login]": user,
- "user[pass]": data['password'],
- "user[submit]": "Login"})
-
- if not '<div class="menu-item user-name">' in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/TusfilesNet.py b/module/plugins/accounts/TusfilesNet.py
deleted file mode 100644
index 279dfd00a..000000000
--- a/module/plugins/accounts/TusfilesNet.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import mktime, strptime, gmtime
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class TusfilesNet(XFSAccount):
- __name__ = "TusfilesNet"
- __type__ = "account"
- __version__ = "0.06"
-
- __description__ = """ Tusfile.net account plugin """
- __license__ = "GPLv3"
- __authors__ = [("guidobelix", "guidobelix@hotmail.it")]
-
-
- HOSTER_DOMAIN = "tusfiles.net"
-
- VALID_UNTIL_PATTERN = r'<span class="label label-default">([^<]+)</span>'
- TRAFFIC_LEFT_PATTERN = r'<td><img src="//www\.tusfiles\.net/i/icon/meter\.png" alt=""/></td>\n<td>&nbsp;(?P<S>[\d.,]+)'
diff --git a/module/plugins/accounts/UlozTo.py b/module/plugins/accounts/UlozTo.py
deleted file mode 100644
index 63adb6aa8..000000000
--- a/module/plugins/accounts/UlozTo.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urljoin
-
-from module.plugins.Account import Account
-
-
-class UlozTo(Account):
- __name__ = "UlozTo"
- __type__ = "account"
- __version__ = "0.07"
-
- __description__ = """Uloz.to account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("pulpe", None)]
-
-
- TRAFFIC_LEFT_PATTERN = r'<li class="menu-kredit"><a href="/kredit" title="[^"]*?GB = ([\d.]+) MB"'
-
-
- def loadAccountInfo(self, user, req):
- self.phpsessid = req.cj.getCookie("ULOSESSID") #@NOTE: this cookie gets lost somehow after each request
-
- html = req.load("http://www.ulozto.net/", decode=True)
-
- req.cj.setCookie("ulozto.net", "ULOSESSID", self.phpsessid)
-
- m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
- trafficleft = int(float(m.group(1).replace(' ', '').replace(',', '.')) * 1000 * 1.048) if m else 0
- self.premium = True if trafficleft else False
-
- return {"validuntil": -1, "trafficleft": trafficleft}
-
-
- def login(self, user, data, req):
- login_page = req.load('http://www.ulozto.net/?do=web-login', decode=True)
- action = re.findall('<form action="(.+?)"', login_page)[1].replace('&amp;', '&')
- token = re.search('_token_" value="(.+?)"', login_page).group(1)
-
- html = req.load(urljoin("http://www.ulozto.net/", action),
- post={'_token_' : token,
- 'do' : "loginForm-submit",
- 'login' : u"Přihlásit",
- 'password': data['password'],
- 'username': user},
- decode=True)
-
- if '<div class="flash error">' in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/UnrestrictLi.py b/module/plugins/accounts/UnrestrictLi.py
deleted file mode 100644
index e43d6bbd7..000000000
--- a/module/plugins/accounts/UnrestrictLi.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Account import Account
-from module.common.json_layer import json_loads
-
-
-class UnrestrictLi(Account):
- __name__ = "UnrestrictLi"
- __type__ = "account"
- __version__ = "0.03"
-
- __description__ = """Unrestrict.li account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- def loadAccountInfo(self, user, req):
- json_data = req.load('http://unrestrict.li/api/jdownloader/user.php?format=json')
- self.logDebug("JSON data: " + json_data)
- json_data = json_loads(json_data)
-
- if 'vip' in json_data['result'] and json_data['result']['vip'] == 0:
- return {"premium": False}
-
- validuntil = json_data['result']['expires']
- trafficleft = int(json_data['result']['traffic'])
-
- return {"premium": True, "validuntil": validuntil, "trafficleft": trafficleft}
-
-
- def login(self, user, data, req):
- req.cj.setCookie("unrestrict.li", "lang", "EN")
- html = req.load("https://unrestrict.li/sign_in")
-
- if 'solvemedia' in html:
- self.logError(_("A Captcha is required. Go to http://unrestrict.li/sign_in and login, then retry"))
- return
-
- post_data = {"username": user, "password": data['password'],
- "remember_me": "remember", "signin": "Sign in"}
- html = req.load("https://unrestrict.li/sign_in", post=post_data)
-
- if 'sign_out' not in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/UploadcCom.py b/module/plugins/accounts/UploadcCom.py
deleted file mode 100644
index d1e1a2ead..000000000
--- a/module/plugins/accounts/UploadcCom.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class UploadcCom(XFSAccount):
- __name__ = "UploadcCom"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """Uploadc.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "uploadc.com"
diff --git a/module/plugins/accounts/UploadedTo.py b/module/plugins/accounts/UploadedTo.py
deleted file mode 100644
index 6cb09e44b..000000000
--- a/module/plugins/accounts/UploadedTo.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from time import time
-
-from module.plugins.Account import Account
-
-
-class UploadedTo(Account):
- __name__ = "UploadedTo"
- __type__ = "account"
- __version__ = "0.27"
-
- __description__ = """Uploaded.to account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("mkaay", "mkaay@mkaay.de")]
-
-
- PREMIUM_PATTERN = r'<em>Premium</em>'
- VALID_UNTIL_PATTERN = r'<td>Duration:</td>\s*<th>([^<]+)'
- TRAFFIC_LEFT_PATTERN = r'<th colspan="2"><b class="cB">([^<]+)'
-
-
- def loadAccountInfo(self, user, req):
- validuntil = None
- trafficleft = None
- premium = None
-
- html = req.load("http://uploaded.net/me")
-
- premium = True if re.search(self.PREMIUM_PATTERN, html) else False
-
- m = re.search(self.VALID_UNTIL_PATTERN, html, re.M)
- if m:
- expiredate = m.group(1).strip()
-
- if expiredate == "unlimited":
- validuntil = -1
- else:
- m = re.findall(r'(\d+) (Week|weeks|day|hour)', expiredate)
- if m:
- validuntil = time()
- for n, u in m:
- validuntil += int(n) * 60 * 60 * {'Week': 168, 'weeks': 168, 'day': 24, 'hour': 1}[u]
-
- m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
- if m:
- trafficleft = self.parseTraffic(m.group(1).replace('.', ''))
-
- return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium}
-
-
- def login(self, user, data, req):
- req.cj.setCookie("uploaded.net", "lang", "en")
-
- page = req.load("http://uploaded.net/io/login",
- post={'id': user, 'pw': data['password'], '_': ""})
-
- if "User and password do not match" in page:
- self.wrongPassword()
diff --git a/module/plugins/accounts/UploadheroCom.py b/module/plugins/accounts/UploadheroCom.py
deleted file mode 100644
index be1dfe981..000000000
--- a/module/plugins/accounts/UploadheroCom.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-import datetime
-import time
-
-from module.plugins.Account import Account
-
-
-class UploadheroCom(Account):
- __name__ = "UploadheroCom"
- __type__ = "account"
- __version__ = "0.2"
-
- __description__ = """Uploadhero.co account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("mcmyst", "mcmyst@hotmail.fr")]
-
-
- def loadAccountInfo(self, user, req):
- premium_pattern = re.compile('Il vous reste <span class="bleu">(\d+)</span> jours premium')
-
- data = self.getAccountData(user)
- page = req.load("http://uploadhero.co/my-account")
-
- if premium_pattern.search(page):
- end_date = datetime.date.today() + datetime.timedelta(days=int(premium_pattern.search(page).group(1)))
- end_date = time.mktime(future.timetuple())
- account_info = {"validuntil": end_date, "trafficleft": -1, "premium": True}
- else:
- account_info = {"validuntil": -1, "trafficleft": -1, "premium": False}
-
- return account_info
-
-
- def login(self, user, data, req):
- page = req.load("http://uploadhero.co/lib/connexion.php",
- post={"pseudo_login": user, "password_login": data['password']})
-
- if "mot de passe invalide" in page:
- self.wrongPassword()
diff --git a/module/plugins/accounts/UploadingCom.py b/module/plugins/accounts/UploadingCom.py
deleted file mode 100644
index c70d2ec11..000000000
--- a/module/plugins/accounts/UploadingCom.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import time, strptime, mktime
-
-from module.plugins.Account import Account
-from module.plugins.internal.SimpleHoster import set_cookies
-
-
-class UploadingCom(Account):
- __name__ = "UploadingCom"
- __type__ = "account"
- __version__ = "0.11"
-
- __description__ = """Uploading.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("mkaay", "mkaay@mkaay.de")]
-
-
- PREMIUM_PATTERN = r'UPGRADE TO PREMIUM'
- VALID_UNTIL_PATTERN = r'Valid Until:(.+?)<'
-
-
- def loadAccountInfo(self, user, req):
- validuntil = None
- trafficleft = None
- premium = None
-
- html = req.load("http://uploading.com/")
-
- premium = False if re.search(self.PREMIUM_PATTERN, html) else True
-
- m = re.search(self.VALID_UNTIL_PATTERN, html)
- if m:
- expiredate = m.group(1).strip()
- self.logDebug("Expire date: " + expiredate)
-
- try:
- validuntil = mktime(strptime(expiredate, "%b %d, %Y"))
-
- except Exception, e:
- self.logError(e)
-
- else:
- if validuntil > mktime(gmtime()):
- premium = True
- else:
- premium = False
- validuntil = None
-
- return {'validuntil': validuntil, 'trafficleft': trafficleft, 'premium': premium}
-
-
- def login(self, user, data, req):
- set_cookies([("uploading.com", "lang", "1"),
- ("uploading.com", "language", "1"),
- ("uploading.com", "setlang", "en"),
- ("uploading.com", "_lang", "en")]
-
- req.load("http://uploading.com/")
- req.load("http://uploading.com/general/login_form/?JsHttpRequest=%s-xml" % long(time() * 1000),
- post={'email': user, 'password': data['password'], 'remember': "on"})
diff --git a/module/plugins/accounts/UptoboxCom.py b/module/plugins/accounts/UptoboxCom.py
deleted file mode 100644
index 299a0acc2..000000000
--- a/module/plugins/accounts/UptoboxCom.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class UptoboxCom(XFSAccount):
- __name__ = "UptoboxCom"
- __type__ = "account"
- __version__ = "0.07"
-
- __description__ = """DDLStorage.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- HOSTER_DOMAIN = "uptobox.com"
- HOSTER_URL = "https://uptobox.com/"
diff --git a/module/plugins/accounts/VidPlayNet.py b/module/plugins/accounts/VidPlayNet.py
deleted file mode 100644
index 5bfc24963..000000000
--- a/module/plugins/accounts/VidPlayNet.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class VidPlayNet(XFSAccount):
- __name__ = "VidPlayNet"
- __type__ = "account"
- __version__ = "0.02"
-
- __description__ = """VidPlay.net account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "vidplay.net"
diff --git a/module/plugins/accounts/XFileSharingPro.py b/module/plugins/accounts/XFileSharingPro.py
deleted file mode 100644
index fb77ab23f..000000000
--- a/module/plugins/accounts/XFileSharingPro.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSAccount import XFSAccount
-
-
-class XFileSharingPro(XFSAccount):
- __name__ = "XFileSharingPro"
- __type__ = "account"
- __version__ = "0.05"
-
- __description__ = """XFileSharingPro multi-purpose account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = None
-
-
- def init(self):
- if self.HOSTER_DOMAIN:
- return super(XFileSharingPro, self).init()
-
-
- def loadAccountInfo(self, user, req):
- return super(XFileSharingPro if self.HOSTER_DOMAIN else XFSAccount, self).loadAccountInfo(user, req)
-
-
- def login(self, user, data, req):
- if self.HOSTER_DOMAIN:
- return super(XFileSharingPro, self).login(user, data, req)
diff --git a/module/plugins/accounts/YibaishiwuCom.py b/module/plugins/accounts/YibaishiwuCom.py
deleted file mode 100644
index 92a6bfedf..000000000
--- a/module/plugins/accounts/YibaishiwuCom.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Account import Account
-
-
-class YibaishiwuCom(Account):
- __name__ = "YibaishiwuCom"
- __type__ = "account"
- __version__ = "0.01"
-
- __description__ = """115.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- ACCOUNT_INFO_PATTERN = r'var USER_PERMISSION = {(.*?)}'
-
-
- def loadAccountInfo(self, user, req):
- #self.relogin(user)
- html = req.load("http://115.com/", decode=True)
-
- m = re.search(self.ACCOUNT_INFO_PATTERN, html, re.S)
- premium = True if (m and 'is_vip: 1' in m.group(1)) else False
- validuntil = trafficleft = (-1 if m else 0)
- return dict({"validuntil": validuntil, "trafficleft": trafficleft, "premium": premium})
-
-
- def login(self, user, data, req):
- html = req.load('http://passport.115.com/?ac=login', post={
- "back": "http://www.115.com/",
- "goto": "http://115.com/",
- "login[account]": user,
- "login[passwd]": data['password']
- }, decode=True)
-
- if not 'var USER_PERMISSION = {' in html:
- self.wrongPassword()
diff --git a/module/plugins/accounts/ZeveraCom.py b/module/plugins/accounts/ZeveraCom.py
deleted file mode 100644
index dbec6984d..000000000
--- a/module/plugins/accounts/ZeveraCom.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from time import mktime, strptime
-
-from module.plugins.Account import Account
-
-
-class ZeveraCom(Account):
- __name__ = "ZeveraCom"
- __type__ = "account"
- __version__ = "0.21"
-
- __description__ = """Zevera.com account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- def loadAccountInfo(self, user, req):
- data = self.getAPIData(req)
- if data == "No traffic":
- account_info = {"trafficleft": 0, "validuntil": 0, "premium": False}
- else:
- account_info = {
- "trafficleft": int(data['availabletodaytraffic']) * 1024,
- "validuntil": mktime(strptime(data['endsubscriptiondate'], "%Y/%m/%d %H:%M:%S")),
- "premium": True
- }
- return account_info
-
-
- def login(self, user, data, req):
- self.loginname = user
- self.password = data['password']
- if self.getAPIData(req) == "No traffic":
- self.wrongPassword()
-
-
- def getAPIData(self, req, just_header=False, **kwargs):
- get_data = {
- 'cmd': 'accountinfo',
- 'login': self.loginname,
- 'pass': self.password
- }
- get_data.update(kwargs)
-
- res = req.load("http://www.zevera.com/jDownloader.ashx", get=get_data,
- decode=True, just_header=just_header)
- self.logDebug(res)
-
- if ':' in res:
- if not just_header:
- res = res.replace(',', '\n')
- return dict((y.strip().lower(), z.strip()) for (y, z) in
- [x.split(':', 1) for x in res.splitlines() if ':' in x])
- else:
- return res
diff --git a/module/plugins/accounts/__init__.py b/module/plugins/accounts/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/plugins/accounts/__init__.py
+++ /dev/null
diff --git a/module/plugins/captcha/GigasizeCom.py b/module/plugins/captcha/GigasizeCom.py
deleted file mode 100644
index 99f432d12..000000000
--- a/module/plugins/captcha/GigasizeCom.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.captcha.captcha import OCR
-
-
-class GigasizeCom(OCR):
- __name__ = "GigasizeCom"
- __type__ = "ocr"
- __version__ = "0.1"
-
- __description__ = """Gigasize.com ocr plugin"""
- __license__ = "GPLv3"
- __authors__ = [("pyLoad Team", "admin@pyload.org")]
-
-
- def __init__(self):
- OCR.__init__(self)
-
-
- def get_captcha(self, image):
- self.load_image(image)
- self.threshold(2.8)
- self.run_tesser(True, False, False, True)
- return self.result_captcha
diff --git a/module/plugins/captcha/LinksaveIn.py b/module/plugins/captcha/LinksaveIn.py
deleted file mode 100644
index 41673d8a6..000000000
--- a/module/plugins/captcha/LinksaveIn.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# -*- coding: utf-8 -*-
-
-try:
- from PIL import Image
-except ImportError:
- import Image
-
-from glob import glob
-from os import sep
-from os.path import abspath, dirname
-
-from module.plugins.captcha.captcha import OCR
-
-
-class LinksaveIn(OCR):
- __name__ = "LinksaveIn"
- __type__ = "ocr"
- __version__ = "0.1"
-
- __description__ = """Linksave.in ocr plugin"""
- __license__ = "GPLv3"
- __authors__ = [("pyLoad Team", "admin@pyload.org")]
-
-
- def __init__(self):
- OCR.__init__(self)
- self.data_dir = dirname(abspath(__file__)) + sep + "LinksaveIn" + sep
-
-
- def load_image(self, image):
- im = Image.open(image)
- frame_nr = 0
-
- lut = im.resize((256, 1))
- lut.putdata(range(256))
- lut = list(lut.convert("RGB").getdata())
-
- new = Image.new("RGB", im.size)
- npix = new.load()
- while True:
- try:
- im.seek(frame_nr)
- except EOFError:
- break
- frame = im.copy()
- pix = frame.load()
- for x in xrange(frame.size[0]):
- for y in xrange(frame.size[1]):
- if lut[pix[x, y]] != (0,0,0):
- npix[x, y] = lut[pix[x, y]]
- frame_nr += 1
- new.save(self.data_dir+"unblacked.png")
- self.image = new.copy()
- self.pixels = self.image.load()
- self.result_captcha = ''
-
-
- def get_bg(self):
- stat = {}
- cstat = {}
- img = self.image.convert("P")
- for bgpath in glob(self.data_dir+"bg/*.gif"):
- stat[bgpath] = 0
- bg = Image.open(bgpath)
-
- bglut = bg.resize((256, 1))
- bglut.putdata(range(256))
- bglut = list(bglut.convert("RGB").getdata())
-
- lut = img.resize((256, 1))
- lut.putdata(range(256))
- lut = list(lut.convert("RGB").getdata())
-
- bgpix = bg.load()
- pix = img.load()
- for x in xrange(bg.size[0]):
- for y in xrange(bg.size[1]):
- rgb_bg = bglut[bgpix[x, y]]
- rgb_c = lut[pix[x, y]]
- try:
- cstat[rgb_c] += 1
- except:
- cstat[rgb_c] = 1
- if rgb_bg == rgb_c:
- stat[bgpath] += 1
- max_p = 0
- bg = ""
- for bgpath, value in stat.iteritems():
- if max_p < value:
- bg = bgpath
- max_p = value
- return bg
-
-
- def substract_bg(self, bgpath):
- bg = Image.open(bgpath)
- img = self.image.convert("P")
-
- bglut = bg.resize((256, 1))
- bglut.putdata(range(256))
- bglut = list(bglut.convert("RGB").getdata())
-
- lut = img.resize((256, 1))
- lut.putdata(range(256))
- lut = list(lut.convert("RGB").getdata())
-
- bgpix = bg.load()
- pix = img.load()
- orgpix = self.image.load()
- for x in xrange(bg.size[0]):
- for y in xrange(bg.size[1]):
- rgb_bg = bglut[bgpix[x, y]]
- rgb_c = lut[pix[x, y]]
- if rgb_c == rgb_bg:
- orgpix[x, y] = (255,255,255)
-
-
- def eval_black_white(self):
- new = Image.new("RGB", (140, 75))
- pix = new.load()
- orgpix = self.image.load()
- thresh = 4
- for x in xrange(new.size[0]):
- for y in xrange(new.size[1]):
- rgb = orgpix[x, y]
- r, g, b = rgb
- pix[x, y] = (255,255,255)
- if r > max(b, g)+thresh:
- pix[x, y] = (0,0,0)
- if g < min(r, b):
- pix[x, y] = (0,0,0)
- if g > max(r, b)+thresh:
- pix[x, y] = (0,0,0)
- if b > max(r, g)+thresh:
- pix[x, y] = (0,0,0)
- self.image = new
- self.pixels = self.image.load()
-
-
- def get_captcha(self, image):
- self.load_image(image)
- bg = self.get_bg()
- self.substract_bg(bg)
- self.eval_black_white()
- self.to_greyscale()
- self.image.save(self.data_dir+"cleaned_pass1.png")
- self.clean(4)
- self.clean(4)
- self.image.save(self.data_dir+"cleaned_pass2.png")
- letters = self.split_captcha_letters()
- final = ""
- for n, letter in enumerate(letters):
- self.image = letter
- self.image.save(ocr.data_dir+"letter%d.png" % n)
- self.run_tesser(True, True, False, False)
- final += self.result_captcha
-
- return final
diff --git a/module/plugins/captcha/NetloadIn.py b/module/plugins/captcha/NetloadIn.py
deleted file mode 100644
index fc8eecf59..000000000
--- a/module/plugins/captcha/NetloadIn.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.captcha.captcha import OCR
-
-
-class NetloadIn(OCR):
- __name__ = "NetloadIn"
- __type__ = "ocr"
- __version__ = "0.1"
-
- __description__ = """Netload.in ocr plugin"""
- __license__ = "GPLv3"
- __authors__ = [("pyLoad Team", "admin@pyload.org")]
-
-
- def __init__(self):
- OCR.__init__(self)
-
-
- def get_captcha(self, image):
- self.load_image(image)
- self.to_greyscale()
- self.clean(3)
- self.clean(3)
- self.run_tesser(True, True, False, False)
-
- self.result_captcha = self.result_captcha.replace(" ", "")[:4] # cut to 4 numbers
-
- return self.result_captcha
diff --git a/module/plugins/captcha/ShareonlineBiz.py b/module/plugins/captcha/ShareonlineBiz.py
deleted file mode 100644
index 6e513941d..000000000
--- a/module/plugins/captcha/ShareonlineBiz.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.captcha.captcha import OCR
-
-
-class ShareonlineBiz(OCR):
- __name__ = "ShareonlineBiz"
- __type__ = "ocr"
- __version__ = "0.1"
-
- __description__ = """Shareonline.biz ocr plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org")]
-
-
- def __init__(self):
- OCR.__init__(self)
-
-
- def get_captcha(self, image):
- self.load_image(image)
- self.to_greyscale()
- self.image = self.image.resize((160, 50))
- self.pixels = self.image.load()
- self.threshold(1.85)
- #self.eval_black_white(240)
- #self.derotate_by_average()
-
- letters = self.split_captcha_letters()
-
- final = ""
- for letter in letters:
- self.image = letter
- self.run_tesser(True, True, False, False)
- final += self.result_captcha
-
- return final
-
- #tesseract at 60%
diff --git a/module/plugins/captcha/__init__.py b/module/plugins/captcha/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/plugins/captcha/__init__.py
+++ /dev/null
diff --git a/module/plugins/captcha/captcha.py b/module/plugins/captcha/captcha.py
deleted file mode 100644
index b67ce9b9e..000000000
--- a/module/plugins/captcha/captcha.py
+++ /dev/null
@@ -1,317 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-try:
- from PIL import Image, GifImagePlugin, JpegImagePlugin, PngImagePlugin, TiffImagePlugin
-except ImportError:
- import Image, GifImagePlugin, JpegImagePlugin, PngImagePlugin, TiffImagePlugin
-
-import logging
-import os
-import subprocess
-#import tempfile
-
-from os.path import abspath, join
-
-
-class OCR(object):
- __name__ = "OCR"
- __type__ = "ocr"
- __version__ = "0.1"
-
- __description__ = """OCR base plugin"""
- __license__ = "GPLv3"
- __authors__ = [("pyLoad Team", "admin@pyload.org")]
-
-
- def __init__(self):
- self.logger = logging.getLogger("log")
-
-
- def load_image(self, image):
- self.image = Image.open(image)
- self.pixels = self.image.load()
- self.result_captcha = ''
-
-
- def unload(self):
- """delete all tmp images"""
- pass
-
-
- def threshold(self, value):
- self.image = self.image.point(lambda a: a * value + 10)
-
-
- def run(self, command):
- """Run a command"""
-
- popen = subprocess.Popen(command, bufsize = -1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- popen.wait()
- output = popen.stdout.read() +" | "+ popen.stderr.read()
- popen.stdout.close()
- popen.stderr.close()
- self.logger.debug("Tesseract ReturnCode %s Output: %s" % (popen.returncode, output))
-
-
- def run_tesser(self, subset=False, digits=True, lowercase=True, uppercase=True):
- #tmpTif = tempfile.NamedTemporaryFile(suffix=".tif")
- try:
- tmpTif = open(join("tmp", "tmpTif_%s.tif" % self.__name__), "wb")
- tmpTif.close()
-
- #tmpTxt = tempfile.NamedTemporaryFile(suffix=".txt")
- tmpTxt = open(join("tmp", "tmpTxt_%s.txt" % self.__name__), "wb")
- tmpTxt.close()
-
- except IOError, e:
- self.logError(e)
- return
-
- self.logger.debug("save tiff")
- self.image.save(tmpTif.name, 'TIFF')
-
- if os.name == "nt":
- tessparams = [join(pypath, "tesseract", "tesseract.exe")]
- else:
- tessparams = ["tesseract"]
-
- tessparams.extend( [abspath(tmpTif.name), abspath(tmpTxt.name).replace(".txt", "")] )
-
- if subset and (digits or lowercase or uppercase):
- #tmpSub = tempfile.NamedTemporaryFile(suffix=".subset")
- tmpSub = open(join("tmp", "tmpSub_%s.subset" % self.__name__), "wb")
- tmpSub.write("tessedit_char_whitelist ")
- if digits:
- tmpSub.write("0123456789")
- if lowercase:
- tmpSub.write("abcdefghijklmnopqrstuvwxyz")
- if uppercase:
- tmpSub.write("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
- tmpSub.write("\n")
- tessparams.append("nobatch")
- tessparams.append(abspath(tmpSub.name))
- tmpSub.close()
-
- self.logger.debug("run tesseract")
- self.run(tessparams)
- self.logger.debug("read txt")
-
- try:
- with open(tmpTxt.name, 'r') as f:
- self.result_captcha = f.read().replace("\n", "")
- except:
- self.result_captcha = ""
-
- self.logger.debug(self.result_captcha)
- try:
- os.remove(tmpTif.name)
- os.remove(tmpTxt.name)
- if subset and (digits or lowercase or uppercase):
- os.remove(tmpSub.name)
- except:
- pass
-
-
- def get_captcha(self, name):
- raise NotImplementedError
-
-
- def to_greyscale(self):
- if self.image.mode != 'L':
- self.image = self.image.convert('L')
-
- self.pixels = self.image.load()
-
-
- def eval_black_white(self, limit):
- self.pixels = self.image.load()
- w, h = self.image.size
- for x in xrange(w):
- for y in xrange(h):
- if self.pixels[x, y] > limit:
- self.pixels[x, y] = 255
- else:
- self.pixels[x, y] = 0
-
-
- def clean(self, allowed):
- pixels = self.pixels
-
- w, h = self.image.size
-
- for x in xrange(w):
- for y in xrange(h):
- if pixels[x, y] == 255:
- continue
- # No point in processing white pixels since we only want to remove black pixel
- count = 0
-
- try:
- if pixels[x-1, y-1] != 255:
- count += 1
- if pixels[x-1, y] != 255:
- count += 1
- if pixels[x-1, y + 1] != 255:
- count += 1
- if pixels[x, y + 1] != 255:
- count += 1
- if pixels[x + 1, y + 1] != 255:
- count += 1
- if pixels[x + 1, y] != 255:
- count += 1
- if pixels[x + 1, y-1] != 255:
- count += 1
- if pixels[x, y-1] != 255:
- count += 1
- except:
- pass
-
- # not enough neighbors are dark pixels so mark this pixel
- # to be changed to white
- if count < allowed:
- pixels[x, y] = 1
-
- # second pass: this time set all 1's to 255 (white)
- for x in xrange(w):
- for y in xrange(h):
- if pixels[x, y] == 1:
- pixels[x, y] = 255
-
- self.pixels = pixels
-
-
- def derotate_by_average(self):
- """rotate by checking each angle and guess most suitable"""
-
- w, h = self.image.size
- pixels = self.pixels
-
- for x in xrange(w):
- for y in xrange(h):
- if pixels[x, y] == 0:
- pixels[x, y] = 155
-
- highest = {}
- counts = {}
-
- for angle in xrange(-45, 45):
-
- tmpimage = self.image.rotate(angle)
-
- pixels = tmpimage.load()
-
- w, h = self.image.size
-
- for x in xrange(w):
- for y in xrange(h):
- if pixels[x, y] == 0:
- pixels[x, y] = 255
-
-
- count = {}
-
- for x in xrange(w):
- count[x] = 0
- for y in xrange(h):
- if pixels[x, y] == 155:
- count[x] += 1
-
- sum = 0
- cnt = 0
-
- for x in count.values():
- if x != 0:
- sum += x
- cnt += 1
-
- avg = sum / cnt
- counts[angle] = cnt
- highest[angle] = 0
- for x in count.values():
- if x > highest[angle]:
- highest[angle] = x
-
- highest[angle] = highest[angle] - avg
-
- hkey = 0
- hvalue = 0
-
- for key, value in highest.iteritems():
- if value > hvalue:
- hkey = key
- hvalue = value
-
- self.image = self.image.rotate(hkey)
- pixels = self.image.load()
-
- for x in xrange(w):
- for y in xrange(h):
- if pixels[x, y] == 0:
- pixels[x, y] = 255
-
- if pixels[x, y] == 155:
- pixels[x, y] = 0
-
- self.pixels = pixels
-
-
- def split_captcha_letters(self):
- captcha = self.image
- started = False
- letters = []
- width, height = captcha.size
- bottomY, topY = 0, height
- pixels = captcha.load()
-
- for x in xrange(width):
- black_pixel_in_col = False
- for y in xrange(height):
- if pixels[x, y] != 255:
- if not started:
- started = True
- firstX = x
- lastX = x
-
- if y > bottomY:
- bottomY = y
- if y < topY:
- topY = y
- if x > lastX:
- lastX = x
-
- black_pixel_in_col = True
-
- if black_pixel_in_col is False and started is True:
- rect = (firstX, topY, lastX, bottomY)
- new_captcha = captcha.crop(rect)
-
- w, h = new_captcha.size
- if w > 5 and h > 5:
- letters.append(new_captcha)
-
- started = False
- bottomY, topY = 0, height
-
- return letters
-
-
- def correct(self, values, var=None):
- if var:
- result = var
- else:
- result = self.result_captcha
-
- for key, item in values.iteritems():
-
- if key.__class__ == str:
- result = result.replace(key, item)
- else:
- for expr in key:
- result = result.replace(expr, item)
-
- if var:
- return result
- else:
- self.result_captcha = result
diff --git a/module/plugins/container/CCF.py b/module/plugins/container/CCF.py
deleted file mode 100644
index c7824fcc4..000000000
--- a/module/plugins/container/CCF.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from os import makedirs
-from os.path import exists
-from urllib2 import build_opener
-
-from MultipartPostHandler import MultipartPostHandler
-
-from module.plugins.Container import Container
-from module.utils import save_join
-
-
-class CCF(Container):
- __name__ = "CCF"
- __version__ = "0.2"
-
- __pattern__ = r'.+\.ccf'
-
- __description__ = """CCF container decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Willnix", "Willnix@pyload.org")]
-
-
- def decrypt(self, pyfile):
- infile = pyfile.url.replace("\n", "")
-
- opener = build_opener(MultipartPostHandler)
- params = {"src": "ccf",
- "filename": "test.ccf",
- "upload": open(infile, "rb")}
- tempdlc_content = opener.open('http://service.jdownloader.net/dlcrypt/getDLC.php', params).read()
-
- download_folder = self.config['general']['download_folder']
-
- tempdlc_name = save_join(download_folder, "tmp_%s.dlc" % pyfile.name)
- tempdlc = open(tempdlc_name, "w")
- tempdlc.write(re.search(r'<dlc>(.*)</dlc>', tempdlc_content, re.S).group(1))
- tempdlc.close()
-
- self.urls = [tempdlc_name]
diff --git a/module/plugins/container/DLC_25.pyc b/module/plugins/container/DLC_25.pyc
deleted file mode 100644
index 409264902..000000000
--- a/module/plugins/container/DLC_25.pyc
+++ /dev/null
Binary files differ
diff --git a/module/plugins/container/DLC_26.pyc b/module/plugins/container/DLC_26.pyc
deleted file mode 100644
index 685995fc2..000000000
--- a/module/plugins/container/DLC_26.pyc
+++ /dev/null
Binary files differ
diff --git a/module/plugins/container/DLC_27.pyc b/module/plugins/container/DLC_27.pyc
deleted file mode 100644
index 6c6d663e5..000000000
--- a/module/plugins/container/DLC_27.pyc
+++ /dev/null
Binary files differ
diff --git a/module/plugins/container/LinkList.py b/module/plugins/container/LinkList.py
deleted file mode 100644
index c6173ad73..000000000
--- a/module/plugins/container/LinkList.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import codecs
-
-from module.plugins.Container import Container
-from module.utils import fs_encode
-
-
-class LinkList(Container):
- __name__ = "LinkList"
- __version__ = "0.12"
-
- __pattern__ = r'.+\.txt'
- __config__ = [("clear", "bool", "Clear Linklist after adding", False),
- ("encoding", "string", "File encoding (default utf-8)", "")]
-
- __description__ = """Read link lists in txt format"""
- __license__ = "GPLv3"
- __authors__ = [("spoob", "spoob@pyload.org"),
- ("jeix", "jeix@hasnomail.com")]
-
-
- def decrypt(self, pyfile):
- try:
- file_enc = codecs.lookup(self.getConfig("encoding")).name
- except:
- file_enc = "utf-8"
-
- file_name = fs_encode(pyfile.url)
-
- txt = codecs.open(file_name, 'r', file_enc)
- links = txt.readlines()
- curPack = "Parsed links from %s" % pyfile.name
-
- packages = {curPack:[],}
-
- for link in links:
- link = link.strip()
- if not link:
- continue
-
- if link.startswith(";"):
- continue
- if link.startswith("[") and link.endswith("]"):
- # new package
- curPack = link[1:-1]
- packages[curPack] = []
- continue
- packages[curPack].append(link)
- txt.close()
-
- # empty packages fix
-
- delete = []
-
- for key,value in packages.iteritems():
- if not value:
- delete.append(key)
-
- for key in delete:
- del packages[key]
-
- if self.getConfig("clear"):
- try:
- txt = open(file_name, 'wb')
- txt.close()
- except:
- self.logWarning(_("LinkList could not be cleared"))
-
- for name, links in packages.iteritems():
- self.packages.append((name, links, name))
diff --git a/module/plugins/container/RSDF.py b/module/plugins/container/RSDF.py
deleted file mode 100644
index 0c43f0e6c..000000000
--- a/module/plugins/container/RSDF.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-import base64
-import binascii
-import re
-
-from module.plugins.Container import Container
-from module.utils import fs_encode
-
-
-class RSDF(Container):
- __name__ = "RSDF"
- __version__ = "0.24"
-
- __pattern__ = r'.+\.rsdf'
-
- __description__ = """RSDF container decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("spoob", "spoob@pyload.org")]
-
-
- def decrypt(self, pyfile):
-
- from Crypto.Cipher import AES
-
- infile = fs_encode(pyfile.url.replace("\n", ""))
- Key = binascii.unhexlify('8C35192D964DC3182C6F84F3252239EB4A320D2500000000')
-
- IV = binascii.unhexlify('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF')
- IV_Cipher = AES.new(Key, AES.MODE_ECB)
- IV = IV_Cipher.encrypt(IV)
-
- obj = AES.new(Key, AES.MODE_CFB, IV)
-
- try:
- with open(infile, 'r') as rsdf:
- data = rsdf.read()
- except IOError, e:
- self.fail(str(e))
-
- if re.search(r"<title>404 - Not Found</title>", data) is None:
- data = binascii.unhexlify(''.join(data.split()))
- data = data.splitlines()
-
- for link in data:
- if not link:
- continue
- link = base64.b64decode(link)
- link = obj.decrypt(link)
- decryptedUrl = link.replace('CCF: ', '')
- self.urls.append(decryptedUrl)
-
- self.logDebug("Adding package %s with %d links" % (pyfile.package().name, len(self.urls)))
diff --git a/module/plugins/container/__init__.py b/module/plugins/container/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/plugins/container/__init__.py
+++ /dev/null
diff --git a/module/plugins/crypter/BitshareComFolder.py b/module/plugins/crypter/BitshareComFolder.py
deleted file mode 100644
index e4038aebf..000000000
--- a/module/plugins/crypter/BitshareComFolder.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class BitshareComFolder(SimpleCrypter):
- __name__ = "BitshareComFolder"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?bitshare\.com/\?d=\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Bitshare.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- LINK_PATTERN = r'<a href="(http://bitshare\.com/files/.+)">.+</a></td>'
- NAME_PATTERN = r'View public folder "(?P<N>.+)"</h1>'
diff --git a/module/plugins/crypter/C1neonCom.py b/module/plugins/crypter/C1neonCom.py
deleted file mode 100644
index cf1d2a211..000000000
--- a/module/plugins/crypter/C1neonCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class C1neonCom(DeadCrypter):
- __name__ = "C1neonCom"
- __type__ = "crypter"
- __version__ = "0.05"
-
- __pattern__ = r'http://(?:www\.)?c1neon\.com/.*?'
- __config__ = []
-
- __description__ = """C1neon.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("godofdream", "soilfiction@gmail.com")]
-
-
-getInfo = create_getInfo(C1neonCom)
diff --git a/module/plugins/crypter/ChipDe.py b/module/plugins/crypter/ChipDe.py
deleted file mode 100644
index 59ed6559e..000000000
--- a/module/plugins/crypter/ChipDe.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from module.plugins.Crypter import Crypter
-
-
-class ChipDe(Crypter):
- __name__ = "ChipDe"
- __type__ = "crypter"
- __version__ = "0.1"
-
- __pattern__ = r'http://(?:www\.)?chip\.de/video/.*\.html'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Chip.de decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("4Christopher", "4Christopher@gmx.de")]
-
-
- def decrypt(self, pyfile):
- self.html = self.load(pyfile.url)
- try:
- f = re.search(r'"(http://video\.chip\.de/.+)"', self.html)
- except:
- self.fail(_("Failed to find the URL"))
- else:
- self.urls = [f.group(1)]
- self.logDebug("The file URL is %s" % self.urls[0])
diff --git a/module/plugins/crypter/CrockoComFolder.py b/module/plugins/crypter/CrockoComFolder.py
deleted file mode 100644
index 64a1f8bce..000000000
--- a/module/plugins/crypter/CrockoComFolder.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class CrockoComFolder(SimpleCrypter):
- __name__ = "CrockoComFolder"
- __type__ = "crypter"
- __version__ = "0.01"
-
- __pattern__ = r'http://(?:www\.)?crocko\.com/f/.*'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Crocko.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- LINK_PATTERN = r'<td class="last"><a href="([^"]+)">download</a>'
diff --git a/module/plugins/crypter/CryptItCom.py b/module/plugins/crypter/CryptItCom.py
deleted file mode 100644
index 2cf4e9f62..000000000
--- a/module/plugins/crypter/CryptItCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class CryptItCom(DeadCrypter):
- __name__ = "CryptItCom"
- __type__ = "crypter"
- __version__ = "0.11"
-
- __pattern__ = r'http://(?:www\.)?crypt-it\.com/(s|e|d|c)/\w+'
- __config__ = []
-
- __description__ = """Crypt-it.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de")]
-
-
-getInfo = create_getInfo(CryptItCom)
diff --git a/module/plugins/crypter/CzshareComFolder.py b/module/plugins/crypter/CzshareComFolder.py
deleted file mode 100644
index 669f469b2..000000000
--- a/module/plugins/crypter/CzshareComFolder.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from module.plugins.Crypter import Crypter
-
-
-class CzshareComFolder(Crypter):
- __name__ = "CzshareComFolder"
- __type__ = "crypter"
- __version__ = "0.2"
-
- __pattern__ = r'http://(?:www\.)?(czshare|sdilej)\.(com|cz)/folders/.*'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Czshare.com folder decrypter plugin, now Sdilej.cz"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- FOLDER_PATTERN = r'<tr class="subdirectory">\s*<td>\s*<table>(.*?)</table>'
- LINK_PATTERN = r'<td class="col2"><a href="([^"]+)">info</a></td>'
-
-
- def decrypt(self, pyfile):
- html = self.load(pyfile.url)
-
- m = re.search(self.FOLDER_PATTERN, html, re.S)
- if m is None:
- self.error(_("FOLDER_PATTERN not found"))
-
- self.urls.extend(re.findall(self.LINK_PATTERN, m.group(1)))
diff --git a/module/plugins/crypter/DDLMusicOrg.py b/module/plugins/crypter/DDLMusicOrg.py
deleted file mode 100644
index c2524b180..000000000
--- a/module/plugins/crypter/DDLMusicOrg.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import sleep
-
-from module.plugins.Crypter import Crypter
-
-
-class DDLMusicOrg(Crypter):
- __name__ = "DDLMusicOrg"
- __type__ = "crypter"
- __version__ = "0.3"
-
- __pattern__ = r'http://(?:www\.)?ddl-music\.org/captcha/ddlm_cr\d\.php\?\d+\?\d+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Ddl-music.org decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("mkaay", "mkaay@mkaay.de")]
-
-
- def setup(self):
- self.multiDL = False
-
-
- def decrypt(self, pyfile):
- html = self.load(pyfile.url, cookies=True)
-
- if re.search(r"Wer dies nicht rechnen kann", html) is not None:
- self.offline()
-
- math = re.search(r"(\d+) ([+-]) (\d+) =\s+<inp", self.html)
- id = re.search(r"name=\"id\" value=\"(\d+)\"", self.html).group(1)
- linknr = re.search(r"name=\"linknr\" value=\"(\d+)\"", self.html).group(1)
-
- solve = ""
- if math.group(2) == "+":
- solve = int(math.group(1)) + int(math.group(3))
- else:
- solve = int(math.group(1)) - int(math.group(3))
- sleep(3)
- htmlwithlink = self.load(pyfile.url, cookies=True,
- post={"calc%s" % linknr: solve, "send%s" % linknr: "Send", "id": id,
- "linknr": linknr})
- m = re.search(r"<form id=\"ff\" action=\"(.*?)\" method=\"post\">", htmlwithlink)
- if m:
- self.urls = [m.group(1)]
- else:
- self.retry()
diff --git a/module/plugins/crypter/DailymotionBatch.py b/module/plugins/crypter/DailymotionBatch.py
deleted file mode 100644
index 28e01c084..000000000
--- a/module/plugins/crypter/DailymotionBatch.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urljoin
-
-from module.common.json_layer import json_loads
-from module.plugins.Crypter import Crypter
-from module.utils import save_join
-
-
-class DailymotionBatch(Crypter):
- __name__ = "DailymotionBatch"
- __type__ = "crypter"
- __version__ = "0.01"
-
- __pattern__ = r'https?://(?:www\.)?dailymotion\.com/((playlists/)?(?P<TYPE>playlist|user)/)?(?P<ID>[\w^_]+)(?(TYPE)|#)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Dailymotion.com channel & playlist decrypter"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- def api_response(self, ref, req=None):
- url = urljoin("https://api.dailymotion.com/", ref)
- page = self.load(url, get=req)
- return json_loads(page)
-
-
- def getPlaylistInfo(self, id):
- ref = "playlist/" + id
- req = {"fields": "name,owner.screenname"}
- playlist = self.api_response(ref, req)
-
- if "error" in playlist:
- return
-
- name = playlist['name']
- owner = playlist['owner.screenname']
- return name, owner
-
-
- def _getPlaylists(self, user_id, page=1):
- ref = "user/%s/playlists" % user_id
- req = {"fields": "id", "page": page, "limit": 100}
- user = self.api_response(ref, req)
-
- if "error" in user:
- return
-
- for playlist in user['list']:
- yield playlist['id']
-
- if user['has_more']:
- for item in self._getPlaylists(user_id, page + 1):
- yield item
-
-
- def getPlaylists(self, user_id):
- return [(id,) + self.getPlaylistInfo(id) for id in self._getPlaylists(user_id)]
-
-
- def _getVideos(self, id, page=1):
- ref = "playlist/%s/videos" % id
- req = {"fields": "url", "page": page, "limit": 100}
- playlist = self.api_response(ref, req)
-
- if "error" in playlist:
- return
-
- for video in playlist['list']:
- yield video['url']
-
- if playlist['has_more']:
- for item in self._getVideos(id, page + 1):
- yield item
-
-
- def getVideos(self, playlist_id):
- return list(self._getVideos(playlist_id))[::-1]
-
-
- def decrypt(self, pyfile):
- m = re.match(self.__pattern__, pyfile.url)
- m_id = m.group("ID")
- m_type = m.group("TYPE")
-
- if m_type == "playlist":
- self.logDebug("Url recognized as Playlist")
- p_info = self.getPlaylistInfo(m_id)
- playlists = [(m_id,) + p_info] if p_info else None
- else:
- self.logDebug("Url recognized as Channel")
- playlists = self.getPlaylists(m_id)
- self.logDebug("%s playlist\s found on channel \"%s\"" % (len(playlists), m_id))
-
- if not playlists:
- self.fail(_("No playlist available"))
-
- for p_id, p_name, p_owner in playlists:
- p_videos = self.getVideos(p_id)
- p_folder = save_join(self.config['general']['download_folder'], p_owner, p_name)
- self.logDebug("%s video\s found on playlist \"%s\"" % (len(p_videos), p_name))
- self.packages.append((p_name, p_videos, p_folder)) #: folder is NOT recognized by pyload 0.4.9!
diff --git a/module/plugins/crypter/DataHuFolder.py b/module/plugins/crypter/DataHuFolder.py
deleted file mode 100644
index 0399e686d..000000000
--- a/module/plugins/crypter/DataHuFolder.py
+++ /dev/null
@@ -1,40 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class DataHuFolder(SimpleCrypter):
- __name__ = "DataHuFolder"
- __type__ = "crypter"
- __version__ = "0.06"
-
- __pattern__ = r'http://(?:www\.)?data\.hu/dir/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Data.hu folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("crash", None),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- LINK_PATTERN = r'<a href=\'(http://data\.hu/get/.+)\' target=\'_blank\'>\1</a>'
- NAME_PATTERN = ur'<title>(?P<N>.+) Let\xf6lt\xe9se</title>'
-
-
- def prepare(self):
- super(DataHuFolder, self).prepare()
-
- if u'K\xe9rlek add meg a jelsz\xf3t' in self.html: # Password protected
- password = self.getPassword()
- if not password:
- self.fail(_("Password required"))
-
- self.logDebug("The folder is password protected', 'Using password: " + password)
-
- self.html = self.load(self.pyfile.url, post={'mappa_pass': password}, decode=True)
-
- if u'Hib\xe1s jelsz\xf3' in self.html: # Wrong password
- self.fail(_("Wrong password"))
diff --git a/module/plugins/crypter/DdlstorageComFolder.py b/module/plugins/crypter/DdlstorageComFolder.py
deleted file mode 100644
index e02e77fda..000000000
--- a/module/plugins/crypter/DdlstorageComFolder.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class DdlstorageComFolder(DeadCrypter):
- __name__ = "DdlstorageComFolder"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'https?://(?:www\.)?ddlstorage\.com/folder/\w+'
- __config__ = []
-
- __description__ = """DDLStorage.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("godofdream", "soilfiction@gmail.com"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(DdlstorageComFolder)
diff --git a/module/plugins/crypter/DepositfilesComFolder.py b/module/plugins/crypter/DepositfilesComFolder.py
deleted file mode 100644
index 79c31c97a..000000000
--- a/module/plugins/crypter/DepositfilesComFolder.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class DepositfilesComFolder(SimpleCrypter):
- __name__ = "DepositfilesComFolder"
- __type__ = "crypter"
- __version__ = "0.01"
-
- __pattern__ = r'http://(?:www\.)?depositfiles\.com/folders/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Depositfiles.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- LINK_PATTERN = r'<div class="progressName"[^>]*>\s*<a href="([^"]+)" title="[^"]*" target="_blank">'
diff --git a/module/plugins/crypter/Dereferer.py b/module/plugins/crypter/Dereferer.py
deleted file mode 100644
index 70b42b00c..000000000
--- a/module/plugins/crypter/Dereferer.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urllib import unquote
-
-from module.plugins.Crypter import Crypter
-
-
-class Dereferer(Crypter):
- __name__ = "Dereferer"
- __type__ = "crypter"
- __version__ = "0.1"
-
- __pattern__ = r'https?://([^/]+)/.*?(?P<url>(ht|f)tps?(://|%3A%2F%2F).*)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Crypter for dereferers"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- def decrypt(self, pyfile):
- link = re.match(self.__pattern__, pyfile.url).group('url')
- self.urls = [unquote(link).rstrip('+')]
diff --git a/module/plugins/crypter/DevhostStFolder.py b/module/plugins/crypter/DevhostStFolder.py
deleted file mode 100644
index 192c5962c..000000000
--- a/module/plugins/crypter/DevhostStFolder.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://d-h.st/users/shine/?fld_id=37263#files
-
-import re
-
-from urlparse import urljoin
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class DevhostStFolder(SimpleCrypter):
- __name__ = "DevhostStFolder"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?d-h\.st/users/(?P<USER>\w+)(/\?fld_id=(?P<ID>\d+))?'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """d-h.st folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zapp-brannigan", "fuerst.reinje@web.de"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- LINK_PATTERN = r'(?:/> |;">)<a href="(.+?)"(?!>Back to \w+<)'
- OFFLINE_PATTERN = r'"/cHP">test\.png<'
-
-
- def getFileInfo(self):
- if re.search(self.OFFLINE_PATTERN, self.html):
- self.offline()
-
- try:
- id = re.match(self.__pattern__, self.pyfile.url).group('ID')
- if id == "0":
- raise
-
- p = r'href="(.+?)">Back to \w+<'
- m = re.search(p, self.html)
- html = self.load(urljoin("http://d-h.st", m.group(1)),
- cookies=False)
-
- p = '\?fld_id=%s.*?">(.+?)<' % id
- m = re.search(p, html)
- name = folder = m.group(1)
-
- except Exception, e:
- self.logDebug(e)
- name = folder = re.match(self.__pattern__, self.pyfile.url).group('USER')
-
- return {'name': name, 'folder': folder}
-
-
- def getLinks(self):
- return [urljoin("http://d-h.st", link) for link in re.findall(self.LINK_PATTERN, self.html)]
diff --git a/module/plugins/crypter/DlProtectCom.py b/module/plugins/crypter/DlProtectCom.py
deleted file mode 100644
index 5efb03c15..000000000
--- a/module/plugins/crypter/DlProtectCom.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from base64 import urlsafe_b64encode
-from time import time
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class DlProtectCom(SimpleCrypter):
- __name__ = "DlProtectCom"
- __type__ = "crypter"
- __version__ = "0.01"
-
- __pattern__ = r'http://(?:www\.)?dl-protect\.com/((en|fr)/)?(?P<ID>\w+)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Dl-protect.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- OFFLINE_PATTERN = r'>Unfortunately, the link you are looking for is not found'
-
-
- def getLinks(self):
- # Direct link with redirect
- if not re.match(r"http://(?:www\.)?dl-protect\.com", self.req.http.lastEffectiveURL):
- return [self.req.http.lastEffectiveURL]
-
- #id = re.match(self.__pattern__, self.pyfile.url).group("ID")
- key = re.search(r'name="id_key" value="(.+?)"', self.html).group(1)
-
- post_req = {"id_key": key, "submitform": ""}
-
- if self.OFFLINE_PATTERN in self.html:
- self.offline()
- elif ">Please click on continue to see the content" in self.html:
- post_req.update({"submitform": "Continue"})
- else:
- mstime = int(round(time() * 1000))
- b64time = "_" + urlsafe_b64encode(str(mstime)).replace("=", "%3D")
-
- post_req.update({"i": b64time, "submitform": "Decrypt+link"})
-
- if ">Password :" in self.html:
- post_req['pwd'] = self.getPassword()
-
- if ">Security Code" in self.html:
- captcha_id = re.search(r'/captcha\.php\?uid=(.+?)"', self.html).group(1)
- captcha_url = "http://www.dl-protect.com/captcha.php?uid=" + captcha_id
- captcha_code = self.decryptCaptcha(captcha_url, imgtype="gif")
-
- post_req['secure'] = captcha_code
-
- self.html = self.load(self.pyfile.url, post=post_req)
-
- for errmsg in (">The password is incorrect", ">The security code is incorrect"):
- if errmsg in self.html:
- self.fail(_(errmsg[1:]))
-
- pattern = r'<a href="([^/].+?)" target="_blank">'
- return re.findall(pattern, self.html)
diff --git a/module/plugins/crypter/DontKnowMe.py b/module/plugins/crypter/DontKnowMe.py
deleted file mode 100644
index 6783abd7c..000000000
--- a/module/plugins/crypter/DontKnowMe.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urllib import unquote
-
-from module.plugins.Crypter import Crypter
-
-
-class DontKnowMe(Crypter):
- __name__ = "DontKnowMe"
- __type__ = "crypter"
- __version__ = "0.1"
-
- __pattern__ = r'http://(?:www\.)?dontknow\.me/at/\?.+$'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """DontKnow.me decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("selaux", None)]
-
-
- LINK_PATTERN = r'http://dontknow\.me/at/\?(.+)$'
-
-
- def decrypt(self, pyfile):
- link = re.findall(self.LINK_PATTERN, pyfile.url)[0]
- self.urls = [unquote(link)]
diff --git a/module/plugins/crypter/DuckCryptInfo.py b/module/plugins/crypter/DuckCryptInfo.py
deleted file mode 100644
index 07cc5cdc4..000000000
--- a/module/plugins/crypter/DuckCryptInfo.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from BeautifulSoup import BeautifulSoup
-
-from module.plugins.Crypter import Crypter
-
-
-class DuckCryptInfo(Crypter):
- __name__ = "DuckCryptInfo"
- __type__ = "crypter"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?duckcrypt\.info/(folder|wait|link)/(\w+)/?(\w*)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """DuckCrypt.info decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("godofdream", "soilfiction@gmail.com")]
-
-
- TIMER_PATTERN = r'<span id="timer">(.*)</span>'
-
-
- def decrypt(self, pyfile):
- url = pyfile.url
-
- m = re.match(self.__pattern__, url)
- if m is None:
- self.fail(_("Weird error in link"))
- if str(m.group(1)) == "link":
- self.handleLink(url)
- else:
- self.handleFolder(m)
-
-
- def handleFolder(self, m):
- html = self.load("http://duckcrypt.info/ajax/auth.php?hash=" + str(m.group(2)))
- m = re.match(self.__pattern__, html)
- self.logDebug("Redirectet to " + str(m.group(0)))
- html = self.load(str(m.group(0)))
- soup = BeautifulSoup(html)
- cryptlinks = soup.findAll("div", attrs={"class": "folderbox"})
- self.logDebug("Redirectet to " + str(cryptlinks))
- if not cryptlinks:
- self.error(_("No link found"))
- for clink in cryptlinks:
- if clink.find("a"):
- self.handleLink(clink.find("a")['href'])
-
-
- def handleLink(self, url):
- html = self.load(url)
- soup = BeautifulSoup(html)
- self.urls = [soup.find("iframe")['src']]
- if not self.urls:
- self.logInfo(_("No link found"))
diff --git a/module/plugins/crypter/DuploadOrgFolder.py b/module/plugins/crypter/DuploadOrgFolder.py
deleted file mode 100644
index 066fbe3d7..000000000
--- a/module/plugins/crypter/DuploadOrgFolder.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class DuploadOrgFolder(DeadCrypter):
- __name__ = "DuploadOrgFolder"
- __type__ = "crypter"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?dupload\.org/folder/\d+'
- __config__ = []
-
- __description__ = """Dupload.org folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(DuploadOrgFolder)
diff --git a/module/plugins/crypter/EasybytezComFolder.py b/module/plugins/crypter/EasybytezComFolder.py
deleted file mode 100644
index fa3e6165c..000000000
--- a/module/plugins/crypter/EasybytezComFolder.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSCrypter import XFSCrypter
-
-
-class EasybytezComFolder(XFSCrypter):
- __name__ = "EasybytezComFolder"
- __type__ = "crypter"
- __version__ = "0.10"
-
- __pattern__ = r'http://(?:www\.)?easybytez\.com/users/\d+/\d+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Easybytez.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- HOSTER_DOMAIN = "easybytez.com"
-
- LOGIN_ACCOUNT = True
diff --git a/module/plugins/crypter/EmbeduploadCom.py b/module/plugins/crypter/EmbeduploadCom.py
deleted file mode 100644
index 1c372c1ca..000000000
--- a/module/plugins/crypter/EmbeduploadCom.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from module.plugins.Crypter import Crypter
-from module.network.HTTPRequest import BadHeader
-
-
-class EmbeduploadCom(Crypter):
- __name__ = "EmbeduploadCom"
- __type__ = "crypter"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?embedupload\.com/\?d=.*'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True),
- ("preferedHoster", "str", "Prefered hoster list (bar-separated)", "embedupload"),
- ("ignoredHoster", "str", "Ignored hoster list (bar-separated)", "")]
-
- __description__ = """EmbedUpload.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- LINK_PATTERN = r'<div id="([^"]+)"[^>]*>\s*<a href="([^"]+)" target="_blank" (?:class="DownloadNow"|style="color:red")>'
-
-
- def decrypt(self, pyfile):
- self.html = self.load(pyfile.url, decode=True)
- tmp_links = []
-
- m = re.findall(self.LINK_PATTERN, self.html)
- if m:
- prefered_set = set(self.getConfig("preferedHoster").split('|'))
- prefered_set = map(lambda s: s.lower().split('.')[0], prefered_set)
-
- self.logDebug("PF: %s" % prefered_set)
-
- tmp_links.extend([x[1] for x in m if x[0] in prefered_set])
- self.urls = self.getLocation(tmp_links)
-
- if not self.urls:
- ignored_set = set(self.getConfig("ignoredHoster").split('|'))
- ignored_set = map(lambda s: s.lower().split('.')[0], ignored_set)
-
- self.logDebug("IG: %s" % ignored_set)
-
- tmp_links.extend([x[1] for x in m if x[0] not in ignored_set])
- self.urls = self.getLocation(tmp_links)
-
-
- def getLocation(self, tmp_links):
- new_links = []
- for link in tmp_links:
- try:
- header = self.load(link, just_header=True)
- if 'location' in header:
- new_links.append(header['location'])
- except BadHeader:
- pass
- return new_links
diff --git a/module/plugins/crypter/FilebeerInfoFolder.py b/module/plugins/crypter/FilebeerInfoFolder.py
deleted file mode 100644
index d0f3547bc..000000000
--- a/module/plugins/crypter/FilebeerInfoFolder.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class FilebeerInfoFolder(DeadCrypter):
- __name__ = "FilebeerInfoFolder"
- __type__ = "crypter"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?filebeer\.info/(\d+~f).*'
- __config__ = []
-
- __description__ = """Filebeer.info folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(FilebeerInfoFolder)
diff --git a/module/plugins/crypter/FilecloudIoFolder.py b/module/plugins/crypter/FilecloudIoFolder.py
deleted file mode 100644
index 5640ab5c9..000000000
--- a/module/plugins/crypter/FilecloudIoFolder.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class FilecloudIoFolder(SimpleCrypter):
- __name__ = "FilecloudIoFolder"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'https?://(?:www\.)?(filecloud\.io|ifile\.it)/_\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Filecloud.io folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- LINK_PATTERN = r'href="(http://filecloud\.io/\w+)" title'
- NAME_PATTERN = r'>(?P<N>.+?) - filecloud\.io<'
diff --git a/module/plugins/crypter/FilecryptCc.py b/module/plugins/crypter/FilecryptCc.py
deleted file mode 100644
index fb53e616b..000000000
--- a/module/plugins/crypter/FilecryptCc.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import base64
-import binascii
-import re
-
-from Crypto.Cipher import AES
-
-from module.plugins.Crypter import Crypter
-
-
-class FilecryptCc(Crypter):
- __name__ = "FilecryptCc"
- __type__ = "crypter"
- __version__ = "0.05"
-
- __pattern__ = r'https?://(?:www\.)?filecrypt\.cc/Container/\w+'
-
- __description__ = """Filecrypt.cc decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zapp-brannigan", "")]
-
-
- # URL_REPLACEMENTS = [(r'.html$', ""), (r'$', ".html")] #@TODO: Extend SimpleCrypter
-
- DLC_LINK_PATTERN = r'<button class="dlcdownload" type="button" title="Download \*.dlc" onclick="DownloadDLC\(\'(.+)\'\);"><i></i><span>dlc<'
- WEBLINK_PATTERN = r"openLink.?'([\w_-]*)',"
-
- CAPTCHA_PATTERN = r'<img id="nc" src="(.+?)"'
-
- MIRROR_PAGE_PATTERN = r'"[\w]*" href="(http://filecrypt.cc/Container/\w+\.html\?mirror=\d+)">'
-
-
- def setup(self):
- self.links = []
-
-
- def decrypt(self, pyfile):
- self.html = self.load(pyfile.url, cookies=True)
-
- if "content not found" in self.html:
- self.offline()
-
- self.handlePasswordProtection()
- self.handleCaptcha()
- self.handleMirrorPages()
-
- for handle in (self.handleCNL, self.handleWeblinks, self.handleDlcContainer):
- handle()
- if self.links:
- self.packages = [(pyfile.package().name, self.links, pyfile.package().name)]
- return
-
-
- def handleMirrorPages(self):
- if "mirror=" not in self.siteWithLinks:
- return
-
- mirror = re.findall(self.MIRROR_PAGE_PATTERN, self.siteWithLinks)
-
- self.logInfo(_("Found %d mirrors") % len(mirror))
-
- for i in mirror[1:]:
- self.siteWithLinks = self.siteWithLinks + self.load(i, cookies=True).decode("utf-8", "replace")
-
-
- def handlePasswordProtection(self):
- if '<input type="text" name="password"' not in self.html:
- return
-
- self.logInfo(_("Folder is password protected"))
-
- if not self.pyfile.package().password:
- self.fail(_("Please enter the password in package section and try again"))
-
- self.html = self.load(self.pyfile.url, post={"password": self.password}, cookies=True)
-
-
- def handleCaptcha(self):
- m = re.search(self.CAPTCHA_PATTERN, self.html)
-
- if m:
- self.logDebug("Captcha-URL: %s" % m.group(1))
- captcha_code = self.decryptCaptcha("http://filecrypt.cc" + m.group(1), forceUser=True, imgtype="gif")
- self.siteWithLinks = self.load(self.pyfile.url, post={"recaptcha_response_field":captcha_code}, decode=True, cookies=True)
- else:
- self.logDebug("No captcha found")
- self.siteWithLinks = self.html
-
- if "recaptcha_response_field" in self.siteWithLinks:
- self.invalidCaptcha()
- self.retry()
-
-
- def handleDlcContainer(self):
- dlc = re.findall(self.DLC_LINK_PATTERN, self.siteWithLinks)
-
- if not dlc:
- return
-
- for i in dlc:
- self.links.append("http://filecrypt.cc/DLC/%s.dlc" % i)
-
-
- def handleWeblinks(self):
- try:
- weblinks = re.findall(self.WEBLINK_PATTERN, self.siteWithLinks)
-
- for link in weblinks:
- response = self.load("http://filecrypt.cc/Link/%s.html" % link, cookies=True)
- link2 = re.search('<iframe noresize src="(.*)"></iframe>', response)
- response2 = self.load(link2.group(1), just_header=True, cookies=True)
- self.links.append(response2['location'])
-
- except Exception, e:
- self.logDebug("Error decrypting weblinks: %s" % e)
-
-
- def handleCNL(self):
- try:
- vjk = re.findall('<input type="hidden" name="jk" value="function f\(\){ return \'(.*)\';}">', self.siteWithLinks)
- vcrypted = re.findall('<input type="hidden" name="crypted" value="(.*)">', self.siteWithLinks)
-
- for i in xrange(len(vcrypted)):
- self.links.extend(self._getLinks(vcrypted[i], vjk[i]))
-
- except Exception, e:
- self.logDebug("Error decrypting CNL: %s" % e)
-
-
- def _getLinks(self, crypted, jk):
- # Get key
- key = binascii.unhexlify(str(jk))
-
- # Decode crypted
- crypted = base64.standard_b64decode(crypted)
-
- # Decrypt
- Key = key
- IV = key
- obj = AES.new(Key, AES.MODE_CBC, IV)
- text = obj.decrypt(crypted)
-
- # Extract links
- links = filter(lambda x: x != "",
- text.replace("\x00", "").replace("\r", "").split("\n"))
-
- return links
diff --git a/module/plugins/crypter/FilefactoryComFolder.py b/module/plugins/crypter/FilefactoryComFolder.py
deleted file mode 100644
index 52c39c386..000000000
--- a/module/plugins/crypter/FilefactoryComFolder.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class FilefactoryComFolder(SimpleCrypter):
- __name__ = "FilefactoryComFolder"
- __type__ = "crypter"
- __version__ = "0.31"
-
- __pattern__ = r'https?://(?:www\.)?filefactory\.com/(?:f|folder)/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Filefactory.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- LINK_PATTERN = r'<td><a href="([^"]+)">'
- NAME_PATTERN = r'<h1>Files in <span>(?P<N>.+)</span></h1>'
- PAGES_PATTERN = r'data-paginator-totalPages="(\d+)"'
-
- COOKIES = [("filefactory.com", "locale", "en_US.utf8")]
-
-
- def loadPage(self, page_n):
- return self.load(self.pyfile.url, get={'page': page_n})
diff --git a/module/plugins/crypter/FilerNetFolder.py b/module/plugins/crypter/FilerNetFolder.py
deleted file mode 100644
index a2daba136..000000000
--- a/module/plugins/crypter/FilerNetFolder.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import re
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class FilerNetFolder(SimpleCrypter):
- __name__ = "FilerNetFolder"
- __type__ = "crypter"
- __version__ = "0.41"
-
- __pattern__ = r'https?://filer\.net/folder/\w{16}'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Filer.net decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("nath_schwarz", "nathan.notwhite@gmail.com"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- LINK_PATTERN = r'href="(/get/\w{16})">(?!<)'
- NAME_PATTERN = r'<h3>(?P<N>.+?) - <small'
-
-
- def getLinks(self):
- return ['http://filer.net%s' % link for link in re.findall(self.LINK_PATTERN, self.html)]
diff --git a/module/plugins/crypter/FileserveComFolder.py b/module/plugins/crypter/FileserveComFolder.py
deleted file mode 100644
index e6b35fd36..000000000
--- a/module/plugins/crypter/FileserveComFolder.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Crypter import Crypter
-
-
-class FileserveComFolder(Crypter):
- __name__ = "FileserveComFolder"
- __type__ = "crypter"
- __version__ = "0.11"
-
- __pattern__ = r'http://(?:www\.)?fileserve\.com/list/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """FileServe.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("fionnc", "fionnc@gmail.com")]
-
-
- FOLDER_PATTERN = r'<table class="file_list">(.*?)</table>'
- LINK_PATTERN = r'<a href="([^"]+)" class="sheet_icon wbold">'
-
-
- def decrypt(self, pyfile):
- html = self.load(pyfile.url)
-
- new_links = []
-
- folder = re.search(self.FOLDER_PATTERN, html, re.S)
- if folder is None:
- self.error(_("FOLDER_PATTERN not found"))
-
- new_links.extend(re.findall(self.LINK_PATTERN, folder.group(1)))
-
- if new_links:
- self.urls = [map(lambda s: "http://fileserve.com%s" % s, new_links)]
diff --git a/module/plugins/crypter/FilesonicComFolder.py b/module/plugins/crypter/FilesonicComFolder.py
deleted file mode 100644
index d58516986..000000000
--- a/module/plugins/crypter/FilesonicComFolder.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class FilesonicComFolder(DeadCrypter):
- __name__ = "FilesonicComFolder"
- __type__ = "crypter"
- __version__ = "0.12"
-
- __pattern__ = r'http://(?:www\.)?filesonic\.com/folder/\w+'
-
- __description__ = """Filesonic.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(FilesonicComFolder)
diff --git a/module/plugins/crypter/FilestubeCom.py b/module/plugins/crypter/FilestubeCom.py
deleted file mode 100644
index 29de108e5..000000000
--- a/module/plugins/crypter/FilestubeCom.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class FilestubeCom(SimpleCrypter):
- __name__ = "FilestubeCom"
- __type__ = "crypter"
- __version__ = "0.05"
-
- __pattern__ = r'http://(?:www\.)?filestube\.(?:com|to)/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Filestube.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- LINK_PATTERN = r'<a class=\"file-link-main(?: noref)?\" [^>]* href=\"(http://[^\"]+)'
- NAME_PATTERN = r'<h1\s*> (?P<N>.+) download\s*</h1>'
diff --git a/module/plugins/crypter/FiletramCom.py b/module/plugins/crypter/FiletramCom.py
deleted file mode 100644
index e4c8b6360..000000000
--- a/module/plugins/crypter/FiletramCom.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class FiletramCom(SimpleCrypter):
- __name__ = "FiletramCom"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?filetram\.com/[^/]+/.+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Filetram.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("igel", "igelkun@myopera.com"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- LINK_PATTERN = r'\s+(http://.+)'
- NAME_PATTERN = r'<title>(?P<N>.+?) - Free Download'
diff --git a/module/plugins/crypter/FiredriveComFolder.py b/module/plugins/crypter/FiredriveComFolder.py
deleted file mode 100644
index 7d3a357fd..000000000
--- a/module/plugins/crypter/FiredriveComFolder.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class FiredriveComFolder(DeadCrypter):
- __name__ = "FiredriveComFolder"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'https?://(?:www\.)?(firedrive|putlocker)\.com/share/.+'
- __config__ = []
-
- __description__ = """Firedrive.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
-getInfo = create_getInfo(FiredriveComFolder)
diff --git a/module/plugins/crypter/FourChanOrg.py b/module/plugins/crypter/FourChanOrg.py
deleted file mode 100644
index 48592d30a..000000000
--- a/module/plugins/crypter/FourChanOrg.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Based on 4chandl by Roland Beermann (https://gist.github.com/enkore/3492599)
-
-import re
-
-from module.plugins.Crypter import Crypter
-
-
-class FourChanOrg(Crypter):
- __name__ = "FourChanOrg"
- __type__ = "crypter"
- __version__ = "0.3"
-
- __pattern__ = r'http://(?:www\.)?boards\.4chan\.org/\w+/res/(\d+)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """4chan.org folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = []
-
-
- def decrypt(self, pyfile):
- pagehtml = self.load(pyfile.url)
- images = set(re.findall(r'(images\.4chan\.org/[^/]*/src/[^"<]*)', pagehtml))
- self.urls = ["http://" + image for image in images]
diff --git a/module/plugins/crypter/FreakhareComFolder.py b/module/plugins/crypter/FreakhareComFolder.py
deleted file mode 100644
index d4d3c6d3c..000000000
--- a/module/plugins/crypter/FreakhareComFolder.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class FreakhareComFolder(SimpleCrypter):
- __name__ = "FreakhareComFolder"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?freakshare\.com/folder/.+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Freakhare.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- LINK_PATTERN = r'<a href="(http://freakshare\.com/files/[^"]+)" target="_blank">'
- NAME_PATTERN = r'Folder:</b> (?P<N>.+)'
- PAGES_PATTERN = r'Pages: +(\d+)'
-
-
- def loadPage(self, page_n):
- if not hasattr(self, 'f_id') and not hasattr(self, 'f_md5'):
- m = re.search(r'http://freakshare.com/\?x=folder&f_id=(\d+)&f_md5=(\w+)', self.html)
- if m:
- self.f_id = m.group(1)
- self.f_md5 = m.group(2)
- return self.load('http://freakshare.com/', get={'x': 'folder',
- 'f_id': self.f_id,
- 'f_md5': self.f_md5,
- 'entrys': '20',
- 'page': page_n - 1,
- 'order': ''}, decode=True)
diff --git a/module/plugins/crypter/FreetexthostCom.py b/module/plugins/crypter/FreetexthostCom.py
deleted file mode 100644
index 36bc61d35..000000000
--- a/module/plugins/crypter/FreetexthostCom.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class FreetexthostCom(SimpleCrypter):
- __name__ = "FreetexthostCom"
- __type__ = "crypter"
- __version__ = "0.01"
-
- __pattern__ = r'http://(?:www\.)?freetexthost\.com/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Freetexthost.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- def getLinks(self):
- m = re.search(r'<div id="contentsinner">\s*(.+)<div class="viewcount">', self.html, re.S)
- if m is None:
- self.error(_("Unable to extract links"))
- links = m.group(1)
- return links.strip().split("<br />\r\n")
diff --git a/module/plugins/crypter/FshareVnFolder.py b/module/plugins/crypter/FshareVnFolder.py
deleted file mode 100644
index e6e67ea13..000000000
--- a/module/plugins/crypter/FshareVnFolder.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class FshareVnFolder(SimpleCrypter):
- __name__ = "FshareVnFolder"
- __type__ = "crypter"
- __version__ = "0.01"
-
- __pattern__ = r'http://(?:www\.)?fshare\.vn/folder/.*'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Fshare.vn folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- LINK_PATTERN = r'<li class="w_80pc"><a href="([^"]+)" target="_blank">'
diff --git a/module/plugins/crypter/GooGl.py b/module/plugins/crypter/GooGl.py
deleted file mode 100644
index d548a3375..000000000
--- a/module/plugins/crypter/GooGl.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Crypter import Crypter
-from module.common.json_layer import json_loads
-
-
-class GooGl(Crypter):
- __name__ = "GooGl"
- __type__ = "crypter"
- __version__ = "0.01"
-
- __pattern__ = r'https?://(?:www\.)?goo\.gl/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Goo.gl decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- API_URL = "https://www.googleapis.com/urlshortener/v1/url"
-
-
- def decrypt(self, pyfile):
- rep = self.load(self.API_URL, get={'shortUrl': pyfile.url})
- self.logDebug("JSON data: " + rep)
- rep = json_loads(rep)
-
- if 'longUrl' in rep:
- self.urls = [rep['longUrl']]
- else:
- self.fail(_("Unable to expand shortened link"))
diff --git a/module/plugins/crypter/HoerbuchIn.py b/module/plugins/crypter/HoerbuchIn.py
deleted file mode 100644
index 4d4b8fbef..000000000
--- a/module/plugins/crypter/HoerbuchIn.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from BeautifulSoup import BeautifulSoup, BeautifulStoneSoup
-
-from module.plugins.Crypter import Crypter
-
-
-class HoerbuchIn(Crypter):
- __name__ = "HoerbuchIn"
- __type__ = "crypter"
- __version__ = "0.6"
-
- __pattern__ = r'http://(?:www\.)?hoerbuch\.in/(wp/horbucher/\d+/.+/|tp/out\.php\?.+|protection/folder_\d+\.html)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Hoerbuch.in decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("spoob", "spoob@pyload.org"),
- ("mkaay", "mkaay@mkaay.de")]
-
-
- article = re.compile("http://(?:www\.)?hoerbuch\.in/wp/horbucher/\d+/.+/")
- protection = re.compile("http://(?:www\.)?hoerbuch\.in/protection/folder_\d+.html")
-
-
- def decrypt(self, pyfile):
- self.pyfile = pyfile
-
- if self.article.match(pyfile.url):
- html = self.load(pyfile.url)
- soup = BeautifulSoup(html, convertEntities=BeautifulStoneSoup.HTML_ENTITIES)
-
- abookname = soup.find("a", attrs={"rel": "bookmark"}).text
- for a in soup.findAll("a", attrs={"href": self.protection}):
- package = "%s (%s)" % (abookname, a.previousSibling.previousSibling.text[:-1])
- links = self.decryptFolder(a['href'])
-
- self.packages.append((package, links, package))
- else:
- self.urls = self.decryptFolder(pyfile.url)
-
-
- def decryptFolder(self, url):
- m = self.protection.search(url)
- if m is None:
- self.fail(_("Bad URL"))
- url = m.group(0)
-
- self.pyfile.url = url
- html = self.load(url, post={"viewed": "adpg"})
-
- links = []
- pattern = re.compile("http://www\.hoerbuch\.in/protection/(\w+)/(.*?)\"")
- for hoster, lid in pattern.findall(html):
- self.req.lastURL = url
- self.load("http://www.hoerbuch.in/protection/%s/%s" % (hoster, lid))
- links.append(self.req.lastEffectiveURL)
-
- return links
diff --git a/module/plugins/crypter/HotfileComFolder.py b/module/plugins/crypter/HotfileComFolder.py
deleted file mode 100644
index a294c04e0..000000000
--- a/module/plugins/crypter/HotfileComFolder.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class HotfileComFolder(DeadCrypter):
- __name__ = "HotfileComFolder"
- __type__ = "crypter"
- __version__ = "0.3"
-
- __pattern__ = r'https?://(?:www\.)?hotfile\.com/list/\w+/\w+'
- __config__ = []
-
- __description__ = """Hotfile.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org")]
-
-
-getInfo = create_getInfo(HotfileComFolder)
diff --git a/module/plugins/crypter/ILoadTo.py b/module/plugins/crypter/ILoadTo.py
deleted file mode 100644
index f3415706d..000000000
--- a/module/plugins/crypter/ILoadTo.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class ILoadTo(DeadCrypter):
- __name__ = "ILoadTo"
- __type__ = "crypter"
- __version__ = "0.11"
-
- __pattern__ = r'http://(?:www\.)?iload\.to/go/\d+-[\w.-]+/'
- __config__ = []
-
- __description__ = """Iload.to decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("hzpz", None)]
-
-
-getInfo = create_getInfo(ILoadTo)
diff --git a/module/plugins/crypter/ImgurComAlbum.py b/module/plugins/crypter/ImgurComAlbum.py
deleted file mode 100644
index f8f226a1b..000000000
--- a/module/plugins/crypter/ImgurComAlbum.py
+++ /dev/null
@@ -1,27 +0,0 @@
-import re
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-from module.utils import uniqify
-
-
-class ImgurComAlbum(SimpleCrypter):
- __name__ = "ImgurComAlbum"
- __type__ = "crypter"
- __version__ = "0.51"
-
- __pattern__ = r'https?://(?:www\.|m\.)?imgur\.com/(a|gallery|)/?\w{5,7}'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Imgur.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("nath_schwarz", "nathan.notwhite@gmail.com")]
-
-
- NAME_PATTERN = r'(?P<N>.+?) - Imgur'
- LINK_PATTERN = r'i\.imgur\.com/\w{7}s?\.(?:jpeg|jpg|png|gif|apng)'
-
-
- def getLinks(self):
- f = lambda url: "http://" + re.sub(r'(\w{7})s\.', r'\1.', url)
- return uniqify(map(f, re.findall(self.LINK_PATTERN, self.html)))
diff --git a/module/plugins/crypter/JunocloudMeFolder.py b/module/plugins/crypter/JunocloudMeFolder.py
deleted file mode 100644
index 509f1f7ec..000000000
--- a/module/plugins/crypter/JunocloudMeFolder.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSCrypter import XFSCrypter
-
-
-class JunocloudMeFolder(XFSCrypter):
- __name__ = "JunocloudMeFolder"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?junocloud\.me/folders/(?P<ID>\d+/\w+)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Junocloud.me folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("guidobelix", "guidobelix@hotmail.it")]
-
-
- HOSTER_DOMAIN = "junocloud.me"
diff --git a/module/plugins/crypter/LetitbitNetFolder.py b/module/plugins/crypter/LetitbitNetFolder.py
deleted file mode 100644
index e3848e2ab..000000000
--- a/module/plugins/crypter/LetitbitNetFolder.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from module.plugins.Crypter import Crypter
-
-
-class LetitbitNetFolder(Crypter):
- __name__ = "LetitbitNetFolder"
- __type__ = "crypter"
- __version__ = "0.1"
-
- __pattern__ = r'http://(?:www\.)?letitbit\.net/folder/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Letitbit.net folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("DHMH", "webmaster@pcProfil.de"),
- ("z00nx", "z00nx0@gmail.com")]
-
-
- FOLDER_PATTERN = r'<table>(.*)</table>'
- LINK_PATTERN = r'<a href="([^"]+)" target="_blank">'
-
-
- def decrypt(self, pyfile):
- html = self.load(pyfile.url)
-
- folder = re.search(self.FOLDER_PATTERN, html, re.S)
- if folder is None:
- self.error(_("FOLDER_PATTERN not found"))
-
- self.urls.extend(re.findall(self.LINK_PATTERN, folder.group(0)))
diff --git a/module/plugins/crypter/LinkCryptWs.py b/module/plugins/crypter/LinkCryptWs.py
deleted file mode 100644
index 835a549b2..000000000
--- a/module/plugins/crypter/LinkCryptWs.py
+++ /dev/null
@@ -1,327 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import base64
-import binascii
-import re
-
-import pycurl
-
-from Crypto.Cipher import AES
-
-from module.plugins.Crypter import Crypter
-from module.utils import html_unescape
-
-
-class LinkCryptWs(Crypter):
- __name__ = "LinkCryptWs"
- __type__ = "crypter"
- __version__ = "0.07"
-
- __pattern__ = r'http://(?:www\.)?linkcrypt\.ws/(dir|container)/(?P<ID>\w+)'
-
- __description__ = """LinkCrypt.ws decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("kagenoshin", "kagenoshin[AT]gmx[DOT]ch"),
- ("glukgluk", None),
- ("Gummibaer", None)]
-
-
- CRYPTED_KEY = "crypted"
- JK_KEY = "jk"
-
-
- def setup(self):
- self.captcha = False
- self.links = []
- self.sources = ['cnl', 'web', 'dlc', 'rsdf', 'ccf']
-
-
- def prepare(self):
- # Init
- self.fileid = re.match(self.__pattern__, self.pyfile.url).group('ID')
-
- self.req.cj.setCookie("linkcrypt.ws", "language", "en")
-
- # Request package
- self.req.http.c.setopt(pycurl.USERAGENT, "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko") #: better chance to not get those key-captchas
- self.html = self.load(self.pyfile.url)
-
-
- def decrypt(self, pyfile):
- if not self.js:
- self.fail(_("Missing JS Engine"))
-
- self.prepare()
-
- if not self.isOnline():
- self.offline()
-
- if self.isKeyCaptchaProtected():
- self.retry(4, 30, _("Can't handle Key-Captcha"))
-
- if self.isCaptchaProtected():
- self.captcha = True
- self.unlockCaptchaProtection()
- self.handleCaptchaErrors()
-
- # Check for protection
- if self.isPasswordProtected():
- self.unlockPasswordProtection()
- self.handleErrors()
-
- # get unrar password
- self.getunrarpw()
-
- # Get package name and folder
- package_name, folder_name = self.getPackageInfo()
-
- #get the container definitions from script section
- self.get_container_html()
-
- # Extract package links
- for type in self.sources:
- links = self.handleLinkSource(type)
-
- if links:
- self.links.extend(links)
- break
-
- if self.links:
- self.packages = [(package_name, self.links, folder_name)]
-
-
- def isOnline(self):
- if "<title>Linkcrypt.ws // Error 404</title>" in self.html:
- self.logDebug("folder doesen't exist anymore")
- return False
- else:
- return True
-
-
- def isPasswordProtected(self):
- if "Authorizing" in self.html:
- self.logDebug("Links are password protected")
- return True
- else:
- return False
-
-
- def isCaptchaProtected(self):
- if 'id="captcha">' in self.html:
- self.logDebug("Links are captcha protected")
- return True
- else:
- return False
-
-
- def isKeyCaptchaProtected(self):
- if re.search(r'Key[ -]', self.html, re.I):
- return True
- else:
- return False
-
-
- def unlockPasswordProtection(self):
- password = self.getPassword()
-
- if password:
- self.logDebug("Submitting password [%s] for protected links" % password)
- self.html = self.load(self.pyfile.url, post={"password": password, 'x': "0", 'y': "0"})
- else:
- self.fail(_("Folder is password protected"))
-
-
- def unlockCaptchaProtection(self):
- captcha_url = re.search(r'<form.*?id\s*?=\s*?"captcha"[^>]*?>.*?<\s*?input.*?src="([^"]*?)"', self.html, re.I | re.S).group(1)
- captcha_code = self.decryptCaptcha(captcha_url, forceUser=True, imgtype="gif", result_type='positional')
-
- self.html = self.load(self.pyfile.url, post={"x": captcha_code[0], "y": captcha_code[1]})
-
-
- def getPackageInfo(self):
- name = self.pyfile.package().name
- folder = self.pyfile.package().folder
-
- self.logDebug("Defaulting to pyfile name [%s] and folder [%s] for package" % (name, folder))
-
- return name, folder
-
-
- def getunrarpw(self):
- sitein = self.html
- indexi = sitein.find("|source|") + 8
- indexe = sitein.find("|",indexi)
-
- unrarpw = sitein[indexi:indexe]
-
- if not (unrarpw == "Password" or "Dateipasswort") :
- self.logDebug("File password set to: [%s]"% unrarpw)
- self.pyfile.package().password = unrarpw
-
-
- def handleErrors(self):
- if self.isPasswordProtected():
- self.fail(_("Incorrect password"))
-
-
- def handleCaptchaErrors(self):
- if self.captcha:
- if "Your choice was wrong!" in self.html:
- self.invalidCaptcha()
- self.retry()
- else:
- self.correctCaptcha()
-
-
- def handleLinkSource(self, type):
- if type == 'cnl':
- return self.handleCNL2()
-
- elif type == 'web':
- return self.handleWebLinks()
-
- elif type in ('rsdf', 'ccf', 'dlc'):
- return self.handleContainer(type)
-
- else:
- self.fail(_("Unknown source type: %s") % type) #@TODO: Replace with self.error in 0.4.10
-
-
- def handleWebLinks(self):
- self.logDebug("Search for Web links ")
-
- package_links = []
- pattern = r'<form action="http://linkcrypt.ws/out.html"[^>]*?>.*?<input[^>]*?value="([^"]*?)"[^>]*?name="file"'
- ids = re.findall(pattern, self.html, re.I | re.S)
-
- self.logDebug("Decrypting %d Web links" % len(ids))
-
- for idx, weblink_id in enumerate(ids):
- try:
- self.logDebug("Decrypting Web link %d, %s" % (idx + 1, weblink_id))
-
- res = self.load("http://linkcrypt.ws/out.html", post = {'file':weblink_id})
-
- indexs = res.find("window.location =") + 19
- indexe = res.find('"', indexs)
-
- link2 = res[indexs:indexe]
-
- self.logDebug(link2)
-
- link2 = html_unescape(link2)
- package_links.append(link2)
-
- except Exception, detail:
- self.logDebug("Error decrypting Web link %s, %s" % (weblink_id, detail))
-
- return package_links
-
-
- def get_container_html(self):
- self.container_html = []
-
- script = re.search(r'<div.*?id="ad_cont".*?<script.*?javascrip[^>]*?>(.*?)</script', self.html, re.I | re.S)
-
- if script:
- container_html_text = script.group(1)
- container_html_text.strip()
- self.container_html = container_html_text.splitlines()
-
-
- def handle_javascript(self, line):
- return self.js.eval(line.replace('{}))',"{}).replace('document.open();document.write','').replace(';document.close();',''))"))
-
-
- def handleContainer(self, type):
- package_links = []
- type = type.lower()
-
- self.logDebug('Search for %s Container links' % type.upper())
-
- if not type.isalnum(): # check to prevent broken re-pattern (cnl2,rsdf,ccf,dlc,web are all alpha-numeric)
- self.fail(_("Unknown container type: %s") % type) #@TODO: Replace with self.error in 0.4.10
-
- for line in self.container_html:
- if type in line:
- jseval = self.handle_javascript(line)
- clink = re.search(r'href=["\']([^"\']*?)["\']',jseval,re.I)
-
- if not clink:
- continue
-
- self.logDebug("clink avaible")
-
- package_name, folder_name = self.getPackageInfo()
- self.logDebug("Added package with name %s.%s and container link %s" %( package_name, type, clink.group(1)))
- self.core.api.uploadContainer( "%s.%s" %(package_name, type), self.load(clink.group(1)))
- return "Found it"
-
- return package_links
-
-
- def handleCNL2(self):
- self.logDebug("Search for CNL links")
-
- package_links = []
- cnl_line = None
-
- for line in self.container_html:
- if "cnl" in line:
- cnl_line = line
- break
-
- if cnl_line:
- self.logDebug("cnl_line gefunden")
-
- try:
- cnl_section = self.handle_javascript(cnl_line)
- (vcrypted, vjk) = self._getCipherParams(cnl_section)
- for (crypted, jk) in zip(vcrypted, vjk):
- package_links.extend(self._getLinks(crypted, jk))
- except:
- self.logError(_("Unable to decrypt CNL links (JS Error) try to get over links"))
- return self.handleWebLinks()
-
- return package_links
-
-
- def _getCipherParams(self, cnl_section):
- # Get jk
- jk_re = r'<INPUT.*?NAME="%s".*?VALUE="(.*?)"' % LinkCryptWs.JK_KEY
- vjk = re.findall(jk_re, cnl_section)
-
- # Get crypted
- crypted_re = r'<INPUT.*?NAME="%s".*?VALUE="(.*?)"' % LinkCryptWs.CRYPTED_KEY
- vcrypted = re.findall(crypted_re, cnl_section)
-
- # Log and return
- self.logDebug("Detected %d crypted blocks" % len(vcrypted))
- return vcrypted, vjk
-
-
- def _getLinks(self, crypted, jk):
- # Get key
- jreturn = self.js.eval("%s f()" % jk)
- key = binascii.unhexlify(jreturn)
-
- self.logDebug("JsEngine returns value [%s]" % jreturn)
-
- # Decode crypted
- crypted = base64.standard_b64decode(crypted)
-
- # Decrypt
- Key = key
- IV = key
- obj = AES.new(Key, AES.MODE_CBC, IV)
- text = obj.decrypt(crypted)
-
- # Extract links
- text = text.replace("\x00", "").replace("\r", "")
- links = text.split("\n")
- links = filter(lambda x: x != "", links)
-
- # Log and return
- self.logDebug("Package has %d links" % len(links))
-
- return links
diff --git a/module/plugins/crypter/LinkSaveIn.py b/module/plugins/crypter/LinkSaveIn.py
deleted file mode 100644
index 46babd156..000000000
--- a/module/plugins/crypter/LinkSaveIn.py
+++ /dev/null
@@ -1,246 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# * cnl2 and web links are skipped if JS is not available (instead of failing the package)
-# * only best available link source is used (priority: cnl2>rsdf>ccf>dlc>web
-
-import base64
-import binascii
-import re
-
-from Crypto.Cipher import AES
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-from module.unescape import unescape
-
-
-class LinkSaveIn(SimpleCrypter):
- __name__ = "LinkSaveIn"
- __type__ = "crypter"
- __version__ = "2.02"
-
- __pattern__ = r'http://(?:www\.)?linksave\.in/(?P<id>\w+)$'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """LinkSave.in decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("fragonib", "fragonib[AT]yahoo[DOT]es")]
-
-
- COOKIES = [("linksave.in", "Linksave_Language", "english")]
-
- # Constants
- _JK_KEY_ = "jk"
- _CRYPTED_KEY_ = "crypted"
-
-
- def setup(self):
- self.fileid = None
- self.captcha = False
- self.package = None
- self.preferred_sources = ["cnl2", "rsdf", "ccf", "dlc", "web"]
-
-
- def decrypt(self, pyfile):
- # Init
- self.package = pyfile.package()
- self.fileid = re.match(self.__pattern__, pyfile.url).group('id')
-
- # Request package
- self.html = self.load(pyfile.url)
- if not self.isOnline():
- self.offline()
-
- # Check for protection
- if self.isPasswordProtected():
- self.unlockPasswordProtection()
- self.handleErrors()
-
- if self.isCaptchaProtected():
- self.captcha = True
- self.unlockCaptchaProtection()
- self.handleErrors()
-
- # Get package name and folder
- (package_name, folder_name) = self.getPackageInfo()
-
- # Extract package links
- package_links = []
- for type_ in self.preferred_sources:
- package_links.extend(self.handleLinkSource(type_))
- if package_links: # use only first source which provides links
- break
- package_links = set(package_links)
-
- # Pack
- if package_links:
- self.packages = [(package_name, package_links, folder_name)]
-
-
- def isOnline(self):
- if "<big>Error 404 - Folder not found!</big>" in self.html:
- self.logDebug("File not found")
- return False
- return True
-
-
- def isPasswordProtected(self):
- if re.search(r'''<input.*?type="password"''', self.html):
- self.logDebug("Links are password protected")
- return True
-
-
- def isCaptchaProtected(self):
- if "<b>Captcha:</b>" in self.html:
- self.logDebug("Links are captcha protected")
- return True
- return False
-
-
- def unlockPasswordProtection(self):
- password = self.getPassword()
- self.logDebug("Submitting password [%s] for protected links" % password)
- post = {"id": self.fileid, "besucherpasswort": password, 'login': 'submit'}
- self.html = self.load(self.pyfile.url, post=post)
-
-
- def unlockCaptchaProtection(self):
- captcha_hash = re.search(r'name="hash" value="([^"]+)', self.html).group(1)
- captcha_url = re.search(r'src=".(/captcha/cap.php\?hsh=[^"]+)', self.html).group(1)
- captcha_code = self.decryptCaptcha("http://linksave.in" + captcha_url, forceUser=True)
- self.html = self.load(self.pyfile.url, post={"id": self.fileid, "hash": captcha_hash, "code": captcha_code})
-
-
- def getPackageInfo(self):
- name = self.pyfile.package().name
- folder = self.pyfile.package().folder
- self.logDebug("Defaulting to pyfile name [%s] and folder [%s] for package" % (name, folder))
- return name, folder
-
-
- def handleErrors(self):
- if "The visitorpassword you have entered is wrong" in self.html:
- self.logDebug("Incorrect password, please set right password on 'Edit package' form and retry")
- self.fail(_("Incorrect password, please set right password on 'Edit package' form and retry"))
-
- if self.captcha:
- if "Wrong code. Please retry" in self.html:
- self.invalidCaptcha()
- self.retry()
- else:
- self.correctCaptcha()
-
-
- def handleLinkSource(self, type_):
- if type_ == "cnl2":
- return self.handleCNL2()
- elif type_ in ("rsdf", "ccf", "dlc"):
- return self.handleContainer(type_)
- elif type_ == "web":
- return self.handleWebLinks()
- else:
- self.error('Unknown source type "%s" (this is probably a bug)' % type_)
-
-
- def handleWebLinks(self):
- package_links = []
- self.logDebug("Search for Web links")
- if not self.js:
- self.logDebug("No JS -> skip Web links")
- else:
- #@TODO: Gather paginated web links
- pattern = r'<a href="http://linksave\.in/(\w{43})"'
- ids = re.findall(pattern, self.html)
- self.logDebug("Decrypting %d Web links" % len(ids))
- for i, weblink_id in enumerate(ids):
- try:
- webLink = "http://linksave.in/%s" % weblink_id
-
- self.logDebug("Decrypting Web link %d, %s" % (i + 1, webLink))
-
- fwLink = "http://linksave.in/fw-%s" % weblink_id
- res = self.load(fwLink)
-
- jscode = re.findall(r'<script type="text/javascript">(.*)</script>', res)[-1]
- jseval = self.js.eval("document = { write: function(e) { return e; } }; %s" % jscode)
- dlLink = re.search(r'http://linksave\.in/dl-\w+', jseval).group(0)
- self.logDebug("JsEngine returns value [%s] for redirection link" % dlLink)
-
- res = self.load(dlLink)
- link = unescape(re.search(r'<iframe src="(.+?)"', res).group(1))
-
- package_links.append(link)
-
- except Exception, detail:
- self.logDebug("Error decrypting Web link %s, %s" % (webLink, detail))
-
- return package_links
-
-
- def handleContainer(self, type_):
- package_links = []
- type_ = type_.lower()
- self.logDebug("Seach for %s Container links" % type_.upper())
- if not type_.isalnum(): # check to prevent broken re-pattern (cnl2,rsdf,ccf,dlc,web are all alpha-numeric)
- self.error('Unknown container type "%s" (this is probably a bug)' % type_)
- pattern = r'\(\'%s_link\'\).href=unescape\(\'(.*?\.%s)\'\)' % (type_, type_)
- containersLinks = re.findall(pattern, self.html)
- self.logDebug("Found %d %s Container links" % (len(containersLinks), type_.upper()))
- for containerLink in containersLinks:
- link = "http://linksave.in/%s" % unescape(containerLink)
- package_links.append(link)
- return package_links
-
-
- def handleCNL2(self):
- package_links = []
- self.logDebug("Search for CNL2 links")
- if not self.js:
- self.logDebug("No JS -> skip CNL2 links")
- elif 'cnl2_load' in self.html:
- try:
- (vcrypted, vjk) = self._getCipherParams()
- for (crypted, jk) in zip(vcrypted, vjk):
- package_links.extend(self._getLinks(crypted, jk))
- except:
- self.fail(_("Unable to decrypt CNL2 links"))
- return package_links
-
-
- def _getCipherParams(self):
- # Get jk
- jk_re = r'<INPUT.*?NAME="%s".*?VALUE="(.*?)"' % LinkSaveIn._JK_KEY_
- vjk = re.findall(jk_re, self.html)
-
- # Get crypted
- crypted_re = r'<INPUT.*?NAME="%s".*?VALUE="(.*?)"' % LinkSaveIn._CRYPTED_KEY_
- vcrypted = re.findall(crypted_re, self.html)
-
- # Log and return
- self.logDebug("Detected %d crypted blocks" % len(vcrypted))
- return vcrypted, vjk
-
-
- def _getLinks(self, crypted, jk):
- # Get key
- jreturn = self.js.eval("%s f()" % jk)
- self.logDebug("JsEngine returns value [%s]" % jreturn)
- key = binascii.unhexlify(jreturn)
-
- # Decode crypted
- crypted = base64.standard_b64decode(crypted)
-
- # Decrypt
- Key = key
- IV = key
- obj = AES.new(Key, AES.MODE_CBC, IV)
- text = obj.decrypt(crypted)
-
- # Extract links
- text = text.replace("\x00", "").replace("\r", "")
- links = text.split("\n")
- links = filter(lambda x: x != "", links)
-
- # Log and return
- self.logDebug("Package has %d links" % len(links))
- return links
diff --git a/module/plugins/crypter/LinkdecrypterCom.py b/module/plugins/crypter/LinkdecrypterCom.py
deleted file mode 100644
index 91318eadf..000000000
--- a/module/plugins/crypter/LinkdecrypterCom.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from module.plugins.Crypter import Crypter
-
-
-class LinkdecrypterCom(Crypter):
- __name__ = "LinkdecrypterCom"
- __type__ = "crypter"
- __version__ = "0.27"
-
- __pattern__ = r'^unmatchable$'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Linkdecrypter.com"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("flowlee", None)]
-
-
- TEXTAREA_PATTERN = r'<textarea name="links" wrap="off" readonly="1" class="caja_des">(.+)</textarea>'
- PASSWORD_PATTERN = r'<input type="text" name="password"'
- CAPTCHA_PATTERN = r'<img class="captcha" src="(.+?)"(.*?)>'
- REDIR_PATTERN = r'<i>(Click <a href="./">here</a> if your browser does not redirect you).</i>'
-
-
- def decrypt(self, pyfile):
- self.passwords = self.getPassword().splitlines()
-
- # API not working anymore
- self.urls = self.decryptHTML()
-
-
- def decryptAPI(self):
- get_dict = {"t": "link", "url": self.pyfile.url, "lcache": "1"}
- self.html = self.load('http://linkdecrypter.com/api', get=get_dict)
- if self.html.startswith('http://'):
- return self.html.splitlines()
-
- if self.html == 'INTERRUPTION(PASSWORD)':
- for get_dict['pass'] in self.passwords:
- self.html = self.load('http://linkdecrypter.com/api', get=get_dict)
- if self.html.startswith('http://'):
- return self.html.splitlines()
-
- self.logError("API", self.html)
- if self.html == 'INTERRUPTION(PASSWORD)':
- self.fail(_("No or incorrect password"))
-
- return None
-
-
- def decryptHTML(self):
- retries = 5
-
- post_dict = {"link_cache": "on", "pro_links": self.pyfile.url, "modo_links": "text"}
- self.html = self.load('http://linkdecrypter.com/', post=post_dict, cookies=True, decode=True)
-
- while self.passwords or retries:
- m = re.search(self.TEXTAREA_PATTERN, self.html, flags=re.S)
- if m:
- return [x for x in m.group(1).splitlines() if '[LINK-ERROR]' not in x]
-
- m = re.search(self.CAPTCHA_PATTERN, self.html)
- if m:
- captcha_url = 'http://linkdecrypter.com/' + m.group(1)
- result_type = "positional" if "getPos" in m.group(2) else "textual"
-
- m = re.search(r"<p><i><b>([^<]+)</b></i></p>", self.html)
- msg = m.group(1) if m else ""
- self.logInfo(_("Captcha protected link"), result_type, msg)
-
- captcha = self.decryptCaptcha(captcha_url, result_type=result_type)
- if result_type == "positional":
- captcha = "%d|%d" % captcha
- self.html = self.load('http://linkdecrypter.com/', post={"captcha": captcha}, decode=True)
- retries -= 1
-
- elif self.PASSWORD_PATTERN in self.html:
- if self.passwords:
- password = self.passwords.pop(0)
- self.logInfo(_("Password protected link, trying ") + password)
- self.html = self.load('http://linkdecrypter.com/', post={'password': password}, decode=True)
- else:
- self.fail(_("No or incorrect password"))
-
- else:
- retries -= 1
- self.html = self.load('http://linkdecrypter.com/', cookies=True, decode=True)
-
- return None
diff --git a/module/plugins/crypter/LixIn.py b/module/plugins/crypter/LixIn.py
deleted file mode 100644
index 50ad217d4..000000000
--- a/module/plugins/crypter/LixIn.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Crypter import Crypter
-
-
-class LixIn(Crypter):
- __name__ = "LixIn"
- __type__ = "crypter"
- __version__ = "0.22"
-
- __pattern__ = r'http://(?:www\.)?lix\.in/(?P<ID>.+)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Lix.in decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("spoob", "spoob@pyload.org")]
-
-
- CAPTCHA_PATTERN = r'<img src="(?P<image>captcha_img\.php\?.*?)"'
- SUBMIT_PATTERN = r'value=\'continue.*?\''
- LINK_PATTERN = r'name="ifram" src="(?P<link>.*?)"'
-
-
- def decrypt(self, pyfile):
- url = pyfile.url
-
- m = re.match(self.__pattern__, url)
- if m is None:
- self.error(_("Unable to identify file ID"))
-
- id = m.group("ID")
- self.logDebug("File id is %s" % id)
-
- self.html = self.load(url, decode=True)
-
- m = re.search(self.SUBMIT_PATTERN, self.html)
- if m is None:
- self.error(_("Link doesn't seem valid"))
-
- m = re.search(self.CAPTCHA_PATTERN, self.html)
- if m:
- for _i in xrange(5):
- m = re.search(self.CAPTCHA_PATTERN, self.html)
- if m:
- self.logDebug("Trying captcha")
- captcharesult = self.decryptCaptcha("http://lix.in/" + m.group("image"))
- self.html = self.load(url, decode=True,
- post={"capt": captcharesult, "submit": "submit", "tiny": id})
- else:
- self.logDebug("No captcha/captcha solved")
- else:
- self.html = self.load(url, decode=True, post={"submit": "submit", "tiny": id})
-
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("Unable to find destination url"))
- else:
- self.urls = [m.group("link")]
- self.logDebug("Found link %s, adding to package" % self.urls[0])
diff --git a/module/plugins/crypter/LofCc.py b/module/plugins/crypter/LofCc.py
deleted file mode 100644
index 65c9b18bd..000000000
--- a/module/plugins/crypter/LofCc.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class LofCc(DeadCrypter):
- __name__ = "LofCc"
- __type__ = "crypter"
- __version__ = "0.21"
-
- __pattern__ = r'http://(?:www\.)?lof\.cc/(.*)'
- __config__ = []
-
- __description__ = """Lof.cc decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("mkaay", "mkaay@mkaay.de")]
-
-
-getInfo = create_getInfo(LofCc)
diff --git a/module/plugins/crypter/MBLinkInfo.py b/module/plugins/crypter/MBLinkInfo.py
deleted file mode 100644
index 82c2d9719..000000000
--- a/module/plugins/crypter/MBLinkInfo.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class MBLinkInfo(DeadCrypter):
- __name__ = "MBLinkInfo"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?mblink\.info/?\?id=(\d+)'
- __config__ = []
-
- __description__ = """MBLink.info decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Gummibaer", "Gummibaer@wiki-bierkiste.de"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(MBLinkInfo)
diff --git a/module/plugins/crypter/MediafireComFolder.py b/module/plugins/crypter/MediafireComFolder.py
deleted file mode 100644
index d1dc89518..000000000
--- a/module/plugins/crypter/MediafireComFolder.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from module.plugins.Crypter import Crypter
-from module.plugins.hoster.MediafireCom import checkHTMLHeader
-from module.common.json_layer import json_loads
-
-
-class MediafireComFolder(Crypter):
- __name__ = "MediafireComFolder"
- __type__ = "crypter"
- __version__ = "0.14"
-
- __pattern__ = r'http://(?:www\.)?mediafire\.com/(folder/|\?sharekey=|\?\w{13}($|[/#]))'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Mediafire.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- FOLDER_KEY_PATTERN = r'var afI= \'(\w+)'
- LINK_PATTERN = r'<meta property="og:url" content="http://www\.mediafire\.com/\?(\w+)"/>'
-
-
- def decrypt(self, pyfile):
- url, result = checkHTMLHeader(pyfile.url)
- self.logDebug("Location (%d): %s" % (result, url))
-
- if result == 0:
- # load and parse html
- html = self.load(pyfile.url)
- m = re.search(self.LINK_PATTERN, html)
- if m:
- # file page
- self.urls.append("http://www.mediafire.com/file/%s" % m.group(1))
- else:
- # folder page
- m = re.search(self.FOLDER_KEY_PATTERN, html)
- if m:
- folder_key = m.group(1)
- self.logDebug("FOLDER KEY: %s" % folder_key)
-
- json_resp = json_loads(self.load("http://www.mediafire.com/api/folder/get_info.php",
- get={'folder_key' : folder_key,
- 'response_format': "json",
- 'version' : 1}))
- #self.logInfo(json_resp)
- if json_resp['response']['result'] == "Success":
- for link in json_resp['response']['folder_info']['files']:
- self.urls.append("http://www.mediafire.com/file/%s" % link['quickkey'])
- else:
- self.fail(json_resp['response']['message'])
- elif result == 1:
- self.offline()
- else:
- self.urls.append(url)
diff --git a/module/plugins/crypter/MegaRapidCzFolder.py b/module/plugins/crypter/MegaRapidCzFolder.py
deleted file mode 100644
index d9fb828a8..000000000
--- a/module/plugins/crypter/MegaRapidCzFolder.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class MegaRapidCzFolder(SimpleCrypter):
- __name__ = "MegaRapidCzFolder"
- __type__ = "crypter"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?(share|mega)rapid\.cz/slozka/\d+/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Share-Rapid.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- LINK_PATTERN = r'<td class="soubor"[^>]*><a href="([^"]+)">'
diff --git a/module/plugins/crypter/MegauploadComFolder.py b/module/plugins/crypter/MegauploadComFolder.py
deleted file mode 100644
index 08f96700d..000000000
--- a/module/plugins/crypter/MegauploadComFolder.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class MegauploadComFolder(DeadCrypter):
- __name__ = "MegauploadComFolder"
- __type__ = "crypter"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?megaupload\.com/(\?f|xml/folderfiles\.php\?.*&?folderid)=\w+'
-
- __description__ = """Megaupload.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(MegauploadComFolder)
diff --git a/module/plugins/crypter/Movie2kTo.py b/module/plugins/crypter/Movie2kTo.py
deleted file mode 100644
index 0be7eb7eb..000000000
--- a/module/plugins/crypter/Movie2kTo.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class Movie2kTo(DeadCrypter):
- __name__ = "Movie2kTo"
- __type__ = "crypter"
- __version__ = "0.51"
-
- __pattern__ = r'http://(?:www\.)?movie2k\.to/(.*)\.html'
- __config__ = []
-
- __description__ = """Movie2k.to decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("4Christopher", "4Christopher@gmx.de")]
-
-
-getInfo = create_getInfo(Movie2kTo)
diff --git a/module/plugins/crypter/MultiUpOrg.py b/module/plugins/crypter/MultiUpOrg.py
deleted file mode 100644
index 5f3fc8b17..000000000
--- a/module/plugins/crypter/MultiUpOrg.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from urlparse import urljoin
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class MultiUpOrg(SimpleCrypter):
- __name__ = "MultiUpOrg"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?multiup\.org/(en|fr)/(?P<TYPE>project|download|miror)/\w+(/\w+)?'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """MultiUp.org crypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- NAME_PATTERN = r'<title>.*(?:Project|Projet|ownload|élécharger) (?P<N>.+?) (\(|- )'
-
-
- def getLinks(self):
- m_type = re.match(self.__pattern__, self.pyfile.url).group("TYPE")
-
- if m_type == "project":
- pattern = r'\n(http://www\.multiup\.org/(?:en|fr)/download/.*)'
- else:
- pattern = r'style="width:97%;text-align:left".*\n.*href="(.*)"'
- if m_type == "download":
- dl_pattern = r'href="(.*)">.*\n.*<h5>DOWNLOAD</h5>'
- miror_page = urljoin("http://www.multiup.org", re.search(dl_pattern, self.html).group(1))
- self.html = self.load(miror_page)
-
- return re.findall(pattern, self.html)
diff --git a/module/plugins/crypter/MultiloadCz.py b/module/plugins/crypter/MultiloadCz.py
deleted file mode 100644
index 127fe068a..000000000
--- a/module/plugins/crypter/MultiloadCz.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from module.plugins.Crypter import Crypter
-
-
-class MultiloadCz(Crypter):
- __name__ = "MultiloadCz"
- __type__ = "crypter"
- __version__ = "0.4"
-
- __pattern__ = r'http://(?:[^/]*\.)?multiload\.cz/(stahnout|slozka)/.*'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True),
- ("usedHoster", "str", "Prefered hoster list (bar-separated)", ""),
- ("ignoredHoster", "str", "Ignored hoster list (bar-separated)", "")]
-
- __description__ = """Multiload.cz decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- FOLDER_PATTERN = r'<form action="" method="get"><textarea[^>]*>([^>]*)</textarea></form>'
- LINK_PATTERN = r'<p class="manager-server"><strong>([^<]+)</strong></p><p class="manager-linky"><a href="([^"]+)">'
-
-
- def decrypt(self, pyfile):
- self.html = self.load(pyfile.url, decode=True)
-
- if re.match(self.__pattern__, pyfile.url).group(1) == "slozka":
- m = re.search(self.FOLDER_PATTERN, self.html)
- if m:
- self.urls.extend(m.group(1).split())
- else:
- m = re.findall(self.LINK_PATTERN, self.html)
- if m:
- prefered_set = set(self.getConfig("usedHoster").split('|'))
- self.urls.extend([x[1] for x in m if x[0] in prefered_set])
-
- if not self.urls:
- ignored_set = set(self.getConfig("ignoredHoster").split('|'))
- self.urls.extend([x[1] for x in m if x[0] not in ignored_set])
diff --git a/module/plugins/crypter/MultiuploadCom.py b/module/plugins/crypter/MultiuploadCom.py
deleted file mode 100644
index 347b7e5af..000000000
--- a/module/plugins/crypter/MultiuploadCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class MultiuploadCom(DeadCrypter):
- __name__ = "MultiuploadCom"
- __type__ = "crypter"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?multiupload\.(com|nl)/\w+'
- __config__ = []
-
- __description__ = """ MultiUpload.com decrypter plugin """
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(MultiuploadCom)
diff --git a/module/plugins/crypter/NCryptIn.py b/module/plugins/crypter/NCryptIn.py
deleted file mode 100644
index 3c08e26c1..000000000
--- a/module/plugins/crypter/NCryptIn.py
+++ /dev/null
@@ -1,315 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import base64
-import binascii
-import re
-
-from Crypto.Cipher import AES
-
-from module.plugins.Crypter import Crypter
-from module.plugins.internal.CaptchaService import ReCaptcha
-
-
-class NCryptIn(Crypter):
- __name__ = "NCryptIn"
- __type__ = "crypter"
- __version__ = "1.33"
-
- __pattern__ = r'http://(?:www\.)?ncrypt\.in/(?P<type>folder|link|frame)-([^/\?]+)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """NCrypt.in decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("fragonib", "fragonib[AT]yahoo[DOT]es"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- JK_KEY = "jk"
- CRYPTED_KEY = "crypted"
-
- NAME_PATTERN = r'<meta name="description" content="(?P<N>[^"]+)"'
-
-
- def setup(self):
- self.package = None
- self.cleanedHtml = None
- self.links_source_order = ["cnl2", "rsdf", "ccf", "dlc", "web"]
- self.protection_type = None
-
-
- def decrypt(self, pyfile):
- # Init
- self.package = pyfile.package()
- package_links = []
- package_name = self.package.name
- folder_name = self.package.folder
-
- # Deal with single links
- if self.isSingleLink():
- package_links.extend(self.handleSingleLink())
-
- # Deal with folders
- else:
-
- # Request folder home
- self.html = self.requestFolderHome()
- self.cleanedHtml = self.removeHtmlCrap(self.html)
- if not self.isOnline():
- self.offline()
-
- # Check for folder protection
- if self.isProtected():
- self.html = self.unlockProtection()
- self.cleanedHtml = self.removeHtmlCrap(self.html)
- self.handleErrors()
-
- # Prepare package name and folder
- (package_name, folder_name) = self.getPackageInfo()
-
- # Extract package links
- for link_source_type in self.links_source_order:
- package_links.extend(self.handleLinkSource(link_source_type))
- if package_links: # use only first source which provides links
- break
- package_links = set(package_links)
-
- # Pack and return links
- if package_links:
- self.packages = [(package_name, package_links, folder_name)]
-
-
- def isSingleLink(self):
- link_type = re.match(self.__pattern__, self.pyfile.url).group('type')
- return link_type in ("link", "frame")
-
-
- def requestFolderHome(self):
- return self.load(self.pyfile.url, decode=True)
-
-
- def removeHtmlCrap(self, content):
- patterns = (r'(type="hidden".*?(name=".*?")?.*?value=".*?")',
- r'display:none;">(.*?)</(div|span)>',
- r'<div\s+class="jdownloader"(.*?)</div>',
- r'<table class="global">(.*?)</table>',
- r'<iframe\s+style="display:none(.*?)</iframe>')
- for pattern in patterns:
- rexpr = re.compile(pattern, re.S)
- content = re.sub(rexpr, "", content)
- return content
-
-
- def isOnline(self):
- if "Your folder does not exist" in self.cleanedHtml:
- self.logDebug("File not m")
- return False
- return True
-
-
- def isProtected(self):
- form = re.search(r'<form.*?name.*?protected.*?>(.*?)</form>', self.cleanedHtml, re.S)
- if form is not None:
- content = form.group(1)
- for keyword in ("password", "captcha"):
- if keyword in content:
- self.protection_type = keyword
- self.logDebug("Links are %s protected" % self.protection_type)
- return True
- return False
-
-
- def getPackageInfo(self):
- m = re.search(self.NAME_PATTERN, self.html)
- if m:
- name = folder = m.group('N').strip()
- self.logDebug("Found name [%s] and folder [%s] in package info" % (name, folder))
- else:
- name = self.package.name
- folder = self.package.folder
- self.logDebug("Package info not m, defaulting to pyfile name [%s] and folder [%s]" % (name, folder))
- return name, folder
-
-
- def unlockProtection(self):
- postData = {}
-
- form = re.search(r'<form name="protected"(.*?)</form>', self.cleanedHtml, re.S).group(1)
-
- # Submit package password
- if "password" in form:
- password = self.getPassword()
- self.logDebug("Submitting password [%s] for protected links" % password)
- postData['password'] = password
-
- # Resolve anicaptcha
- if "anicaptcha" in form:
- self.logDebug("Captcha protected")
- captchaUri = re.search(r'src="(/temp/anicaptcha/[^"]+)', form).group(1)
- captcha = self.decryptCaptcha("http://ncrypt.in" + captchaUri)
- self.logDebug("Captcha resolved [%s]" % captcha)
- postData['captcha'] = captcha
-
- # Resolve recaptcha
- if "recaptcha" in form:
- self.logDebug("ReCaptcha protected")
- captcha_key = re.search(r'\?k=(.*?)"', form).group(1)
- self.logDebug("Resolving ReCaptcha with key [%s]" % captcha_key)
- recaptcha = ReCaptcha(self)
- challenge, code = recaptcha.challenge(captcha_key)
- postData['recaptcha_challenge_field'] = challenge
- postData['recaptcha_response_field'] = code
-
- # Resolve circlecaptcha
- if "circlecaptcha" in form:
- self.logDebug("CircleCaptcha protected")
- captcha_img_url = "http://ncrypt.in/classes/captcha/circlecaptcha.php"
- coords = self.decryptCaptcha(captcha_img_url, forceUser=True, imgtype="png", result_type='positional')
- self.logDebug("Captcha resolved, coords [%s]" % str(coords))
- postData['circle.x'] = coords[0]
- postData['circle.y'] = coords[1]
-
- # Unlock protection
- postData['submit_protected'] = 'Continue to folder'
- return self.load(self.pyfile.url, post=postData, decode=True)
-
-
- def handleErrors(self):
- if self.protection_type == "password":
- if "This password is invalid!" in self.cleanedHtml:
- self.logDebug("Incorrect password, please set right password on 'Edit package' form and retry")
- self.fail(_("Incorrect password, please set right password on 'Edit package' form and retry"))
-
- if self.protection_type == "captcha":
- if "The securitycheck was wrong!" in self.cleanedHtml:
- self.invalidCaptcha()
- self.retry()
- else:
- self.correctCaptcha()
-
-
- def handleLinkSource(self, link_source_type):
- # Check for JS engine
- require_js_engine = link_source_type in ("cnl2", "rsdf", "ccf", "dlc")
- if require_js_engine and not self.js:
- self.logDebug("No JS engine available, skip %s links" % link_source_type)
- return []
-
- # Select suitable handler
- if link_source_type == 'single':
- return self.handleSingleLink()
- if link_source_type == 'cnl2':
- return self.handleCNL2()
- elif link_source_type in ("rsdf", "ccf", "dlc"):
- return self.handleContainer(link_source_type)
- elif link_source_type == "web":
- return self.handleWebLinks()
- else:
- self.error('Unknown source type "%s" (this is probably a bug)' % link_source_type)
-
-
- def handleSingleLink(self):
- self.logDebug("Handling Single link")
- package_links = []
-
- # Decrypt single link
- decrypted_link = self.decryptLink(self.pyfile.url)
- if decrypted_link:
- package_links.append(decrypted_link)
-
- return package_links
-
-
- def handleCNL2(self):
- self.logDebug("Handling CNL2 links")
- package_links = []
-
- if 'cnl2_output' in self.cleanedHtml:
- try:
- (vcrypted, vjk) = self._getCipherParams()
- for (crypted, jk) in zip(vcrypted, vjk):
- package_links.extend(self._getLinks(crypted, jk))
- except:
- self.fail(_("Unable to decrypt CNL2 links"))
-
- return package_links
-
-
- def handleContainers(self):
- self.logDebug("Handling Container links")
- package_links = []
-
- pattern = r'/container/(rsdf|dlc|ccf)/(\w+)'
- containersLinks = re.findall(pattern, self.html)
- self.logDebug("Decrypting %d Container links" % len(containersLinks))
- for containerLink in containersLinks:
- link = "http://ncrypt.in/container/%s/%s.%s" % (containerLink[0], containerLink[1], containerLink[0])
- package_links.append(link)
-
- return package_links
-
-
- def handleWebLinks(self):
- self.logDebug("Handling Web links")
- pattern = r'(http://ncrypt\.in/link-.*?=)'
- links = re.findall(pattern, self.html)
-
- package_links = []
- self.logDebug("Decrypting %d Web links" % len(links))
- for i, link in enumerate(links):
- self.logDebug("Decrypting Web link %d, %s" % (i + 1, link))
- decrypted_link = self.decrypt(link)
- if decrypted_link:
- package_links.append(decrypted_link)
-
- return package_links
-
-
- def decryptLink(self, link):
- try:
- url = link.replace("link-", "frame-")
- link = self.load(url, just_header=True)['location']
- return link
- except Exception, detail:
- self.logDebug("Error decrypting link %s, %s" % (link, detail))
-
-
- def _getCipherParams(self):
- pattern = r'<input.*?name="%s".*?value="(.*?)"'
-
- # Get jk
- jk_re = pattern % NCryptIn.JK_KEY
- vjk = re.findall(jk_re, self.html)
-
- # Get crypted
- crypted_re = pattern % NCryptIn.CRYPTED_KEY
- vcrypted = re.findall(crypted_re, self.html)
-
- # Log and return
- self.logDebug("Detected %d crypted blocks" % len(vcrypted))
- return vcrypted, vjk
-
-
- def _getLinks(self, crypted, jk):
- # Get key
- jreturn = self.js.eval("%s f()" % jk)
- self.logDebug("JsEngine returns value [%s]" % jreturn)
- key = binascii.unhexlify(jreturn)
-
- # Decode crypted
- crypted = base64.standard_b64decode(crypted)
-
- # Decrypt
- Key = key
- IV = key
- obj = AES.new(Key, AES.MODE_CBC, IV)
- text = obj.decrypt(crypted)
-
- # Extract links
- text = text.replace("\x00", "").replace("\r", "")
- links = text.split("\n")
- links = filter(lambda x: x != "", links)
-
- # Log and return
- self.logDebug("Block has %d links" % len(links))
- return links
diff --git a/module/plugins/crypter/NetfolderIn.py b/module/plugins/crypter/NetfolderIn.py
deleted file mode 100644
index a47bd0503..000000000
--- a/module/plugins/crypter/NetfolderIn.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class NetfolderIn(SimpleCrypter):
- __name__ = "NetfolderIn"
- __type__ = "crypter"
- __version__ = "0.72"
-
- __pattern__ = r'http://(?:www\.)?netfolder\.in/((?P<id1>\w+)/\w+|folder\.php\?folder_id=(?P<id2>\w+))'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """NetFolder.in decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("fragonib", "fragonib[AT]yahoo[DOT]es")]
-
-
- NAME_PATTERN = r'<div class="Text">Inhalt des Ordners <span.*>(?P<N>.+)</span></div>'
-
-
- def prepare(self):
- super(NetfolderIn, self).prepare()
-
- # Check for password protection
- if self.isPasswordProtected():
- self.html = self.submitPassword()
- if not self.html:
- self.fail(_("Incorrect password, please set right password on Add package form and retry"))
-
-
- def isPasswordProtected(self):
- if '<input type="password" name="password"' in self.html:
- self.logDebug("Links are password protected")
- return True
- return False
-
-
- def submitPassword(self):
- # Gather data
- try:
- m = re.match(self.__pattern__, self.pyfile.url)
- id = max(m.group('id1'), m.group('id2'))
- except AttributeError:
- self.logDebug("Unable to get package id from url [%s]" % self.pyfile.url)
- return
- url = "http://netfolder.in/folder.php?folder_id=" + id
- password = self.getPassword()
-
- # Submit package password
- post = {'password': password, 'save': 'Absenden'}
- self.logDebug("Submitting password [%s] for protected links with id [%s]" % (password, id))
- html = self.load(url, {}, post)
-
- # Check for invalid password
- if '<div class="InPage_Error">' in html:
- self.logDebug("Incorrect password, please set right password on Edit package form and retry")
- return None
-
- return html
-
-
- def getLinks(self):
- links = re.search(r'name="list" value="(.*?)"', self.html).group(1).split(",")
- self.logDebug("Package has %d links" % len(links))
- return links
diff --git a/module/plugins/crypter/NosvideoCom.py b/module/plugins/crypter/NosvideoCom.py
deleted file mode 100644
index 3abefdadb..000000000
--- a/module/plugins/crypter/NosvideoCom.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class NosvideoCom(SimpleCrypter):
- __name__ = "NosvideoCom"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?nosvideo\.com/\?v=\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Nosvideo.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("igel", "igelkun@myopera.com")]
-
-
- LINK_PATTERN = r'href="(http://(?:w{3}\.)?nosupload\.com/\?d=\w+)"'
- NAME_PATTERN = r'<[tT]itle>Watch (?P<N>.+?)<'
diff --git a/module/plugins/crypter/OneKhDe.py b/module/plugins/crypter/OneKhDe.py
deleted file mode 100644
index 540f14568..000000000
--- a/module/plugins/crypter/OneKhDe.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.unescape import unescape
-from module.plugins.Crypter import Crypter
-
-
-class OneKhDe(Crypter):
- __name__ = "OneKhDe"
- __type__ = "crypter"
- __version__ = "0.1"
-
- __pattern__ = r'http://(?:www\.)?1kh\.de/f/'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """1kh.de decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("spoob", "spoob@pyload.org")]
-
-
- def __init__(self, parent):
- Crypter.__init__(self, parent)
- self.parent = parent
-
-
- def file_exists(self):
- """ returns True or False
- """
- return True
-
-
- def proceed(self, url, location):
- url = self.parent.url
- self.html = self.load(url)
- link_ids = re.findall(r"<a id=\"DownloadLink_(\d*)\" href=\"http://1kh.de/", self.html)
- for id in link_ids:
- new_link = unescape(
- re.search("width=\"100%\" src=\"(.*)\"></iframe>", self.load("http://1kh.de/l/" + id)).group(1))
- self.urls.append(new_link)
diff --git a/module/plugins/crypter/OronComFolder.py b/module/plugins/crypter/OronComFolder.py
deleted file mode 100755
index 9e06bdf32..000000000
--- a/module/plugins/crypter/OronComFolder.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class OronComFolder(DeadCrypter):
- __name__ = "OronComFolder"
- __type__ = "crypter"
- __version__ = "0.11"
-
- __pattern__ = r'http://(?:www\.)?oron\.com/folder/\w+'
- __config__ = []
-
- __description__ = """Oron.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("DHMH", "webmaster@pcProfil.de")]
-
-
-getInfo = create_getInfo(OronComFolder)
diff --git a/module/plugins/crypter/PastebinCom.py b/module/plugins/crypter/PastebinCom.py
deleted file mode 100644
index 1dae39bb8..000000000
--- a/module/plugins/crypter/PastebinCom.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class PastebinCom(SimpleCrypter):
- __name__ = "PastebinCom"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?pastebin\.com/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Pastebin.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- LINK_PATTERN = r'<div class="de\d+">(https?://[^ <]+)(?:[^<]*)</div>'
- NAME_PATTERN = r'<div class="paste_box_line1" title="(?P<N>[^"]+)">'
diff --git a/module/plugins/crypter/QuickshareCzFolder.py b/module/plugins/crypter/QuickshareCzFolder.py
deleted file mode 100644
index 52d558af7..000000000
--- a/module/plugins/crypter/QuickshareCzFolder.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from module.plugins.Crypter import Crypter
-
-
-class QuickshareCzFolder(Crypter):
- __name__ = "QuickshareCzFolder"
- __type__ = "crypter"
- __version__ = "0.1"
-
- __pattern__ = r'http://(?:www\.)?quickshare\.cz/slozka-\d+.*'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Quickshare.cz folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- FOLDER_PATTERN = r'<textarea[^>]*>(.*?)</textarea>'
- LINK_PATTERN = r'(http://www\.quickshare\.cz/\S+)'
-
-
- def decrypt(self, pyfile):
- html = self.load(pyfile.url)
-
- m = re.search(self.FOLDER_PATTERN, html, re.S)
- if m is None:
- self.error(_("FOLDER_PATTERN not found"))
- self.urls.extend(re.findall(self.LINK_PATTERN, m.group(1)))
diff --git a/module/plugins/crypter/RSLayerCom.py b/module/plugins/crypter/RSLayerCom.py
deleted file mode 100644
index cc3b23bbc..000000000
--- a/module/plugins/crypter/RSLayerCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class RSLayerCom(DeadCrypter):
- __name__ = "RSLayerCom"
- __type__ = "crypter"
- __version__ = "0.21"
-
- __pattern__ = r'http://(?:www\.)?rs-layer\.com/directory-'
- __config__ = []
-
- __description__ = """RS-Layer.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("hzpz", None)]
-
-
-getInfo = create_getInfo(RSLayerCom)
diff --git a/module/plugins/crypter/RapidfileshareNetFolder.py b/module/plugins/crypter/RapidfileshareNetFolder.py
deleted file mode 100644
index 5531d5a90..000000000
--- a/module/plugins/crypter/RapidfileshareNetFolder.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSCrypter import XFSCrypter
-
-
-class RapidfileshareNetFolder(XFSCrypter):
- __name__ = "RapidfileshareNetFolder"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?rapidfileshare\.net/users/\w+/\d+/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Rapidfileshare.net folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("guidobelix", "guidobelix@hotmail.it")]
-
-
- HOSTER_DOMAIN = "rapidfileshare.net"
diff --git a/module/plugins/crypter/RelinkUs.py b/module/plugins/crypter/RelinkUs.py
deleted file mode 100644
index 1111118b2..000000000
--- a/module/plugins/crypter/RelinkUs.py
+++ /dev/null
@@ -1,284 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-import base64
-import binascii
-import re
-import os
-
-from Crypto.Cipher import AES
-from module.plugins.Crypter import Crypter
-
-
-class RelinkUs(Crypter):
- __name__ = "RelinkUs"
- __type__ = "crypter"
- __version__ = "3.11"
-
- __pattern__ = r'http://(?:www\.)?relink\.us/(f/|((view|go)\.php\?id=))(?P<id>.+)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Relink.us decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("fragonib", "fragonib[AT]yahoo[DOT]es"),
- ("AndroKev", "neureither.kevin@gmail.com")]
-
-
- PREFERRED_LINK_SOURCES = ["cnl2", "dlc", "web"]
-
- OFFLINE_TOKEN = r'<title>Tattooside'
-
- PASSWORD_TOKEN = r'container_password.php'
- PASSWORD_ERROR_ROKEN = r'You have entered an incorrect password'
- PASSWORD_SUBMIT_URL = r'http://www.relink.us/container_password.php'
-
- CAPTCHA_TOKEN = r'container_captcha.php'
- CAPTCHA_ERROR_ROKEN = r'You have solved the captcha wrong'
- CAPTCHA_IMG_URL = r'http://www.relink.us/core/captcha/circlecaptcha.php'
- CAPTCHA_SUBMIT_URL = r'http://www.relink.us/container_captcha.php'
-
- FILE_TITLE_REGEX = r'<th>Title</th><td>(.*)</td></tr>'
- FILE_NOTITLE = r'No title'
-
- CNL2_FORM_REGEX = r'<form id="cnl_form-(.*?)</form>'
- CNL2_FORMINPUT_REGEX = r'<input.*?name="%s".*?value="(.*?)"'
- CNL2_JK_KEY = "jk"
- CNL2_CRYPTED_KEY = "crypted"
-
- DLC_LINK_REGEX = r'<a href=".*?" class="dlc_button" target="_blank">'
- DLC_DOWNLOAD_URL = r'http://www.relink.us/download.php'
-
- WEB_FORWARD_REGEX = r'getFile\(\'(?P<link>.+)\'\)'
- WEB_FORWARD_URL = r'http://www.relink.us/frame.php'
- WEB_LINK_REGEX = r'<iframe name="Container" height="100%" frameborder="no" width="100%" src="(?P<link>.+)"></iframe>'
-
-
- def setup(self):
- self.fileid = None
- self.package = None
- self.password = None
- self.captcha = False
-
-
- def decrypt(self, pyfile):
- # Init
- self.initPackage(pyfile)
-
- # Request package
- self.requestPackage()
-
- # Check for online
- if not self.isOnline():
- self.offline()
-
- # Check for protection
- if self.isPasswordProtected():
- self.unlockPasswordProtection()
- self.handleErrors()
-
- if self.isCaptchaProtected():
- self.captcha = True
- self.unlockCaptchaProtection()
- self.handleErrors()
-
- # Get package name and folder
- (package_name, folder_name) = self.getPackageInfo()
-
- # Extract package links
- package_links = []
- for sources in self.PREFERRED_LINK_SOURCES:
- package_links.extend(self.handleLinkSource(sources))
- if package_links: # use only first source which provides links
- break
- package_links = set(package_links)
-
- # Pack
- if package_links:
- self.packages = [(package_name, package_links, folder_name)]
-
-
- def initPackage(self, pyfile):
- self.fileid = re.match(self.__pattern__, pyfile.url).group('id')
- self.package = pyfile.package()
- self.password = self.getPassword()
-
-
- def requestPackage(self):
- self.html = self.load(self.pyfile.url, decode=True)
-
-
- def isOnline(self):
- if self.OFFLINE_TOKEN in self.html:
- self.logDebug("File not found")
- return False
- return True
-
-
- def isPasswordProtected(self):
- if self.PASSWORD_TOKEN in self.html:
- self.logDebug("Links are password protected")
- return True
-
-
- def isCaptchaProtected(self):
- if self.CAPTCHA_TOKEN in self.html:
- self.logDebug("Links are captcha protected")
- return True
- return False
-
-
- def unlockPasswordProtection(self):
- self.logDebug("Submitting password [%s] for protected links" % self.password)
- passwd_url = self.PASSWORD_SUBMIT_URL + "?id=%s" % self.fileid
- passwd_data = {'id': self.fileid, 'password': self.password, 'pw': 'submit'}
- self.html = self.load(passwd_url, post=passwd_data, decode=True)
-
-
- def unlockCaptchaProtection(self):
- self.logDebug("Request user positional captcha resolving")
- captcha_img_url = self.CAPTCHA_IMG_URL + "?id=%s" % self.fileid
- coords = self.decryptCaptcha(captcha_img_url, forceUser=True, imgtype="png", result_type='positional')
- self.logDebug("Captcha resolved, coords [%s]" % str(coords))
- captcha_post_url = self.CAPTCHA_SUBMIT_URL + "?id=%s" % self.fileid
- captcha_post_data = {'button.x': coords[0], 'button.y': coords[1], 'captcha': 'submit'}
- self.html = self.load(captcha_post_url, post=captcha_post_data, decode=True)
-
-
- def getPackageInfo(self):
- name = folder = None
-
- # Try to get info from web
- m = re.search(self.FILE_TITLE_REGEX, self.html)
- if m is not None:
- title = m.group(1).strip()
- if not self.FILE_NOTITLE in title:
- name = folder = title
- self.logDebug("Found name [%s] and folder [%s] in package info" % (name, folder))
-
- # Fallback to defaults
- if not name or not folder:
- name = self.package.name
- folder = self.package.folder
- self.logDebug("Package info not found, defaulting to pyfile name [%s] and folder [%s]" % (name, folder))
-
- # Return package info
- return name, folder
-
-
- def handleErrors(self):
- if self.PASSWORD_ERROR_ROKEN in self.html:
- msg = "Incorrect password, please set right password on 'Edit package' form and retry"
- self.logDebug(msg)
- self.fail(_(msg))
-
- if self.captcha:
- if self.CAPTCHA_ERROR_ROKEN in self.html:
- self.invalidCaptcha()
- self.retry()
- else:
- self.correctCaptcha()
-
-
- def handleLinkSource(self, source):
- if source == 'cnl2':
- return self.handleCNL2Links()
- elif source == 'dlc':
- return self.handleDLCLinks()
- elif source == 'web':
- return self.handleWEBLinks()
- else:
- self.error('Unknown source type "%s" (this is probably a bug)' % source)
-
-
- def handleCNL2Links(self):
- self.logDebug("Search for CNL2 links")
- package_links = []
- m = re.search(self.CNL2_FORM_REGEX, self.html, re.S)
- if m is not None:
- cnl2_form = m.group(1)
- try:
- (vcrypted, vjk) = self._getCipherParams(cnl2_form)
- for (crypted, jk) in zip(vcrypted, vjk):
- package_links.extend(self._getLinks(crypted, jk))
- except:
- self.logDebug("Unable to decrypt CNL2 links")
- return package_links
-
-
- def handleDLCLinks(self):
- self.logDebug("Search for DLC links")
- package_links = []
- m = re.search(self.DLC_LINK_REGEX, self.html)
- if m is not None:
- container_url = self.DLC_DOWNLOAD_URL + "?id=%s&dlc=1" % self.fileid
- self.logDebug("Downloading DLC container link [%s]" % container_url)
- try:
- dlc = self.load(container_url)
- dlc_filename = self.fileid + ".dlc"
- dlc_filepath = os.path.join(self.config['general']['download_folder'], dlc_filename)
- with open(dlc_filepath, "wb") as f:
- f.write(dlc)
- package_links.append(dlc_filepath)
- except:
- self.fail("Unable to download DLC container")
- return package_links
-
-
- def handleWEBLinks(self):
- self.logDebug("Search for WEB links")
- package_links = []
- fw_params = re.findall(self.WEB_FORWARD_REGEX, self.html)
- self.logDebug("Decrypting %d Web links" % len(fw_params))
- for index, fw_param in enumerate(fw_params):
- try:
- fw_url = self.WEB_FORWARD_URL + "?%s" % fw_param
- self.logDebug("Decrypting Web link %d, %s" % (index + 1, fw_url))
- fw_response = self.load(fw_url, decode=True)
- dl_link = re.search(self.WEB_LINK_REGEX, fw_response).group('link')
- package_links.append(dl_link)
- except Exception, detail:
- self.logDebug("Error decrypting Web link %s, %s" % (index, detail))
- self.setWait(4)
- self.wait()
- return package_links
-
-
- def _getCipherParams(self, cnl2_form):
- # Get jk
- jk_re = self.CNL2_FORMINPUT_REGEX % self.CNL2_JK_KEY
- vjk = re.findall(jk_re, cnl2_form, re.I)
-
- # Get crypted
- crypted_re = self.CNL2_FORMINPUT_REGEX % RelinkUs.CNL2_CRYPTED_KEY
- vcrypted = re.findall(crypted_re, cnl2_form, re.I)
-
- # Log and return
- self.logDebug("Detected %d crypted blocks" % len(vcrypted))
- return vcrypted, vjk
-
-
- def _getLinks(self, crypted, jk):
- # Get key
- jreturn = self.js.eval("%s f()" % jk)
- self.logDebug("JsEngine returns value [%s]" % jreturn)
- key = binascii.unhexlify(jreturn)
-
- # Decode crypted
- crypted = base64.standard_b64decode(crypted)
-
- # Decrypt
- Key = key
- IV = key
- obj = AES.new(Key, AES.MODE_CBC, IV)
- text = obj.decrypt(crypted)
-
- # Extract links
- text = text.replace("\x00", "").replace("\r", "")
- links = text.split("\n")
- links = filter(lambda x: x != "", links)
-
- # Log and return
- self.logDebug("Package has %d links" % len(links))
- return links
diff --git a/module/plugins/crypter/SafelinkingNet.py b/module/plugins/crypter/SafelinkingNet.py
deleted file mode 100644
index 9bb6c3229..000000000
--- a/module/plugins/crypter/SafelinkingNet.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from pycurl import FOLLOWLOCATION
-
-from BeautifulSoup import BeautifulSoup
-
-from module.common.json_layer import json_loads
-from module.plugins.Crypter import Crypter
-from module.plugins.internal.CaptchaService import SolveMedia
-
-
-class SafelinkingNet(Crypter):
- __name__ = "SafelinkingNet"
- __type__ = "crypter"
- __version__ = "0.11"
-
- __pattern__ = r'https?://(?:www\.)?safelinking\.net/([pd])/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Safelinking.net decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("quareevo", "quareevo@arcor.de")]
-
-
- SOLVEMEDIA_PATTERN = "solvemediaApiKey = '([\w.-]+)';"
-
-
- def decrypt(self, pyfile):
- url = pyfile.url
-
- if re.match(self.__pattern__, url).group(1) == "d":
-
- header = self.load(url, just_header=True)
- if 'location' in header:
- self.urls = [header['location']]
- else:
- self.error(_("Couldn't find forwarded Link"))
-
- else:
- postData = {"post-protect": "1"}
-
- if "link-password" in self.html:
- postData['link-password'] = self.getPassword()
-
- if "altcaptcha" in self.html:
- for _i in xrange(5):
- m = re.search(self.SOLVEMEDIA_PATTERN, self.html)
- if m:
- captchaKey = m.group(1)
- captcha = SolveMedia(self)
- captchaProvider = "Solvemedia"
- else:
- self.fail(_("Error parsing captcha"))
-
- challenge, response = captcha.challenge(captchaKey)
- postData['adcopy_challenge'] = challenge
- postData['adcopy_response'] = response
-
- self.html = self.load(url, post=postData)
- if "The password you entered was incorrect" in self.html:
- self.fail(_("Incorrect Password"))
- if not "The CAPTCHA code you entered was wrong" in self.html:
- break
-
- pyfile.package().password = ""
- soup = BeautifulSoup(self.html)
- scripts = soup.findAll("script")
- for s in scripts:
- if "d_links" in s.text:
- break
- m = re.search('d_links":(\[.*?\])', s.text)
- if m:
- linkDict = json_loads(m.group(1))
- for link in linkDict:
- if not "http://" in link['full']:
- self.urls.append("https://safelinking.net/d/" + link['full'])
- else:
- self.urls.append(link['full'])
diff --git a/module/plugins/crypter/SecuredIn.py b/module/plugins/crypter/SecuredIn.py
deleted file mode 100644
index cbfa919ac..000000000
--- a/module/plugins/crypter/SecuredIn.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class SecuredIn(DeadCrypter):
- __name__ = "SecuredIn"
- __type__ = "crypter"
- __version__ = "0.21"
-
- __pattern__ = r'http://(?:www\.)?secured\.in/download-[\d]+-\w{8}\.html'
- __config__ = []
-
- __description__ = """Secured.in decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("mkaay", "mkaay@mkaay.de")]
-
-
-getInfo = create_getInfo(SecuredIn)
diff --git a/module/plugins/crypter/SexuriaCom.py b/module/plugins/crypter/SexuriaCom.py
deleted file mode 100644
index 3c952fd6b..000000000
--- a/module/plugins/crypter/SexuriaCom.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Crypter import Crypter
-
-
-class SexuriaCom(Crypter):
- __name__ = "SexuriaCom"
- __type__ = "crypter"
- __version__ = "0.01"
-
- __pattern__ = r'http://(?:www\.)?sexuria\.com/(v1/)?(Pornos_Kostenlos_.+?_(\d+)\.html|dl_links_\d+_\d+\.html|id=\d+\&part=\d+\&link=\d+)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Sexuria.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("NETHead", "NETHead.AT.gmx.DOT.net")]
-
-
- PATTERN_SUPPORTED_MAIN = re.compile(r'http://(www\.)?sexuria\.com/(v1/)?Pornos_Kostenlos_.+?_(\d+)\.html', flags=re.I)
- PATTERN_SUPPORTED_CRYPT = re.compile(r'http://(www\.)?sexuria\.com/(v1/)?dl_links_\d+_(?P<ID>\d+)\.html', flags=re.I)
- PATTERN_SUPPORTED_REDIRECT = re.compile(r'http://(www\.)?sexuria\.com/out\.php\?id=(?P<ID>\d+)\&part=\d+\&link=\d+', flags=re.I)
- PATTERN_TITLE = re.compile(r'<title> - (?P<TITLE>.*) Sexuria - Kostenlose Pornos - Rapidshare XXX Porn</title>', flags=re.I)
- PATTERN_PASSWORD = re.compile(r'<strong>Passwort: </strong></div></td>.*?bgcolor="#EFEFEF">(?P<PWD>.*?)</td>', flags=re.I | re.S)
- PATTERN_DL_LINK_PAGE = re.compile(r'"(dl_links_\d+_\d+\.html)"', flags=re.I)
- PATTERN_REDIRECT_LINKS = re.compile(r'value="(http://sexuria\.com/out\.php\?id=\d+\&part=\d+\&link=\d+)" readonly', flags=re.I)
-
-
- def decrypt(self, pyfile):
- # Init
- self.pyfile = pyfile
- self.package = pyfile.package()
-
- # Get package links
- package_name, self.links, folder_name, package_pwd = self.decryptLinks(self.pyfile.url)
- self.packages = [(package_name, self.links, folder_name)]
-
-
- def decryptLinks(self, url):
- linklist = []
- name = self.package.name
- folder = self.package.folder
- password = None
-
- if re.match(self.PATTERN_SUPPORTED_MAIN, url):
- # Processing main page
- html = self.load(url)
- links = re.findall(self.PATTERN_DL_LINK_PAGE, html)
- for link in links:
- linklist.append("http://sexuria.com/v1/" + link)
-
- elif re.match(self.PATTERN_SUPPORTED_REDIRECT, url):
- # Processing direct redirect link (out.php), redirecting to main page
- id = re.search(self.PATTERN_SUPPORTED_REDIRECT, url).group('ID')
- if id:
- linklist.append("http://sexuria.com/v1/Pornos_Kostenlos_liebe_%s.html" % id)
-
- elif re.match(self.PATTERN_SUPPORTED_CRYPT, url):
- # Extract info from main file
- id = re.search(self.PATTERN_SUPPORTED_CRYPT, url).group('ID')
- html = self.load("http://sexuria.com/v1/Pornos_Kostenlos_info_%s.html" % id, decode=True)
-
- title = re.search(self.PATTERN_TITLE, html).group('TITLE').strip()
- if title:
- name = folder = title
- self.logDebug("Package info found, name [%s] and folder [%s]" % (name, folder))
-
- pwd = re.search(self.PATTERN_PASSWORD, html).group('PWD')
- if pwd:
- password = pwd.strip()
- self.logDebug("Password info [%s] found" % password)
-
- # Process link (dl_link)
- html = self.load(url)
- links = re.findall(self.PATTERN_REDIRECT_LINKS, html)
- if len(links) == 0:
- self.LogError("Broken for link %s" % link)
- else:
- for link in links:
- link = link.replace("http://sexuria.com/", "http://www.sexuria.com/")
- finallink = self.load(link, just_header=True)['location']
- if not finallink or "sexuria.com/" in finallink:
- self.LogError("Broken for link %s" % link)
- else:
- linklist.append(finallink)
-
- # Debug log
- self.logDebug("%d supported links" % len(linklist))
- for i, link in enumerate(linklist):
- self.logDebug("Supported link %d, %s" % (i + 1, link))
-
- return name, linklist, folder, password
diff --git a/module/plugins/crypter/ShareLinksBiz.py b/module/plugins/crypter/ShareLinksBiz.py
deleted file mode 100644
index d2e8138f6..000000000
--- a/module/plugins/crypter/ShareLinksBiz.py
+++ /dev/null
@@ -1,286 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import base64
-import binascii
-import re
-
-from Crypto.Cipher import AES
-from module.plugins.Crypter import Crypter
-
-
-class ShareLinksBiz(Crypter):
- __name__ = "ShareLinksBiz"
- __type__ = "crypter"
- __version__ = "1.14"
-
- __pattern__ = r'http://(?:www\.)?(share-links|s2l)\.biz/(?P<ID>_?\w+)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Share-Links.biz decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("fragonib", "fragonib[AT]yahoo[DOT]es")]
-
-
- def setup(self):
- self.baseUrl = None
- self.fileId = None
- self.package = None
- self.captcha = False
-
-
- def decrypt(self, pyfile):
- # Init
- self.initFile(pyfile)
-
- # Request package
- url = self.baseUrl + '/' + self.fileId
- self.html = self.load(url, decode=True)
-
- # Unblock server (load all images)
- self.unblockServer()
-
- # Check for protection
- if self.isPasswordProtected():
- self.unlockPasswordProtection()
- self.handleErrors()
-
- if self.isCaptchaProtected():
- self.captcha = True
- self.unlockCaptchaProtection()
- self.handleErrors()
-
- # Extract package links
- package_links = []
- package_links.extend(self.handleWebLinks())
- package_links.extend(self.handleContainers())
- package_links.extend(self.handleCNL2())
- package_links = set(package_links)
-
- # Get package info
- package_name, package_folder = self.getPackageInfo()
-
- # Pack
- self.packages = [(package_name, package_links, package_folder)]
-
-
- def initFile(self, pyfile):
- url = pyfile.url
- if 's2l.biz' in url:
- url = self.load(url, just_header=True)['location']
- self.baseUrl = "http://www.%s.biz" % re.match(self.__pattern__, url).group(1)
- self.fileId = re.match(self.__pattern__, url).group('ID')
- self.package = pyfile.package()
-
-
- def isOnline(self):
- if "No usable content was found" in self.html:
- self.logDebug("File not found")
- return False
- return True
-
-
- def isPasswordProtected(self):
- if re.search(r'''<form.*?id="passwordForm".*?>''', self.html):
- self.logDebug("Links are protected")
- return True
- return False
-
-
- def isCaptchaProtected(self):
- if '<map id="captchamap"' in self.html:
- self.logDebug("Links are captcha protected")
- return True
- return False
-
-
- def unblockServer(self):
- imgs = re.findall(r"(/template/images/.*?\.gif)", self.html)
- for img in imgs:
- self.load(self.baseUrl + img)
-
-
- def unlockPasswordProtection(self):
- password = self.getPassword()
- self.logDebug("Submitting password [%s] for protected links" % password)
- post = {"password": password, 'login': 'Submit form'}
- url = self.baseUrl + '/' + self.fileId
- self.html = self.load(url, post=post, decode=True)
-
-
- def unlockCaptchaProtection(self):
- # Get captcha map
- captchaMap = self._getCaptchaMap()
- self.logDebug("Captcha map with [%d] positions" % len(captchaMap.keys()))
-
- # Request user for captcha coords
- m = re.search(r'<img src="/captcha.gif\?d=(.*?)&amp;PHPSESSID=(.*?)&amp;legend=1"', self.html)
- captchaUrl = self.baseUrl + '/captcha.gif?d=%s&PHPSESSID=%s' % (m.group(1), m.group(2))
- self.logDebug("Waiting user for correct position")
- coords = self.decryptCaptcha(captchaUrl, forceUser=True, imgtype="gif", result_type='positional')
- self.logDebug("Captcha resolved, coords [%s]" % str(coords))
-
- # Resolve captcha
- href = self._resolveCoords(coords, captchaMap)
- if href is None:
- self.invalidCaptcha()
- self.retry(wait_time=5)
- url = self.baseUrl + href
- self.html = self.load(url, decode=True)
-
-
- def _getCaptchaMap(self):
- mapp = {}
- for m in re.finditer(r'<area shape="rect" coords="(.*?)" href="(.*?)"', self.html):
- rect = eval('(' + m.group(1) + ')')
- href = m.group(2)
- mapp[rect] = href
- return mapp
-
-
- def _resolveCoords(self, coords, captchaMap):
- x, y = coords
- for rect, href in captchaMap.iteritems():
- x1, y1, x2, y2 = rect
- if (x >= x1 and x <= x2) and (y >= y1 and y <= y2):
- return href
-
-
- def handleErrors(self):
- if "The inserted password was wrong" in self.html:
- self.logDebug("Incorrect password, please set right password on 'Edit package' form and retry")
- self.fail(_("Incorrect password, please set right password on 'Edit package' form and retry"))
-
- if self.captcha:
- if "Your choice was wrong" in self.html:
- self.invalidCaptcha()
- self.retry(wait_time=5)
- else:
- self.correctCaptcha()
-
-
- def getPackageInfo(self):
- name = folder = None
-
- # Extract from web package header
- title_re = r'<h2><img.*?/>(.*)</h2>'
- m = re.search(title_re, self.html, re.S)
- if m is not None:
- title = m.group(1).strip()
- if 'unnamed' not in title:
- name = folder = title
- self.logDebug("Found name [%s] and folder [%s] in package info" % (name, folder))
-
- # Fallback to defaults
- if not name or not folder:
- name = self.package.name
- folder = self.package.folder
- self.logDebug("Package info not found, defaulting to pyfile name [%s] and folder [%s]" % (name, folder))
-
- # Return package info
- return name, folder
-
-
- def handleWebLinks(self):
- package_links = []
- self.logDebug("Handling Web links")
-
- #@TODO: Gather paginated web links
- pattern = r'javascript:_get\(\'(.*?)\', \d+, \'\'\)'
- ids = re.findall(pattern, self.html)
- self.logDebug("Decrypting %d Web links" % len(ids))
- for i, ID in enumerate(ids):
- try:
- self.logDebug("Decrypting Web link %d, [%s]" % (i + 1, ID))
-
- dwLink = self.baseUrl + "/get/lnk/" + ID
- res = self.load(dwLink)
-
- code = re.search(r'frm/(\d+)', res).group(1)
- fwLink = self.baseUrl + "/get/frm/" + code
- res = self.load(fwLink)
-
- jscode = re.search(r'<script language="javascript">\s*eval\((.*)\)\s*</script>', res, re.S).group(1)
- jscode = self.js.eval("f = %s" % jscode)
- jslauncher = "window=''; parent={frames:{Main:{location:{href:''}}},location:''}; %s; parent.frames.Main.location.href"
-
- dlLink = self.js.eval(jslauncher % jscode)
-
- self.logDebug("JsEngine returns value [%s] for redirection link" % dlLink)
-
- package_links.append(dlLink)
- except Exception, detail:
- self.logDebug("Error decrypting Web link [%s], %s" % (ID, detail))
- return package_links
-
-
- def handleContainers(self):
- package_links = []
- self.logDebug("Handling Container links")
-
- pattern = r'javascript:_get\(\'(.*?)\', 0, \'(rsdf|ccf|dlc)\'\)'
- containersLinks = re.findall(pattern, self.html)
- self.logDebug("Decrypting %d Container links" % len(containersLinks))
- for containerLink in containersLinks:
- link = "%s/get/%s/%s" % (self.baseUrl, containerLink[1], containerLink[0])
- package_links.append(link)
- return package_links
-
-
- def handleCNL2(self):
- package_links = []
- self.logDebug("Handling CNL2 links")
-
- if '/lib/cnl2/ClicknLoad.swf' in self.html:
- try:
- (crypted, jk) = self._getCipherParams()
- package_links.extend(self._getLinks(crypted, jk))
- except:
- self.fail(_("Unable to decrypt CNL2 links"))
- return package_links
-
-
- def _getCipherParams(self):
- # Request CNL2
- code = re.search(r'ClicknLoad.swf\?code=(.*?)"', self.html).group(1)
- url = "%s/get/cnl2/%s" % (self.baseUrl, code)
- res = self.load(url)
- params = res.split(";;")
-
- # Get jk
- strlist = list(base64.standard_b64decode(params[1]))
- strlist.reverse()
- jk = ''.join(strlist)
-
- # Get crypted
- strlist = list(base64.standard_b64decode(params[2]))
- strlist.reverse()
- crypted = ''.join(strlist)
-
- # Log and return
- return crypted, jk
-
-
- def _getLinks(self, crypted, jk):
- # Get key
- jreturn = self.js.eval("%s f()" % jk)
- self.logDebug("JsEngine returns value [%s]" % jreturn)
- key = binascii.unhexlify(jreturn)
-
- # Decode crypted
- crypted = base64.standard_b64decode(crypted)
-
- # Decrypt
- Key = key
- IV = key
- obj = AES.new(Key, AES.MODE_CBC, IV)
- text = obj.decrypt(crypted)
-
- # Extract links
- text = text.replace("\x00", "").replace("\r", "")
- links = text.split("\n")
- links = filter(lambda x: x != "", links)
-
- # Log and return
- self.logDebug("Block has %d links" % len(links))
- return links
diff --git a/module/plugins/crypter/SharingmatrixComFolder.py b/module/plugins/crypter/SharingmatrixComFolder.py
deleted file mode 100644
index e16bdf814..000000000
--- a/module/plugins/crypter/SharingmatrixComFolder.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class SharingmatrixComFolder(DeadCrypter):
- __name__ = "SharingmatrixComFolder"
- __type__ = "crypter"
- __version__ = "0.01"
-
- __pattern__ = r'http://(?:www\.)?sharingmatrix\.com/folder/\w+'
-
- __description__ = """Sharingmatrix.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(SharingmatrixComFolder)
diff --git a/module/plugins/crypter/SpeedLoadOrgFolder.py b/module/plugins/crypter/SpeedLoadOrgFolder.py
deleted file mode 100644
index 1d3a7df91..000000000
--- a/module/plugins/crypter/SpeedLoadOrgFolder.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class SpeedLoadOrgFolder(DeadCrypter):
- __name__ = "SpeedLoadOrgFolder"
- __type__ = "crypter"
- __version__ = "0.3"
-
- __pattern__ = r'http://(?:www\.)?speedload\.org/(\d+~f$|folder/\d+/)'
- __config__ = []
-
- __description__ = """Speedload decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(SpeedLoadOrgFolder)
diff --git a/module/plugins/crypter/StealthTo.py b/module/plugins/crypter/StealthTo.py
deleted file mode 100644
index e4da3e7e4..000000000
--- a/module/plugins/crypter/StealthTo.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class StealthTo(DeadCrypter):
- __name__ = "StealthTo"
- __type__ = "crypter"
- __version__ = "0.2"
-
- __pattern__ = r'http://(?:www\.)?stealth\.to/folder/.+'
- __config__ = []
-
- __description__ = """Stealth.to decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("spoob", "spoob@pyload.org")]
-
-
-getInfo = create_getInfo(StealthTo)
diff --git a/module/plugins/crypter/TnyCz.py b/module/plugins/crypter/TnyCz.py
deleted file mode 100644
index 6dde729f5..000000000
--- a/module/plugins/crypter/TnyCz.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-import re
-
-
-class TnyCz(SimpleCrypter):
- __name__ = "TnyCz"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?tny\.cz/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Tny.cz decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- NAME_PATTERN = r'<title>(?P<N>.+) - .+</title>'
-
-
- def getLinks(self):
- m = re.search(r'<a id=\'save_paste\' href="(.+save\.php\?hash=.+)">', self.html)
- return re.findall(".+", self.load(m.group(1), decode=True)) if m else None
diff --git a/module/plugins/crypter/TrailerzoneInfo.py b/module/plugins/crypter/TrailerzoneInfo.py
deleted file mode 100644
index abdb2307e..000000000
--- a/module/plugins/crypter/TrailerzoneInfo.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class TrailerzoneInfo(DeadCrypter):
- __name__ = "TrailerzoneInfo"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?trailerzone\.info/.*?'
- __config__ = []
-
- __description__ = """TrailerZone.info decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("godofdream", "soilfiction@gmail.com")]
-
-
-getInfo = create_getInfo(TrailerzoneInfo)
diff --git a/module/plugins/crypter/TurbobitNetFolder.py b/module/plugins/crypter/TurbobitNetFolder.py
deleted file mode 100644
index 4e07564af..000000000
--- a/module/plugins/crypter/TurbobitNetFolder.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-from module.common.json_layer import json_loads
-
-
-class TurbobitNetFolder(SimpleCrypter):
- __name__ = "TurbobitNetFolder"
- __type__ = "crypter"
- __version__ = "0.05"
-
- __pattern__ = r'http://(?:www\.)?turbobit\.net/download/folder/(?P<ID>\w+)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Turbobit.net folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- NAME_PATTERN = r'src=\'/js/lib/grid/icon/folder.png\'> <span>(?P<N>.+?)</span>'
-
-
- def _getLinks(self, id, page=1):
- gridFile = self.load("http://turbobit.net/downloadfolder/gridFile",
- get={"rootId": id, "rows": 200, "page": page}, decode=True)
- grid = json_loads(gridFile)
-
- if grid['rows']:
- for i in grid['rows']:
- yield i['id']
- for id in self._getLinks(id, page + 1):
- yield id
- else:
- return
-
-
- def getLinks(self):
- id = re.match(self.__pattern__, self.pyfile.url).group("ID")
- fixurl = lambda id: "http://turbobit.net/%s.html" % id
- return map(fixurl, self._getLinks(id))
diff --git a/module/plugins/crypter/TusfilesNetFolder.py b/module/plugins/crypter/TusfilesNetFolder.py
deleted file mode 100644
index 9533a6e76..000000000
--- a/module/plugins/crypter/TusfilesNetFolder.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import math
-import re
-from urlparse import urljoin
-
-from module.plugins.internal.XFSCrypter import XFSCrypter
-
-
-class TusfilesNetFolder(XFSCrypter):
- __name__ = "TusfilesNetFolder"
- __type__ = "crypter"
- __version__ = "0.07"
-
- __pattern__ = r'https?://(?:www\.)?tusfiles\.net/go/(?P<ID>\w+)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Tusfiles.net folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- HOSTER_DOMAIN = "tusfiles.net"
-
- PAGES_PATTERN = r'>\((\d+) \w+\)<'
-
- URL_REPLACEMENTS = [(__pattern__ + ".*", r'https://www.tusfiles.net/go/\g<ID>/')]
-
-
- def loadPage(self, page_n):
- return self.load(urljoin(self.pyfile.url, str(page_n)), decode=True)
-
-
- def handleMultiPages(self):
- pages = re.search(self.PAGES_PATTERN, self.html)
- if pages:
- pages = int(math.ceil(int(pages.group('pages')) / 25.0))
- else:
- return
-
- for p in xrange(2, pages + 1):
- self.html = self.loadPage(p)
- self.links += self.getLinks()
diff --git a/module/plugins/crypter/UlozToFolder.py b/module/plugins/crypter/UlozToFolder.py
deleted file mode 100644
index 59c828174..000000000
--- a/module/plugins/crypter/UlozToFolder.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-from module.plugins.Crypter import Crypter
-
-
-class UlozToFolder(Crypter):
- __name__ = "UlozToFolder"
- __type__ = "crypter"
- __version__ = "0.2"
-
- __pattern__ = r'http://(?:www\.)?(uloz\.to|ulozto\.(cz|sk|net)|bagruj\.cz|zachowajto\.pl)/(m|soubory)/.*'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Uloz.to folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- FOLDER_PATTERN = r'<ul class="profile_files">(.*?)</ul>'
- LINK_PATTERN = r'<br /><a href="/([^"]+)">[^<]+</a>'
- NEXT_PAGE_PATTERN = r'<a class="next " href="/([^"]+)">&nbsp;</a>'
-
-
- def decrypt(self, pyfile):
- html = self.load(pyfile.url)
-
- new_links = []
- for i in xrange(1, 100):
- self.logInfo(_("Fetching links from page %i") % i)
- m = re.search(self.FOLDER_PATTERN, html, re.S)
- if m is None:
- self.error(_("FOLDER_PATTERN not found"))
-
- new_links.extend(re.findall(self.LINK_PATTERN, m.group(1)))
- m = re.search(self.NEXT_PAGE_PATTERN, html)
- if m:
- html = self.load("http://ulozto.net/" + m.group(1))
- else:
- break
- else:
- self.logInfo(_("Limit of 99 pages reached, aborting"))
-
- if new_links:
- self.urls = [map(lambda s: "http://ulozto.net/%s" % s, new_links)]
diff --git a/module/plugins/crypter/UploadableChFolder.py b/module/plugins/crypter/UploadableChFolder.py
deleted file mode 100644
index 070753ac0..000000000
--- a/module/plugins/crypter/UploadableChFolder.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class UploadableChFolder(SimpleCrypter):
- __name__ = "UploadableChFolder"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?uploadable\.ch/list/\w+'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """ Uploadable.ch folder decrypter plugin """
- __license__ = "GPLv3"
- __authors__ = [("guidobelix", "guidobelix@hotmail.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- LINK_PATTERN = r'"(.+?)" class="icon_zipfile">'
- NAME_PATTERN = r'<div class="folder"><span>&nbsp;</span>(?P<N>.+?)</div>'
- OFFLINE_PATTERN = r'We are sorry... The URL you entered cannot be found on the server.'
- TEMP_OFFLINE_PATTERN = r'<div class="icon_err">'
diff --git a/module/plugins/crypter/UploadedToFolder.py b/module/plugins/crypter/UploadedToFolder.py
deleted file mode 100644
index 359ba92d0..000000000
--- a/module/plugins/crypter/UploadedToFolder.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urljoin
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class UploadedToFolder(SimpleCrypter):
- __name__ = "UploadedToFolder"
- __type__ = "crypter"
- __version__ = "0.42"
-
- __pattern__ = r'http://(?:www\.)?(uploaded|ul)\.(to|net)/(f|folder|list)/(?P<id>\w+)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """UploadedTo decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- PLAIN_PATTERN = r'<small class="date"><a href="(?P<plain>[\w/]+)" onclick='
- NAME_PATTERN = r'<title>(?P<N>.+?)<'
-
-
- def getLinks(self):
- m = re.search(self.PLAIN_PATTERN, self.html)
- if m is None:
- self.error(_("PLAIN_PATTERN not found"))
-
- plain_link = urljoin("http://uploaded.net/", m.group('plain'))
- return self.load(plain_link).split('\n')[:-1]
diff --git a/module/plugins/crypter/WiiReloadedOrg.py b/module/plugins/crypter/WiiReloadedOrg.py
deleted file mode 100644
index c3c5b8222..000000000
--- a/module/plugins/crypter/WiiReloadedOrg.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class WiiReloadedOrg(DeadCrypter):
- __name__ = "WiiReloadedOrg"
- __type__ = "crypter"
- __version__ = "0.11"
-
- __pattern__ = r'http://(?:www\.)?wii-reloaded\.org/protect/get\.php\?i=.+'
- __config__ = []
-
- __description__ = """Wii-Reloaded.org decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("hzpz", None)]
-
-
-getInfo = create_getInfo(WiiReloadedOrg)
diff --git a/module/plugins/crypter/WuploadComFolder.py b/module/plugins/crypter/WuploadComFolder.py
deleted file mode 100644
index 873c71fad..000000000
--- a/module/plugins/crypter/WuploadComFolder.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadCrypter import DeadCrypter, create_getInfo
-
-
-class WuploadComFolder(DeadCrypter):
- __name__ = "WuploadComFolder"
- __type__ = "crypter"
- __version__ = "0.01"
-
- __pattern__ = r'http://(?:www\.)?wupload\.com/folder/\w+'
-
- __description__ = """Wupload.com folder decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(WuploadComFolder)
diff --git a/module/plugins/crypter/XFileSharingProFolder.py b/module/plugins/crypter/XFileSharingProFolder.py
deleted file mode 100644
index dffd8909c..000000000
--- a/module/plugins/crypter/XFileSharingProFolder.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.XFSCrypter import XFSCrypter
-
-
-class XFileSharingProFolder(XFSCrypter):
- __name__ = "XFileSharingProFolder"
- __type__ = "crypter"
- __version__ = "0.03"
-
- __pattern__ = r'^unmatchable$'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """XFileSharingPro dummy folder decrypter plugin for hook"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- def _log(self, type, args):
- msg = " | ".join([str(a).strip() for a in args if a])
- logger = getattr(self.log, type)
- logger("%s: %s: %s" % (self.__name__, self.HOSTER_NAME, msg or _("%s MARK" % type.upper())))
-
-
- def init(self):
- super(XFileSharingProFolder, self).init()
-
- self.__pattern__ = self.core.pluginManager.crypterPlugins[self.__name__]['pattern']
-
- self.HOSTER_DOMAIN = re.match(self.__pattern__, self.pyfile.url).group(1).lower()
- self.HOSTER_NAME = "".join([str.capitalize() for str in self.HOSTER_DOMAIN.split('.')])
-
- account = self.core.accountManager.getAccountPlugin(self.HOSTER_NAME)
-
- if account and account.canUse():
- self.account = account
- elif self.account:
- self.account.HOSTER_DOMAIN = self.HOSTER_DOMAIN
- else:
- return
-
- self.user, data = self.account.selectAccount()
- self.req = self.account.getAccountRequest(self.user)
- self.premium = self.account.isPremium(self.user)
diff --git a/module/plugins/crypter/XupPl.py b/module/plugins/crypter/XupPl.py
deleted file mode 100644
index c456723b3..000000000
--- a/module/plugins/crypter/XupPl.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Crypter import Crypter
-
-
-class XupPl(Crypter):
- __name__ = "XupPl"
- __type__ = "crypter"
- __version__ = "0.1"
-
- __pattern__ = r'https?://(?:[^/]*\.)?xup\.pl/.*'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Xup.pl decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("z00nx", "z00nx0@gmail.com")]
-
-
- def decrypt(self, pyfile):
- header = self.load(pyfile.url, just_header=True)
- if 'location' in header:
- self.urls = [header['location']]
- else:
- self.fail(_("Unable to find link"))
diff --git a/module/plugins/crypter/YoutubeBatch.py b/module/plugins/crypter/YoutubeBatch.py
deleted file mode 100644
index c66f94753..000000000
--- a/module/plugins/crypter/YoutubeBatch.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urljoin
-
-from module.common.json_layer import json_loads
-from module.plugins.Crypter import Crypter
-from module.utils import save_join
-
-
-class YoutubeBatch(Crypter):
- __name__ = "YoutubeBatch"
- __type__ = "crypter"
- __version__ = "1.01"
-
- __pattern__ = r'https?://(?:www\.|m\.)?youtube\.com/(?P<TYPE>user|playlist|view_play_list)(/|.*?[?&](?:list|p)=)(?P<ID>[\w-]+)'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True),
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True),
- ("likes", "bool", "Grab user (channel) liked videos", False),
- ("favorites", "bool", "Grab user (channel) favorite videos", False),
- ("uploads", "bool", "Grab channel unplaylisted videos", True)]
-
- __description__ = """Youtube.com channel & playlist decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- API_KEY = "AIzaSyCKnWLNlkX-L4oD1aEzqqhRw1zczeD6_k0"
-
-
- def api_response(self, ref, req):
- req.update({"key": self.API_KEY})
- url = urljoin("https://www.googleapis.com/youtube/v3/", ref)
- page = self.load(url, get=req)
- return json_loads(page)
-
-
- def getChannel(self, user):
- channels = self.api_response("channels", {"part": "id,snippet,contentDetails", "forUsername": user, "maxResults": "50"})
- if channels['items']:
- channel = channels['items'][0]
- return {"id": channel['id'],
- "title": channel['snippet']['title'],
- "relatedPlaylists": channel['contentDetails']['relatedPlaylists'],
- "user": user} # One lone channel for user?
-
-
- def getPlaylist(self, p_id):
- playlists = self.api_response("playlists", {"part": "snippet", "id": p_id})
- if playlists['items']:
- playlist = playlists['items'][0]
- return {"id": p_id,
- "title": playlist['snippet']['title'],
- "channelId": playlist['snippet']['channelId'],
- "channelTitle": playlist['snippet']['channelTitle']}
-
-
- def _getPlaylists(self, id, token=None):
- req = {"part": "id", "maxResults": "50", "channelId": id}
- if token:
- req.update({"pageToken": token})
-
- playlists = self.api_response("playlists", req)
-
- for playlist in playlists['items']:
- yield playlist['id']
-
- if "nextPageToken" in playlists:
- for item in self._getPlaylists(id, playlists['nextPageToken']):
- yield item
-
-
- def getPlaylists(self, ch_id):
- return map(self.getPlaylist, self._getPlaylists(ch_id))
-
-
- def _getVideosId(self, id, token=None):
- req = {"part": "contentDetails", "maxResults": "50", "playlistId": id}
- if token:
- req.update({"pageToken": token})
-
- playlist = self.api_response("playlistItems", req)
-
- for item in playlist['items']:
- yield item['contentDetails']['videoId']
-
- if "nextPageToken" in playlist:
- for item in self._getVideosId(id, playlist['nextPageToken']):
- yield item
-
-
- def getVideosId(self, p_id):
- return list(self._getVideosId(p_id))
-
-
- def decrypt(self, pyfile):
- m = re.match(self.__pattern__, pyfile.url)
- m_id = m.group("ID")
- m_type = m.group("TYPE")
-
- if m_type == "user":
- self.logDebug("Url recognized as Channel")
- user = m_id
- channel = self.getChannel(user)
-
- if channel:
- playlists = self.getPlaylists(channel['id'])
- self.logDebug("%s playlist\s found on channel \"%s\"" % (len(playlists), channel['title']))
-
- relatedplaylist = {p_name: self.getPlaylist(p_id) for p_name, p_id in channel['relatedPlaylists'].iteritems()}
- self.logDebug("Channel's related playlists found = %s" % relatedplaylist.keys())
-
- relatedplaylist['uploads']['title'] = "Unplaylisted videos"
- relatedplaylist['uploads']['checkDups'] = True #: checkDups flag
-
- for p_name, p_data in relatedplaylist.iteritems():
- if self.getConfig(p_name):
- p_data['title'] += " of " + user
- playlists.append(p_data)
- else:
- playlists = []
- else:
- self.logDebug("Url recognized as Playlist")
- playlists = [self.getPlaylist(m_id)]
-
- if not playlists:
- self.fail(_("No playlist available"))
-
- addedvideos = []
- urlize = lambda x: "https://www.youtube.com/watch?v=" + x
- for p in playlists:
- p_name = p['title']
- p_videos = self.getVideosId(p['id'])
- p_folder = save_join(self.config['general']['download_folder'], p['channelTitle'], p_name)
- self.logDebug("%s video\s found on playlist \"%s\"" % (len(p_videos), p_name))
-
- if not p_videos:
- continue
- elif "checkDups" in p:
- p_urls = [urlize(v_id) for v_id in p_videos if v_id not in addedvideos]
- self.logDebug("%s video\s available on playlist \"%s\" after duplicates cleanup" % (len(p_urls), p_name))
- else:
- p_urls = map(urlize, p_videos)
-
- self.packages.append((p_name, p_urls, p_folder)) #: folder is NOT recognized by pyload 0.4.9!
-
- addedvideos.extend(p_videos)
diff --git a/module/plugins/crypter/__init__.py b/module/plugins/crypter/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/plugins/crypter/__init__.py
+++ /dev/null
diff --git a/module/plugins/hooks/AlldebridCom.py b/module/plugins/hooks/AlldebridCom.py
deleted file mode 100644
index 2d3c8aad7..000000000
--- a/module/plugins/hooks/AlldebridCom.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class AlldebridCom(MultiHoster):
- __name__ = "AlldebridCom"
- __type__ = "hook"
- __version__ = "0.13"
-
- __config__ = [("https", "bool", "Enable HTTPS", False),
- ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", ""),
- ("unloadFailing", "bool", "Revert to stanard download if download fails", False),
- ("interval", "int", "Reload interval in hours (0 to disable)", 24)]
-
- __description__ = """Alldebrid.com hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Andy Voigt", "spamsales@online.de")]
-
-
- def getHoster(self):
- https = "https" if self.getConfig("https") else "http"
- page = getURL(https + "://www.alldebrid.com/api.php", get={'action': "get_host"}).replace("\"", "").strip()
-
- return [x.strip() for x in page.split(",") if x.strip()]
diff --git a/module/plugins/hooks/BypassCaptcha.py b/module/plugins/hooks/BypassCaptcha.py
deleted file mode 100644
index 7e1ea6424..000000000
--- a/module/plugins/hooks/BypassCaptcha.py
+++ /dev/null
@@ -1,139 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from pycurl import FORM_FILE, LOW_SPEED_TIME
-from thread import start_new_thread
-
-from module.network.HTTPRequest import BadHeader
-from module.network.RequestFactory import getURL, getRequest
-from module.plugins.Hook import Hook
-
-
-class BypassCaptchaException(Exception):
-
- def __init__(self, err):
- self.err = err
-
-
- def getCode(self):
- return self.err
-
-
- def __str__(self):
- return "<BypassCaptchaException %s>" % self.err
-
-
- def __repr__(self):
- return "<BypassCaptchaException %s>" % self.err
-
-
-class BypassCaptcha(Hook):
- __name__ = "BypassCaptcha"
- __type__ = "hook"
- __version__ = "0.04"
-
- __config__ = [("force", "bool", "Force BC even if client is connected", False),
- ("passkey", "password", "Passkey", "")]
-
- __description__ = """Send captchas to BypassCaptcha.com"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("Godofdream", "soilfcition@gmail.com"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- PYLOAD_KEY = "4f771155b640970d5607f919a615bdefc67e7d32"
-
- SUBMIT_URL = "http://bypasscaptcha.com/upload.php"
- RESPOND_URL = "http://bypasscaptcha.com/check_value.php"
- GETCREDITS_URL = "http://bypasscaptcha.com/ex_left.php"
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
-
-
- def getCredits(self):
- res = getURL(self.GETCREDITS_URL, post={"key": self.getConfig("passkey")})
-
- data = dict([x.split(' ', 1) for x in res.splitlines()])
- return int(data['Left'])
-
-
- def submit(self, captcha, captchaType="file", match=None):
- req = getRequest()
-
- #raise timeout threshold
- req.c.setopt(LOW_SPEED_TIME, 80)
-
- try:
- res = req.load(self.SUBMIT_URL,
- post={'vendor_key': self.PYLOAD_KEY,
- 'key': self.getConfig("passkey"),
- 'gen_task_id': "1",
- 'file': (FORM_FILE, captcha)},
- multipart=True)
- finally:
- req.close()
-
- data = dict([x.split(' ', 1) for x in res.splitlines()])
- if not data or "Value" not in data:
- raise BypassCaptchaException(res)
-
- result = data['Value']
- ticket = data['TaskId']
- self.logDebug("Result %s : %s" % (ticket, result))
-
- return ticket, result
-
-
- def respond(self, ticket, success):
- try:
- res = getURL(self.RESPOND_URL, post={"task_id": ticket, "key": self.getConfig("passkey"),
- "cv": 1 if success else 0})
- except BadHeader, e:
- self.logError(_("Could not send response"), e)
-
-
- def newCaptchaTask(self, task):
- if "service" in task.data:
- return False
-
- if not task.isTextual():
- return False
-
- if not self.getConfig("passkey"):
- return False
-
- if self.core.isClientConnected() and not self.getConfig("force"):
- return False
-
- if self.getCredits() > 0:
- task.handler.append(self)
- task.data['service'] = self.__name__
- task.setWaiting(100)
- start_new_thread(self.processCaptcha, (task,))
-
- else:
- self.logInfo(_("Your %s account has not enough credits") % self.__name__)
-
-
- def captchaCorrect(self, task):
- if task.data['service'] == self.__name__ and "ticket" in task.data:
- self.respond(task.data['ticket'], True)
-
-
- def captchaInvalid(self, task):
- if task.data['service'] == self.__name__ and "ticket" in task.data:
- self.respond(task.data['ticket'], False)
-
-
- def processCaptcha(self, task):
- c = task.captchaFile
- try:
- ticket, result = self.submit(c)
- except BypassCaptchaException, e:
- task.error = e.getCode()
- return
-
- task.data['ticket'] = ticket
- task.setResult(result)
diff --git a/module/plugins/hooks/Captcha9kw.py b/module/plugins/hooks/Captcha9kw.py
deleted file mode 100755
index ead8aec9a..000000000
--- a/module/plugins/hooks/Captcha9kw.py
+++ /dev/null
@@ -1,256 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-import re
-
-from base64 import b64encode
-from thread import start_new_thread
-from time import sleep
-
-from module.network.HTTPRequest import BadHeader
-from module.network.RequestFactory import getURL
-
-from module.plugins.Hook import Hook
-
-
-class Captcha9kw(Hook):
- __name__ = "Captcha9kw"
- __type__ = "hook"
- __version__ = "0.25"
-
- __config__ = [("activated" , "bool" , "Activated" , True ),
- ("ssl" , "bool" , "Use HTTPS" , True ),
- ("force" , "bool" , "Force captcha resolving even if client is connected" , True ),
- ("confirm" , "bool" , "Confirm Captcha (cost +6 credits)" , False ),
- ("captchaperhour", "int" , "Captcha per hour" , "9999" ),
- ("captchapermin" , "int" , "Captcha per minute" , "9999" ),
- ("prio" , "int" , "Priority (max 10)(cost +0 -> +10 credits)" , "0" ),
- ("queue" , "int" , "Max. Queue (max 999)" , "50" ),
- ("hoster_options", "string" , "Hoster options (format: pluginname:prio=1:selfsolfe=1:confirm=1:timeout=900|...)", "ShareonlineBiz:prio=0:timeout=999 | UploadedTo:prio=0:timeout=999"),
- ("selfsolve" , "bool" , "Selfsolve (manually solve your captcha in your 9kw client if active)" , "0" ),
- ("passkey" , "password", "API key" , "" ),
- ("timeout" , "int" , "Timeout in seconds (min 60, max 3999)" , "900" )]
-
- __description__ = """Send captchas to 9kw.eu"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- API_URL = "http://www.9kw.eu/index.cgi"
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
- if self.getConfig("ssl"):
- self.API_URL = self.API_URL.replace("http://", "https://")
-
-
- def getCredits(self):
- res = getURL(self.API_URL,
- get={'apikey': self.getConfig("passkey"),
- 'pyload': "1",
- 'source': "pyload",
- 'action': "usercaptchaguthaben"})
-
- if res.isdigit():
- self.logInfo(_("%s credits left") % res)
- credits = self.info['credits'] = int(res)
- return credits
- else:
- self.logError(res)
- return 0
-
-
- def _processCaptcha(self, task):
- try:
- with open(task.captchaFile, 'rb') as f:
- data = f.read()
-
- except IOError, e:
- self.logError(e)
- return
-
- data = b64encode(data)
- mouse = 1 if task.isPositional() else 0
- pluginname = re.search(r'_([^_]*)_\d+.\w+', task.captchaFile).group(1)
-
- option = {'min' : 2,
- 'max' : 50,
- 'phrase' : 0,
- 'numeric' : 0,
- 'case_sensitive': 0,
- 'math' : 0,
- 'prio' : min(max(self.getConfig("prio"), 0), 10),
- 'confirm' : self.getConfig("confirm"),
- 'timeout' : min(max(self.getConfig("timeout"), 300), 3999),
- 'selfsolve' : self.getConfig("selfsolve"),
- 'cph' : self.getConfig("captchaperhour"),
- 'cpm' : self.getConfig("captchapermin")}
-
- for opt in str(self.getConfig("hoster_options").split('|')):
-
- details = map(str.strip, opt.split(':'))
-
- if not details or details[0].lower() != pluginname.lower():
- continue
-
- for d in details:
- hosteroption = d.split("=")
-
- if len(hosteroption) < 2 or not hosteroption[1].isdigit():
- continue
-
- o = hosteroption[0].lower()
- if o in option:
- option[o] = hosteroption[1]
-
- break
-
- post_data = {'apikey' : self.getConfig("passkey"),
- 'prio' : option['prio'],
- 'confirm' : option['confirm'],
- 'maxtimeout' : option['timeout'],
- 'selfsolve' : option['selfsolve'],
- 'captchaperhour': option['cph'],
- 'captchapermin' : option['cpm'],
- 'case-sensitive': option['case_sensitive'],
- 'min_len' : option['min'],
- 'max_len' : option['max'],
- 'phrase' : option['phrase'],
- 'numeric' : option['numeric'],
- 'math' : option['math'],
- 'oldsource' : pluginname,
- 'pyload' : "1",
- 'source' : "pyload",
- 'base64' : "1",
- 'mouse' : mouse,
- 'file-upload-01': data,
- 'action' : "usercaptchaupload"}
-
- for _i in xrange(5):
- try:
- res = getURL(self.API_URL, post=post_data)
- except BadHeader, e:
- sleep(3)
- else:
- if res and res.isdigit():
- break
- else:
- self.logError(_("Bad upload: %s") % res)
- return
-
- self.logDebug(_("NewCaptchaID ticket: %s") % res, task.captchaFile)
-
- task.data["ticket"] = res
-
- for _i in xrange(int(self.getConfig("timeout") / 5)):
- result = getURL(self.API_URL,
- get={'apikey': self.getConfig("passkey"),
- 'id' : res,
- 'pyload': "1",
- 'info' : "1",
- 'source': "pyload",
- 'action': "usercaptchacorrectdata"})
-
- if not result or result == "NO DATA":
- sleep(5)
- else:
- break
- else:
- self.logDebug("Could not send request: %s" % res)
- result = None
-
- self.logInfo(_("Captcha result for ticket %s: %s") % (res, result))
-
- task.setResult(result)
-
-
- def newCaptchaTask(self, task):
- if not task.isTextual() and not task.isPositional():
- return
-
- if not self.getConfig("passkey"):
- return
-
- if self.core.isClientConnected() and not self.getConfig("force"):
- return
-
- credits = self.getCredits()
-
- if not credits:
- self.logError(_("Your captcha 9kw.eu account has not enough credits"))
- return
-
- queue = min(self.getConfig("queue"), 999)
- timeout = min(max(self.getConfig("timeout"), 300), 3999)
- pluginname = re.search(r'_([^_]*)_\d+.\w+', task.captchaFile).group(1)
-
- for _i in xrange(5):
- servercheck = getURL("http://www.9kw.eu/grafik/servercheck.txt")
- if queue < re.search(r'queue=(\d+)', servercheck).group(1):
- break
-
- sleep(10)
- else:
- self.fail(_("Too many captchas in queue"))
-
- for opt in str(self.getConfig("hoster_options").split('|')):
- details = map(str.strip, opt.split(':'))
-
- if not details or details[0].lower() != pluginname.lower():
- continue
-
- for d in details:
- hosteroption = d.split("=")
-
- if (len(hosteroption) > 1
- and hosteroption[0].lower() == 'timeout'
- and hosteroption[1].isdigit()):
- timeout = int(hosteroption[1])
-
- break
-
- task.handler.append(self)
-
- task.setWaiting(timeout)
-
- self._processCaptcha(task)
-
-
- def _captchaResponse(self, task, correct):
- type = "correct" if correct else "refund"
-
- if 'ticket' not in task.data:
- self.logDebug("No CaptchaID for %s request (task: %s)" % (type, task))
- return
-
- passkey = self.getConfig("passkey")
-
- for _i in xrange(3):
- res = getURL(self.API_URL,
- get={'action' : "usercaptchacorrectback",
- 'apikey' : passkey,
- 'api_key': passkey,
- 'correct': "1" if correct else "2",
- 'pyload' : "1",
- 'source' : "pyload",
- 'id' : task.data["ticket"]})
-
- self.logDebug("Request %s: %s" % (type, res))
-
- if res == "OK":
- break
-
- sleep(5)
- else:
- self.logDebug("Could not send %s request: %s" % (type, res))
-
-
- def captchaCorrect(self, task):
- self._captchaResponse(task, True)
-
-
- def captchaInvalid(self, task):
- self._captchaResponse(task, False)
diff --git a/module/plugins/hooks/CaptchaBrotherhood.py b/module/plugins/hooks/CaptchaBrotherhood.py
deleted file mode 100644
index 2ebeb1734..000000000
--- a/module/plugins/hooks/CaptchaBrotherhood.py
+++ /dev/null
@@ -1,171 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-import StringIO
-import pycurl
-
-try:
- from PIL import Image
-except ImportError:
- import Image
-
-from thread import start_new_thread
-from time import sleep
-from urllib import urlencode
-
-from module.network.RequestFactory import getURL, getRequest
-from module.plugins.Hook import Hook
-
-
-class CaptchaBrotherhoodException(Exception):
-
- def __init__(self, err):
- self.err = err
-
-
- def getCode(self):
- return self.err
-
-
- def __str__(self):
- return "<CaptchaBrotherhoodException %s>" % self.err
-
-
- def __repr__(self):
- return "<CaptchaBrotherhoodException %s>" % self.err
-
-
-class CaptchaBrotherhood(Hook):
- __name__ = "CaptchaBrotherhood"
- __type__ = "hook"
- __version__ = "0.05"
-
- __config__ = [("username", "str", "Username", ""),
- ("force", "bool", "Force CT even if client is connected", False),
- ("passkey", "password", "Password", "")]
-
- __description__ = """Send captchas to CaptchaBrotherhood.com"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- API_URL = "http://www.captchabrotherhood.com/"
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
-
-
- def getCredits(self):
- res = getURL(self.API_URL + "askCredits.aspx",
- get={"username": self.getConfig("username"), "password": self.getConfig("passkey")})
- if not res.startswith("OK"):
- raise CaptchaBrotherhoodException(res)
- else:
- credits = int(res[3:])
- self.logInfo(_("%d credits left") % credits)
- self.info['credits'] = credits
- return credits
-
-
- def submit(self, captcha, captchaType="file", match=None):
- try:
- img = Image.open(captcha)
- output = StringIO.StringIO()
- self.logDebug("CAPTCHA IMAGE", img, img.format, img.mode)
- if img.format in ("GIF", "JPEG"):
- img.save(output, img.format)
- else:
- if img.mode != "RGB":
- img = img.convert("RGB")
- img.save(output, "JPEG")
- data = output.getvalue()
- output.close()
- except Exception, e:
- raise CaptchaBrotherhoodException("Reading or converting captcha image failed: %s" % e)
-
- req = getRequest()
-
- url = "%ssendNewCaptcha.aspx?%s" % (self.API_URL,
- urlencode({"username": self.getConfig("username"),
- "password": self.getConfig("passkey"),
- "captchaSource": "pyLoad",
- "timeout": "80"}))
-
- req.c.setopt(pycurl.URL, url)
- req.c.setopt(pycurl.POST, 1)
- req.c.setopt(pycurl.POSTFIELDS, data)
- req.c.setopt(pycurl.HTTPHEADER, ["Content-Type: text/html"])
-
- try:
- req.c.perform()
- res = req.getResponse()
- except Exception, e:
- raise CaptchaBrotherhoodException("Submit captcha image failed")
-
- req.close()
-
- if not res.startswith("OK"):
- raise CaptchaBrotherhoodException(res[1])
-
- ticket = res[3:]
-
- for _i in xrange(15):
- sleep(5)
- res = self.get_api("askCaptchaResult", ticket)
- if res.startswith("OK-answered"):
- return ticket, res[12:]
-
- raise CaptchaBrotherhoodException("No solution received in time")
-
-
- def get_api(self, api, ticket):
- res = getURL("%s%s.aspx" % (self.API_URL, api),
- get={"username": self.getConfig("username"),
- "password": self.getConfig("passkey"),
- "captchaID": ticket})
- if not res.startswith("OK"):
- raise CaptchaBrotherhoodException("Unknown response: %s" % res)
-
- return res
-
-
- def newCaptchaTask(self, task):
- if "service" in task.data:
- return False
-
- if not task.isTextual():
- return False
-
- if not self.getConfig("username") or not self.getConfig("passkey"):
- return False
-
- if self.core.isClientConnected() and not self.getConfig("force"):
- return False
-
- if self.getCredits() > 10:
- task.handler.append(self)
- task.data['service'] = self.__name__
- task.setWaiting(100)
- start_new_thread(self.processCaptcha, (task,))
- else:
- self.logInfo(_("Your CaptchaBrotherhood Account has not enough credits"))
-
-
- def captchaInvalid(self, task):
- if task.data['service'] == self.__name__ and "ticket" in task.data:
- res = self.get_api("complainCaptcha", task.data['ticket'])
-
-
- def processCaptcha(self, task):
- c = task.captchaFile
- try:
- ticket, result = self.submit(c)
- except CaptchaBrotherhoodException, e:
- task.error = e.getCode()
- return
-
- task.data['ticket'] = ticket
- task.setResult(result)
diff --git a/module/plugins/hooks/Checksum.py b/module/plugins/hooks/Checksum.py
deleted file mode 100644
index eeda2d849..000000000
--- a/module/plugins/hooks/Checksum.py
+++ /dev/null
@@ -1,185 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-import hashlib
-import re
-import zlib
-
-from os import remove
-from os.path import getsize, isfile, splitext
-
-from module.plugins.Hook import Hook
-from module.utils import save_join, fs_encode
-
-
-def computeChecksum(local_file, algorithm):
- if algorithm in getattr(hashlib, "algorithms", ("md5", "sha1", "sha224", "sha256", "sha384", "sha512")):
- h = getattr(hashlib, algorithm)()
-
- with open(local_file, 'rb') as f:
- for chunk in iter(lambda: f.read(128 * h.block_size), ''):
- h.update(chunk)
-
- return h.hexdigest()
-
- elif algorithm in ("adler32", "crc32"):
- hf = getattr(zlib, algorithm)
- last = 0
-
- with open(local_file, 'rb') as f:
- for chunk in iter(lambda: f.read(8192), ''):
- last = hf(chunk, last)
-
- return "%x" % last
-
- else:
- return None
-
-
-class Checksum(Hook):
- __name__ = "Checksum"
- __type__ = "hook"
- __version__ = "0.14"
-
- __config__ = [("check_checksum", "bool", "Check checksum? (If False only size will be verified)", True),
- ("check_action", "fail;retry;nothing", "What to do if check fails?", "retry"),
- ("max_tries", "int", "Number of retries", 2),
- ("retry_action", "fail;nothing", "What to do if all retries fail?", "fail"),
- ("wait_time", "int", "Time to wait before each retry (seconds)", 1)]
-
- __description__ = """Verify downloaded file size and checksum"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("Walter Purcaro", "vuolter@gmail.com"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- methods = {'sfv': 'crc32', 'crc': 'crc32', 'hash': 'md5'}
- regexps = {'sfv': r'^(?P<name>[^;].+)\s+(?P<hash>[0-9A-Fa-f]{8})$',
- 'md5': r'^(?P<name>[0-9A-Fa-f]{32}) (?P<file>.+)$',
- 'crc': r'filename=(?P<name>.+)\nsize=(?P<size>\d+)\ncrc32=(?P<hash>[0-9A-Fa-f]{8})$',
- 'default': r'^(?P<hash>[0-9A-Fa-f]+)\s+\*?(?P<name>.+)$'}
-
-
- def coreReady(self):
- if not self.getConfig("check_checksum"):
- self.logInfo(_("Checksum validation is disabled in plugin configuration"))
-
-
- def setup(self):
- self.algorithms = sorted(
- getattr(hashlib, "algorithms", ("md5", "sha1", "sha224", "sha256", "sha384", "sha512")), reverse=True)
- self.algorithms.extend(["crc32", "adler32"])
- self.formats = self.algorithms + ["sfv", "crc", "hash"]
-
-
- def downloadFinished(self, pyfile):
- """
- Compute checksum for the downloaded file and compare it with the hash provided by the hoster.
- pyfile.plugin.check_data should be a dictionary which can contain:
- a) if known, the exact filesize in bytes (e.g. "size": 123456789)
- b) hexadecimal hash string with algorithm name as key (e.g. "md5": "d76505d0869f9f928a17d42d66326307")
- """
- if hasattr(pyfile.plugin, "check_data") and isinstance(pyfile.plugin.check_data, dict):
- data = pyfile.plugin.check_data.copy()
-
- elif hasattr(pyfile.plugin, "api_data") and isinstance(pyfile.plugin.api_data, dict):
- data = pyfile.plugin.api_data.copy()
-
- # elif hasattr(pyfile.plugin, "info") and isinstance(pyfile.plugin.info, dict):
- # data = pyfile.plugin.info.copy()
-
- else:
- return
-
- self.logDebug(data)
-
- if not pyfile.plugin.lastDownload:
- self.checkFailed(pyfile, None, "No file downloaded")
-
- local_file = fs_encode(pyfile.plugin.lastDownload)
- #download_folder = self.config['general']['download_folder']
- #local_file = fs_encode(save_join(download_folder, pyfile.package().folder, pyfile.name))
-
- if not isfile(local_file):
- self.checkFailed(pyfile, None, "File does not exist")
-
- # validate file size
- if "size" in data:
- api_size = int(data['size'])
- file_size = getsize(local_file)
- if api_size != file_size:
- self.logWarning(_("File %s has incorrect size: %d B (%d expected)") % (pyfile.name, file_size, api_size))
- self.checkFailed(pyfile, local_file, "Incorrect file size")
- del data['size']
-
- # validate checksum
- if data and self.getConfig("check_checksum"):
- if "checksum" in data:
- data['md5'] = data['checksum']
-
- for key in self.algorithms:
- if key in data:
- checksum = computeChecksum(local_file, key.replace("-", "").lower())
- if checksum:
- if checksum == data[key].lower():
- self.logInfo(_('File integrity of "%s" verified by %s checksum (%s)') %
- (pyfile.name, key.upper(), checksum))
- break
- else:
- self.logWarning(_("%s checksum for file %s does not match (%s != %s)") %
- (key.upper(), pyfile.name, checksum, data[key]))
- self.checkFailed(pyfile, local_file, "Checksums do not match")
- else:
- self.logWarning(_("Unsupported hashing algorithm"), key.upper())
- else:
- self.logWarning(_("Unable to validate checksum for file: ") + pyfile.name)
-
-
- def checkFailed(self, pyfile, local_file, msg):
- check_action = self.getConfig("check_action")
- if check_action == "retry":
- max_tries = self.getConfig("max_tries")
- retry_action = self.getConfig("retry_action")
- if pyfile.plugin.retries < max_tries:
- if local_file:
- remove(local_file)
- pyfile.plugin.retry(max_tries, self.getConfig("wait_time"), msg)
- elif retry_action == "nothing":
- return
- elif check_action == "nothing":
- return
- pyfile.plugin.fail(reason=msg)
-
-
- def packageFinished(self, pypack):
- download_folder = save_join(self.config['general']['download_folder'], pypack.folder, "")
-
- for link in pypack.getChildren().itervalues():
- file_type = splitext(link['name'])[1][1:].lower()
-
- if file_type not in self.formats:
- continue
-
- hash_file = fs_encode(save_join(download_folder, link['name']))
- if not isfile(hash_file):
- self.logWarning(_("File not found"), link['name'])
- continue
-
- with open(hash_file) as f:
- text = f.read()
-
- for m in re.finditer(self.regexps.get(file_type, self.regexps['default']), text):
- data = m.groupdict()
- self.logDebug(link['name'], data)
-
- local_file = fs_encode(save_join(download_folder, data['name']))
- algorithm = self.methods.get(file_type, file_type)
- checksum = computeChecksum(local_file, algorithm)
- if checksum == data['hash']:
- self.logInfo(_('File integrity of "%s" verified by %s checksum (%s)') %
- (data['name'], algorithm, checksum))
- else:
- self.logWarning(_("%s checksum for file %s does not match (%s != %s)") %
- (algorithm, data['name'], checksum, data['hash']))
diff --git a/module/plugins/hooks/ClickAndLoad.py b/module/plugins/hooks/ClickAndLoad.py
deleted file mode 100644
index c9c0f60c0..000000000
--- a/module/plugins/hooks/ClickAndLoad.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import socket
-import thread
-
-from module.plugins.Hook import Hook
-
-
-def proxy(self, *settings):
- thread.start_new_thread(server, (self,) + settings)
- lock = thread.allocate_lock()
- lock.acquire()
- lock.acquire()
-
-
-def server(self, *settings):
- try:
- dock_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- dock_socket.bind((settings[0], settings[2]))
- dock_socket.listen(5)
- while True:
- client_socket = dock_socket.accept()[0]
- server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- server_socket.connect(("127.0.0.1", settings[1]))
- thread.start_new_thread(forward, (client_socket, server_socket))
- thread.start_new_thread(forward, (server_socket, client_socket))
- except socket.error, e:
- if hasattr(e, "errno"):
- errno = e.errno
- else:
- errno = e.args[0]
-
- if errno == 98:
- self.logWarning(_("Click'N'Load: Port 9666 already in use"))
- return
- thread.start_new_thread(server, (self,) + settings)
- except:
- thread.start_new_thread(server, (self,) + settings)
-
-
-def forward(source, destination):
- string = ' '
- while string:
- string = source.recv(1024)
- if string:
- destination.sendall(string)
- else:
- #source.shutdown(socket.SHUT_RD)
- destination.shutdown(socket.SHUT_WR)
-
-
-class ClickAndLoad(Hook):
- __name__ = "ClickAndLoad"
- __type__ = "hook"
- __version__ = "0.22"
-
- __config__ = [("activated", "bool", "Activated", True),
- ("extern", "bool", "Allow external link adding", False)]
-
- __description__ = """Click'N'Load hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.de"),
- ("mkaay", "mkaay@mkaay.de")]
-
-
- def coreReady(self):
- self.port = int(self.config['webinterface']['port'])
- if self.config['webinterface']['activated']:
- try:
- if self.getConfig("extern"):
- ip = "0.0.0.0"
- else:
- ip = "127.0.0.1"
-
- thread.start_new_thread(proxy, (self, ip, self.port, 9666))
- except:
- self.logError(_("ClickAndLoad port already in use"))
diff --git a/module/plugins/hooks/DeathByCaptcha.py b/module/plugins/hooks/DeathByCaptcha.py
deleted file mode 100644
index df09769ce..000000000
--- a/module/plugins/hooks/DeathByCaptcha.py
+++ /dev/null
@@ -1,218 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-import re
-
-from base64 import b64encode
-from pycurl import FORM_FILE, HTTPHEADER
-from thread import start_new_thread
-from time import sleep
-
-from module.common.json_layer import json_loads
-from module.network.HTTPRequest import BadHeader
-from module.network.RequestFactory import getRequest
-from module.plugins.Hook import Hook
-
-
-class DeathByCaptchaException(Exception):
- DBC_ERRORS = {'not-logged-in': 'Access denied, check your credentials',
- 'invalid-credentials': 'Access denied, check your credentials',
- 'banned': 'Access denied, account is suspended',
- 'insufficient-funds': 'Insufficient account balance to decrypt CAPTCHA',
- 'invalid-captcha': 'CAPTCHA is not a valid image',
- 'service-overload': 'CAPTCHA was rejected due to service overload, try again later',
- 'invalid-request': 'Invalid request',
- 'timed-out': 'No CAPTCHA solution received in time'}
-
-
- def __init__(self, err):
- self.err = err
-
-
- def getCode(self):
- return self.err
-
-
- def getDesc(self):
- if self.err in self.DBC_ERRORS.keys():
- return self.DBC_ERRORS[self.err]
- else:
- return self.err
-
-
- def __str__(self):
- return "<DeathByCaptchaException %s>" % self.err
-
-
- def __repr__(self):
- return "<DeathByCaptchaException %s>" % self.err
-
-
-class DeathByCaptcha(Hook):
- __name__ = "DeathByCaptcha"
- __type__ = "hook"
- __version__ = "0.03"
-
- __config__ = [("username", "str", "Username", ""),
- ("passkey", "password", "Password", ""),
- ("force", "bool", "Force DBC even if client is connected", False)]
-
- __description__ = """Send captchas to DeathByCaptcha.com"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- API_URL = "http://api.dbcapi.me/api/"
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
-
-
- def call_api(self, api="captcha", post=False, multipart=False):
- req = getRequest()
- req.c.setopt(HTTPHEADER, ["Accept: application/json", "User-Agent: pyLoad %s" % self.core.version])
-
- if post:
- if not isinstance(post, dict):
- post = {}
- post.update({"username": self.getConfig("username"),
- "password": self.getConfig("passkey")})
-
- res = None
- try:
- json = req.load("%s%s" % (self.API_URL, api),
- post=post,
- multipart=multipart)
- self.logDebug(json)
- res = json_loads(json)
-
- if "error" in res:
- raise DeathByCaptchaException(res['error'])
- elif "status" not in res:
- raise DeathByCaptchaException(str(res))
-
- except BadHeader, e:
- if 403 == e.code:
- raise DeathByCaptchaException('not-logged-in')
- elif 413 == e.code:
- raise DeathByCaptchaException('invalid-captcha')
- elif 503 == e.code:
- raise DeathByCaptchaException('service-overload')
- elif e.code in (400, 405):
- raise DeathByCaptchaException('invalid-request')
- else:
- raise
-
- finally:
- req.close()
-
- return res
-
-
- def getCredits(self):
- res = self.call_api("user", True)
-
- if 'is_banned' in res and res['is_banned']:
- raise DeathByCaptchaException('banned')
- elif 'balance' in res and 'rate' in res:
- self.info.update(res)
- else:
- raise DeathByCaptchaException(res)
-
-
- def getStatus(self):
- res = self.call_api("status", False)
-
- if 'is_service_overloaded' in res and res['is_service_overloaded']:
- raise DeathByCaptchaException('service-overload')
-
-
- def submit(self, captcha, captchaType="file", match=None):
- #workaround multipart-post bug in HTTPRequest.py
- if re.match("^\w*$", self.getConfig("passkey")):
- multipart = True
- data = (FORM_FILE, captcha)
- else:
- multipart = False
- with open(captcha, 'rb') as f:
- data = f.read()
- data = "base64:" + b64encode(data)
-
- res = self.call_api("captcha", {"captchafile": data}, multipart)
-
- if "captcha" not in res:
- raise DeathByCaptchaException(res)
- ticket = res['captcha']
-
- for _i in xrange(24):
- sleep(5)
- res = self.call_api("captcha/%d" % ticket, False)
- if res['text'] and res['is_correct']:
- break
- else:
- raise DeathByCaptchaException('timed-out')
-
- result = res['text']
- self.logDebug("Result %s : %s" % (ticket, result))
-
- return ticket, result
-
-
- def newCaptchaTask(self, task):
- if "service" in task.data:
- return False
-
- if not task.isTextual():
- return False
-
- if not self.getConfig("username") or not self.getConfig("passkey"):
- return False
-
- if self.core.isClientConnected() and not self.getConfig("force"):
- return False
-
- try:
- self.getStatus()
- self.getCredits()
- except DeathByCaptchaException, e:
- self.logError(e.getDesc())
- return False
-
- balance, rate = self.info['balance'], self.info['rate']
- self.logInfo(_("Account balance"),
- _("US$%.3f (%d captchas left at %.2f cents each)") % (balance / 100,
- balance // rate, rate))
-
- if balance > rate:
- task.handler.append(self)
- task.data['service'] = self.__name__
- task.setWaiting(180)
- start_new_thread(self.processCaptcha, (task,))
-
-
- def captchaInvalid(self, task):
- if task.data['service'] == self.__name__ and "ticket" in task.data:
- try:
- res = self.call_api("captcha/%d/report" % task.data['ticket'], True)
-
- except DeathByCaptchaException, e:
- self.logError(e.getDesc())
-
- except Exception, e:
- self.logError(e)
-
-
- def processCaptcha(self, task):
- c = task.captchaFile
- try:
- ticket, result = self.submit(c)
- except DeathByCaptchaException, e:
- task.error = e.getCode()
- self.logError(e.getDesc())
- return
-
- task.data['ticket'] = ticket
- task.setResult(result)
diff --git a/module/plugins/hooks/DebridItaliaCom.py b/module/plugins/hooks/DebridItaliaCom.py
deleted file mode 100644
index b7f0ef1c7..000000000
--- a/module/plugins/hooks/DebridItaliaCom.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class DebridItaliaCom(MultiHoster):
- __name__ = "DebridItaliaCom"
- __type__ = "hook"
- __version__ = "0.08"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", ""),
- ("unloadFailing", "bool", "Revert to standard download if download fails", False),
- ("interval", "int", "Reload interval in hours (0 to disable)", 24)]
-
- __description__ = """Debriditalia.com hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- def getHoster(self):
- html = getURL("http://www.debriditalia.com/status.php")
- return re.findall(r'title="(.+?)"> \1</td><td><img src="/images/(?:attivo|testing)', html)
diff --git a/module/plugins/hooks/DeleteFinished.py b/module/plugins/hooks/DeleteFinished.py
deleted file mode 100644
index 5d2b78d50..000000000
--- a/module/plugins/hooks/DeleteFinished.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.database import style
-from module.plugins.Hook import Hook
-
-
-class DeleteFinished(Hook):
- __name__ = "DeleteFinished"
- __type__ = "hook"
- __version__ = "1.11"
-
- __config__ = [('activated', 'bool', 'Activated', 'False'),
- ('interval', 'int', 'Delete every (hours)', '72'),
- ('deloffline', 'bool', 'Delete packages with offline links', 'False')]
-
- __description__ = """Automatically delete all finished packages from queue"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- # event_list = ["pluginConfigChanged"]
-
-
- ## overwritten methods ##
- def periodical(self):
- if not self.info['sleep']:
- deloffline = self.getConfig('deloffline')
- mode = '0,1,4' if deloffline else '0,4'
- msg = _('delete all finished packages in queue list (%s packages with offline links)')
- self.logInfo(msg % (_('including') if deloffline else _('excluding')))
- self.deleteFinished(mode)
- self.info['sleep'] = True
- self.addEvent('packageFinished', self.wakeup)
-
-
- def pluginConfigChanged(self, plugin, name, value):
- if name == "interval" and value != self.interval:
- self.interval = value * 3600
- self.initPeriodical()
-
-
- def unload(self):
- self.removeEvent('packageFinished', self.wakeup)
-
-
- def coreReady(self):
- self.info = {'sleep': True}
- interval = self.getConfig('interval')
- self.pluginConfigChanged(self.__name__, 'interval', interval)
- self.addEvent('packageFinished', self.wakeup)
-
-
- ## own methods ##
- @style.queue
- def deleteFinished(self, mode):
- self.c.execute('DELETE FROM packages WHERE NOT EXISTS(SELECT 1 FROM links WHERE package=packages.id AND status NOT IN (%s))' % mode)
- self.c.execute('DELETE FROM links WHERE NOT EXISTS(SELECT 1 FROM packages WHERE id=links.package)')
-
-
- def wakeup(self, pypack):
- self.removeEvent('packageFinished', self.wakeup)
- self.info['sleep'] = False
-
-
- ## event managing ##
- def addEvent(self, event, func):
- """Adds an event listener for event name"""
- if event in self.m.events:
- if func in self.m.events[event]:
- self.logDebug("Function already registered", func)
- else:
- self.m.events[event].append(func)
- else:
- self.m.events[event] = [func]
-
-
- def setup(self):
- self.m = self.manager
- self.removeEvent = self.m.removeEvent
diff --git a/module/plugins/hooks/DownloadScheduler.py b/module/plugins/hooks/DownloadScheduler.py
deleted file mode 100644
index 14884426f..000000000
--- a/module/plugins/hooks/DownloadScheduler.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import localtime
-
-from module.plugins.Hook import Hook
-
-
-class DownloadScheduler(Hook):
- __name__ = "DownloadScheduler"
- __type__ = "hook"
- __version__ = "0.21"
-
- __config__ = [("timetable", "str", "List time periods as hh:mm full or number(kB/s)",
- "0:00 full, 7:00 250, 10:00 0, 17:00 150"),
- ("abort", "bool", "Abort active downloads when start period with speed 0", False)]
-
- __description__ = """Download Scheduler"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- def setup(self):
- self.cb = None # callback to scheduler job; will be by removed hookmanager when hook unloaded
-
-
- def coreReady(self):
- self.updateSchedule()
-
-
- def updateSchedule(self, schedule=None):
- if schedule is None:
- schedule = self.getConfig("timetable")
-
- schedule = re.findall("(\d{1,2}):(\d{2})[\s]*(-?\d+)",
- schedule.lower().replace("full", "-1").replace("none", "0"))
- if not schedule:
- self.logError(_("Invalid schedule"))
- return
-
- t0 = localtime()
- now = (t0.tm_hour, t0.tm_min, t0.tm_sec, "X")
- schedule = sorted([(int(x[0]), int(x[1]), 0, int(x[2])) for x in schedule] + [now])
-
- self.logDebug("Schedule", schedule)
-
- for i, v in enumerate(schedule):
- if v[3] == "X":
- last, next = schedule[i - 1], schedule[(i + 1) % len(schedule)]
- self.logDebug("Now/Last/Next", now, last, next)
-
- self.setDownloadSpeed(last[3])
-
- next_time = (((24 + next[0] - now[0]) * 60 + next[1] - now[1]) * 60 + next[2] - now[2]) % 86400
- self.core.scheduler.removeJob(self.cb)
- self.cb = self.core.scheduler.addJob(next_time, self.updateSchedule, threaded=False)
-
-
- def setDownloadSpeed(self, speed):
- if speed == 0:
- abort = self.getConfig("abort")
- self.logInfo(_("Stopping download server. (Running downloads will %sbe aborted.)") % '' if abort else _('not '))
- self.core.api.pauseServer()
- if abort:
- self.core.api.stopAllDownloads()
- else:
- self.core.api.unpauseServer()
-
- if speed > 0:
- self.logInfo(_("Setting download speed to %d kB/s") % speed)
- self.core.api.setConfigValue("download", "limit_speed", 1)
- self.core.api.setConfigValue("download", "max_speed", speed)
- else:
- self.logInfo(_("Setting download speed to FULL"))
- self.core.api.setConfigValue("download", "limit_speed", 0)
- self.core.api.setConfigValue("download", "max_speed", -1)
diff --git a/module/plugins/hooks/EasybytezCom.py b/module/plugins/hooks/EasybytezCom.py
deleted file mode 100644
index 3faa4fa1a..000000000
--- a/module/plugins/hooks/EasybytezCom.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class EasybytezCom(MultiHoster):
- __name__ = "EasybytezCom"
- __type__ = "hook"
- __version__ = "0.03"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", "")]
-
- __description__ = """EasyBytez.com hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- def getHoster(self):
- self.account = self.core.accountManager.getAccountPlugin(self.__name__)
- user = self.account.selectAccount()[0]
-
- try:
- req = self.account.getAccountRequest(user)
- page = req.load("http://www.easybytez.com")
-
- hosters = re.search(r'</textarea>\s*Supported sites:(.*)', page).group(1).split(',')
-
- except Exception, e:
- self.logWarning(_("Unable to load supported hoster list, using last known"))
- self.logDebug(e)
-
- hosters = ["bitshare.com", "crocko.com", "ddlstorage.com", "depositfiles.com", "extabit.com", "hotfile.com",
- "mediafire.com", "netload.in", "rapidgator.net", "rapidshare.com", "uploading.com", "uload.to",
- "uploaded.to"]
- finally:
- return hosters
diff --git a/module/plugins/hooks/ExpertDecoders.py b/module/plugins/hooks/ExpertDecoders.py
deleted file mode 100644
index 1b9459eb6..000000000
--- a/module/plugins/hooks/ExpertDecoders.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-from base64 import b64encode
-from pycurl import LOW_SPEED_TIME
-from thread import start_new_thread
-from uuid import uuid4
-
-from module.network.HTTPRequest import BadHeader
-from module.network.RequestFactory import getURL, getRequest
-from module.plugins.Hook import Hook
-
-
-class ExpertDecoders(Hook):
- __name__ = "ExpertDecoders"
- __type__ = "hook"
- __version__ = "0.01"
-
- __config__ = [("force", "bool", "Force CT even if client is connected", False),
- ("passkey", "password", "Access key", "")]
-
- __description__ = """Send captchas to expertdecoders.com"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- API_URL = "http://www.fasttypers.org/imagepost.ashx"
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
-
-
- def getCredits(self):
- res = getURL(self.API_URL, post={"key": self.getConfig("passkey"), "action": "balance"})
-
- if res.isdigit():
- self.logInfo(_("%s credits left") % res)
- self.info['credits'] = credits = int(res)
- return credits
- else:
- self.logError(res)
- return 0
-
-
- def processCaptcha(self, task):
- task.data['ticket'] = ticket = uuid4()
- result = None
-
- with open(task.captchaFile, 'rb') as f:
- data = f.read()
- data = b64encode(data)
-
- req = getRequest()
- #raise timeout threshold
- req.c.setopt(LOW_SPEED_TIME, 80)
-
- try:
- result = req.load(self.API_URL, post={"action": "upload", "key": self.getConfig("passkey"),
- "file": data, "gen_task_id": ticket})
- finally:
- req.close()
-
- self.logDebug("Result %s : %s" % (ticket, result))
- task.setResult(result)
-
-
- def newCaptchaTask(self, task):
- if not task.isTextual():
- return False
-
- if not self.getConfig("passkey"):
- return False
-
- if self.core.isClientConnected() and not self.getConfig("force"):
- return False
-
- if self.getCredits() > 0:
- task.handler.append(self)
- task.setWaiting(100)
- start_new_thread(self.processCaptcha, (task,))
-
- else:
- self.logInfo(_("Your ExpertDecoders Account has not enough credits"))
-
-
- def captchaInvalid(self, task):
- if "ticket" in task.data:
-
- try:
- res = getURL(self.API_URL,
- post={'action': "refund", 'key': self.getConfig("passkey"), 'gen_task_id': task.data['ticket']})
- self.logInfo(_("Request refund", res)
-
- except BadHeader, e:
- self.logError(_("Could not send refund request"), e)
diff --git a/module/plugins/hooks/ExternalScripts.py b/module/plugins/hooks/ExternalScripts.py
deleted file mode 100644
index 5db2037fa..000000000
--- a/module/plugins/hooks/ExternalScripts.py
+++ /dev/null
@@ -1,141 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import subprocess
-
-from itertools import chain
-from os import listdir, access, X_OK, makedirs
-from os.path import join, exists, basename, abspath
-
-from module.plugins.Hook import Hook
-from module.utils import save_join
-
-
-class ExternalScripts(Hook):
- __name__ = "ExternalScripts"
- __type__ = "hook"
- __version__ = "0.24"
-
- __config__ = [("activated", "bool", "Activated", True)]
-
- __description__ = """Run external scripts"""
- __license__ = "GPLv3"
- __authors__ = [("mkaay", "mkaay@mkaay.de"),
- ("RaNaN", "ranan@pyload.org"),
- ("spoob", "spoob@pyload.org"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- event_list = ["archive_extracted", "package_extracted", "all_archives_extracted", "all_archives_processed",
- "allDownloadsFinished", "allDownloadsProcessed"]
-
-
- def setup(self):
- self.scripts = {}
-
- folders = ["download_preparing", "download_finished", "all_downloads_finished", "all_downloads_processed",
- "before_reconnect", "after_reconnect",
- "package_finished", "package_extracted",
- "archive_extracted", "all_archives_extracted", "all_archives_processed",
- # deprecated folders
- "unrar_finished", "all_dls_finished", "all_dls_processed"]
-
- for folder in folders:
- self.scripts[folder] = []
-
- self.initPluginType(folder, join(pypath, 'scripts', folder))
- self.initPluginType(folder, join('scripts', folder))
-
- for script_type, names in self.scripts.iteritems():
- if names:
- self.logInfo(_("Installed scripts for"), script_type, ", ".join([basename(x) for x in names]))
-
-
- def initPluginType(self, folder, path):
- if not exists(path):
- try:
- makedirs(path)
- except:
- self.logDebug("Script folder %s not created" % folder)
- return
-
- for f in listdir(path):
- if f.startswith("#") or f.startswith(".") or f.startswith("_") or f.endswith("~") or f.endswith(".swp"):
- continue
-
- if not access(join(path, f), X_OK):
- self.logWarning(_("Script not executable:") + " %s/%s" % (folder, f))
-
- self.scripts[folder].append(join(path, f))
-
-
- def callScript(self, script, *args):
- try:
- cmd = [script] + [str(x) if not isinstance(x, basestring) else x for x in args]
- self.logDebug("Executing", abspath(script), " ".join(cmd))
- #output goes to pyload
- subprocess.Popen(cmd, bufsize=-1)
- except Exception, e:
- self.logError(_("Error in %(script)s: %(error)s") % {"script": basename(script), "error": e})
-
-
- def downloadPreparing(self, pyfile):
- for script in self.scripts['download_preparing']:
- self.callScript(script, pyfile.pluginname, pyfile.url, pyfile.id)
-
-
- def downloadFinished(self, pyfile):
- download_folder = self.config['general']['download_folder']
- for script in self.scripts['download_finished']:
- filename = save_join(download_folder, pyfile.package().folder, pyfile.name)
- self.callScript(script, pyfile.pluginname, pyfile.url, pyfile.name, filename, pyfile.id)
-
-
- def packageFinished(self, pypack):
- download_folder = self.config['general']['download_folder']
- for script in self.scripts['package_finished']:
- folder = save_join(download_folder, pypack.folder)
- self.callScript(script, pypack.name, folder, pypack.password, pypack.id)
-
-
- def beforeReconnecting(self, ip):
- for script in self.scripts['before_reconnect']:
- self.callScript(script, ip)
-
-
- def afterReconnecting(self, ip):
- for script in self.scripts['after_reconnect']:
- self.callScript(script, ip)
-
-
- def archive_extracted(self, pyfile, folder, filename, files):
- for script in self.scripts['archive_extracted']:
- self.callScript(script, folder, filename, files)
- for script in self.scripts['unrar_finished']: #: deprecated
- self.callScript(script, folder, filename)
-
-
- def package_extracted(self, pypack):
- download_folder = self.config['general']['download_folder']
- for script in self.scripts['package_extracted']:
- folder = save_join(download_folder, pypack.folder)
- self.callScript(script, pypack.name, folder, pypack.password, pypack.id)
-
-
- def all_archives_extracted(self):
- for script in self.scripts['all_archives_extracted']:
- self.callScript(script)
-
-
- def all_archives_processed(self):
- for script in self.scripts['all_archives_processed']:
- self.callScript(script)
-
-
- def allDownloadsFinished(self):
- for script in chain(self.scripts['all_downloads_finished'], self.scripts['all_dls_finished']):
- self.callScript(script)
-
-
- def allDownloadsProcessed(self):
- for script in chain(self.scripts['all_downloads_processed'], self.scripts['all_dls_processed']):
- self.callScript(script)
diff --git a/module/plugins/hooks/ExtractArchive.py b/module/plugins/hooks/ExtractArchive.py
deleted file mode 100644
index 07db13aa1..000000000
--- a/module/plugins/hooks/ExtractArchive.py
+++ /dev/null
@@ -1,362 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-import os
-import sys
-
-from copy import copy
-from os import remove, chmod, makedirs
-from os.path import exists, basename, isfile, isdir
-from traceback import print_exc
-
-# monkey patch bug in python 2.6 and lower
-# http://bugs.python.org/issue6122 , http://bugs.python.org/issue1236 , http://bugs.python.org/issue1731717
-if sys.version_info < (2, 7) and os.name != "nt":
- import errno
- from subprocess import Popen
-
-
- def _eintr_retry_call(func, *args):
- while True:
- try:
- return func(*args)
- except OSError, e:
- if e.errno == errno.EINTR:
- continue
- raise
-
-
- # unsued timeout option for older python version
- def wait(self, timeout=0):
- """Wait for child process to terminate. Returns returncode
- attribute."""
- if self.returncode is None:
- try:
- pid, sts = _eintr_retry_call(os.waitpid, self.pid, 0)
- except OSError, e:
- if e.errno != errno.ECHILD:
- raise
- # This happens if SIGCLD is set to be ignored or waiting
- # for child processes has otherwise been disabled for our
- # process. This child is dead, we can't get the status.
- sts = 0
- self._handle_exitstatus(sts)
- return self.returncode
-
- Popen.wait = wait
-
-if os.name != "nt":
- from grp import getgrnam
- from os import chown
- from pwd import getpwnam
-
-from module.plugins.Hook import Hook, threaded, Expose
-from module.plugins.internal.AbstractExtractor import ArchiveError, CRCError, WrongPassword
-from module.utils import save_join, fs_encode
-
-
-class ExtractArchive(Hook):
- __name__ = "ExtractArchive"
- __type__ = "hook"
- __version__ = "0.18"
-
- __config__ = [("activated", "bool", "Activated", True),
- ("fullpath", "bool", "Extract full path", True),
- ("overwrite", "bool", "Overwrite files", True),
- ("passwordfile", "file", "password file", "archive_password.txt"),
- ("deletearchive", "bool", "Delete archives when done", False),
- ("subfolder", "bool", "Create subfolder for each package", False),
- ("destination", "folder", "Extract files to", ""),
- ("excludefiles", "str", "Exclude files from unpacking (seperated by ;)", ""),
- ("recursive", "bool", "Extract archives in archvies", True),
- ("queue", "bool", "Wait for all downloads to be finished", True),
- ("renice", "int", "CPU Priority", 0)]
-
- __description__ = """Extract different kind of archives"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "ranan@pyload.org"),
- ("AndroKev", None),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- event_list = ["allDownloadsProcessed"]
-
-
- def setup(self):
- self.plugins = []
- self.passwords = []
- names = []
-
- for p in ("UnRar", "UnZip"):
- try:
- module = self.core.pluginManager.loadModule("internal", p)
- klass = getattr(module, p)
- if klass.checkDeps():
- names.append(p)
- self.plugins.append(klass)
-
- except OSError, e:
- if e.errno == 2:
- self.logInfo(_("No %s installed") % p)
- else:
- self.logWarning(_("Could not activate %s") % p, e)
- if self.core.debug:
- print_exc()
-
- except Exception, e:
- self.logWarning(_("Could not activate %s") % p, e)
- if self.core.debug:
- print_exc()
-
- if names:
- self.logInfo(_("Activated") + " " + " ".join(names))
- else:
- self.logInfo(_("No Extract plugins activated"))
-
- # queue with package ids
- self.queue = []
-
-
- @Expose
- def extractPackage(self, id):
- """ Extract package with given id"""
- self.manager.startThread(self.extract, [id])
-
-
- def packageFinished(self, pypack):
- pid = pypack.id
- if self.getConfig("queue"):
- self.logInfo(_("Package %s queued for later extracting") % pypack.name)
- self.queue.append(pid)
- else:
- self.manager.startThread(self.extract, [pid])
-
-
- @threaded
- def allDownloadsProcessed(self, thread):
- local = copy(self.queue)
- del self.queue[:]
- if self.extract(local, thread): #: check only if all gone fine, no failed reporting for now
- self.manager.dispatchEvent("all_archives_extracted")
- self.manager.dispatchEvent("all_archives_processed")
-
-
- def extract(self, ids, thread=None):
- processed = []
- extracted = []
- failed = []
-
- destination = self.getConfig("destination")
- subfolder = self.getConfig("subfolder")
- fullpath = self.getConfig("fullpath")
- overwrite = self.getConfig("overwrite")
- excludefiles = self.getConfig("excludefiles")
- renice = self.getConfig("renice")
- recursive = self.getConfig("recursive")
-
- # reload from txt file
- self.reloadPasswords()
-
- # dl folder
- dl = self.config['general']['download_folder']
-
- #iterate packages -> plugins -> targets
- for pid in ids:
- p = self.core.files.getPackage(pid)
- self.logInfo(_("Check package %s") % p.name)
- if not p:
- continue
-
- # determine output folder
- out = save_join(dl, p.folder, destination, "") #: force trailing slash
-
- if subfolder:
- out = save_join(out, fs_encode(p.folder))
-
- if not exists(out):
- makedirs(out)
-
- files_ids = [(save_join(dl, p.folder, x['name']), x['id']) for x in p.getChildren().itervalues()]
- matched = False
- success = True
-
- # check as long there are unseen files
- while files_ids:
- new_files_ids = []
-
- for plugin in self.plugins:
- targets = plugin.getTargets(files_ids)
- if targets:
- self.logDebug("Targets for %s: %s" % (plugin.__name__, targets))
- matched = True
- for target, fid in targets:
- if target in processed:
- self.logDebug(basename(target), "skipped")
- continue
-
- processed.append(target) # prevent extracting same file twice
-
- self.logInfo(basename(target), _("Extract to %s") % out)
- try:
- klass = plugin(self, target, out, fullpath, overwrite, excludefiles, renice)
- klass.init()
- password = p.password.strip().splitlines()
- new_files = self._extract(klass, fid, password, thread)
- except Exception, e:
- self.logError(basename(target), e)
- success = False
- continue
-
- self.logDebug("Extracted", new_files)
- self.setPermissions(new_files)
-
- for file in new_files:
- if not exists(file):
- self.logDebug("New file %s does not exists" % file)
- continue
- if recursive and isfile(file):
- new_files_ids.append((file, fid)) # append as new target
-
- files_ids = new_files_ids # also check extracted files
-
- if matched:
- if success:
- extracted.append(pid)
- self.manager.dispatchEvent("package_extracted", p)
- else:
- failed.append(pid)
- self.manager.dispatchEvent("package_extract_failed", p)
- else:
- self.logInfo(_("No files found to extract"))
-
- return True if not failed else False
-
-
- def _extract(self, plugin, fid, passwords, thread):
- pyfile = self.core.files.getFile(fid)
- deletearchive = self.getConfig("deletearchive")
-
- pyfile.setCustomStatus(_("extracting"))
- thread.addActive(pyfile) # keep this file until everything is done
-
- try:
- progress = lambda x: pyfile.setProgress(x)
- success = False
-
- if not plugin.checkArchive():
- plugin.extract(progress)
- success = True
- else:
- self.logInfo(basename(plugin.file), _("Password protected"))
- self.logDebug("Passwords", passwords)
-
- pwlist = copy(self.getPasswords())
- # remove already supplied pws from list (only local)
- for pw in passwords:
- if pw in pwlist:
- pwlist.remove(pw)
-
- for pw in passwords + pwlist:
- try:
- self.logDebug("Try password", pw)
- if plugin.checkPassword(pw):
- plugin.extract(progress, pw)
- self.addPassword(pw)
- success = True
- break
- except WrongPassword:
- self.logDebug("Password was wrong")
-
- if not success:
- raise Exception(_("Wrong password"))
-
- if self.core.debug:
- self.logDebug("Would delete", ", ".join(plugin.getDeleteFiles()))
-
- if deletearchive:
- files = plugin.getDeleteFiles()
- self.logInfo(_("Deleting %s files") % len(files))
- for f in files:
- if exists(f):
- remove(f)
- else:
- self.logDebug("%s does not exists" % f)
-
- self.logInfo(basename(plugin.file), _("Extracting finished"))
-
- extracted_files = plugin.getExtractedFiles()
- self.manager.dispatchEvent("archive_extracted", pyfile, plugin.out, plugin.file, extracted_files)
-
- return extracted_files
-
- except ArchiveError, e:
- self.logError(basename(plugin.file), _("Archive Error"), e)
- except CRCError:
- self.logError(basename(plugin.file), _("CRC Mismatch"))
- except Exception, e:
- if self.core.debug:
- print_exc()
- self.logError(basename(plugin.file), _("Unknown Error"), e)
-
- self.manager.dispatchEvent("archive_extract_failed", pyfile)
- raise Exception(_("Extract failed"))
-
-
- @Expose
- def getPasswords(self):
- """ List of saved passwords """
- return self.passwords
-
-
- def reloadPasswords(self):
- passwordfile = self.getConfig("passwordfile")
-
- try:
- passwords = []
- with open(passwordfile, "a+") as f:
- for pw in f.read().splitlines():
- passwords.append(pw)
-
- except IOError, e:
- self.logError(e)
-
- else:
- self.passwords = passwords
-
-
- @Expose
- def addPassword(self, pw):
- """ Adds a password to saved list"""
- passwordfile = self.getConfig("passwordfile")
-
- if pw in self.passwords:
- self.passwords.remove(pw)
-
- self.passwords.insert(0, pw)
-
- try:
- with open(passwordfile, "wb") as f:
- for pw in self.passwords:
- f.write(pw + "\n")
- except IOError, e:
- self.logError(e)
-
-
- def setPermissions(self, files):
- for f in files:
- if not exists(f):
- continue
- try:
- if self.config['permission']['change_file']:
- if isfile(f):
- chmod(f, int(self.config['permission']['file'], 8))
- elif isdir(f):
- chmod(f, int(self.config['permission']['folder'], 8))
-
- if self.config['permission']['change_dl'] and os.name != "nt":
- uid = getpwnam(self.config['permission']['user'])[2]
- gid = getgrnam(self.config['permission']['group'])[2]
- chown(f, uid, gid)
- except Exception, e:
- self.logWarning(_("Setting User and Group failed"), e)
diff --git a/module/plugins/hooks/FastixRu.py b/module/plugins/hooks/FastixRu.py
deleted file mode 100644
index cec6c6f1f..000000000
--- a/module/plugins/hooks/FastixRu.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.common.json_layer import json_loads
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class FastixRu(MultiHoster):
- __name__ = "FastixRu"
- __type__ = "hook"
- __version__ = "0.02"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"),
- ("unloadFailing", "bool", "Revert to standard download if download fails", False),
- ("interval", "int", "Reload interval in hours (0 to disable)", 24)]
-
- __description__ = """Fastix.ru hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Massimo Rosamilia", "max@spiritix.eu")]
-
-
- def getHoster(self):
- page = getURL("http://fastix.ru/api_v2",
- get={'apikey': "5182964c3f8f9a7f0b00000a_kelmFB4n1IrnCDYuIFn2y",
- 'sub' : "allowed_sources"})
- host_list = json_loads(page)
- host_list = host_list['allow']
- return host_list
diff --git a/module/plugins/hooks/FreeWayMe.py b/module/plugins/hooks/FreeWayMe.py
deleted file mode 100644
index 5abec29ba..000000000
--- a/module/plugins/hooks/FreeWayMe.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class FreeWayMe(MultiHoster):
- __name__ = "FreeWayMe"
- __type__ = "hook"
- __version__ = "0.11"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", ""),
- ("unloadFailing", "bool", "Revert to stanard download if download fails", False),
- ("interval", "int", "Reload interval in hours (0 to disable)", 24)]
-
- __description__ = """FreeWay.me hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Nicolas Giese", "james@free-way.me")]
-
-
- def getHoster(self):
- hostis = getURL("https://www.free-way.me/ajax/jd.php", get={'id': 3}).replace("\"", "").strip()
- self.logDebug("Hosters", hostis)
- return [x.strip() for x in hostis.split(",") if x.strip()]
diff --git a/module/plugins/hooks/HotFolder.py b/module/plugins/hooks/HotFolder.py
deleted file mode 100644
index b0b59e2ba..000000000
--- a/module/plugins/hooks/HotFolder.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-import time
-
-from os import listdir, makedirs
-from os.path import exists, isfile, join
-from shutil import move
-
-from module.plugins.Hook import Hook
-from module.utils import fs_encode, save_join
-
-
-class HotFolder(Hook):
- __name__ = "HotFolder"
- __type__ = "hook"
- __version__ = "0.12"
-
- __config__ = [("folder", "str", "Folder to observe", "container"),
- ("watch_file", "bool", "Observe link file", False),
- ("keep", "bool", "Keep added containers", True),
- ("file", "str", "Link file", "links.txt")]
-
- __description__ = """Observe folder and file for changes and add container and links"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.de")]
-
-
- def setup(self):
- self.interval = 10
-
-
- def periodical(self):
- folder = fs_encode(self.getConfig("folder"))
-
- try:
- if not exists(join(folder, "finished")):
- makedirs(join(folder, "finished"))
-
- if self.getConfig("watch_file"):
- with open(fs_encode(self.getConfig("file")), "a+") as f:
- content = f.read().strip()
-
- if content:
- name = "%s_%s.txt" % (self.getConfig("file"), time.strftime("%H-%M-%S_%d%b%Y"))
-
- with open(save_join(folder, "finished", name), "wb") as f:
- f.write(content)
-
- self.core.api.addPackage(f.name, [f.name], 1)
-
- for f in listdir(folder):
- path = join(folder, f)
-
- if not isfile(path) or f.endswith("~") or f.startswith("#") or f.startswith("."):
- continue
-
- newpath = join(folder, "finished", f if self.getConfig("keep") else "tmp_" + f)
- move(path, newpath)
-
- self.logInfo(_("Added %s from HotFolder") % f)
- self.core.api.addPackage(f, [newpath], 1)
-
- except IOError, e:
- self.logError(e)
diff --git a/module/plugins/hooks/IRCInterface.py b/module/plugins/hooks/IRCInterface.py
deleted file mode 100644
index 98edc2f7f..000000000
--- a/module/plugins/hooks/IRCInterface.py
+++ /dev/null
@@ -1,431 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-import socket
-import ssl
-import time
-
-from pycurl import FORM_FILE
-from select import select
-from threading import Thread
-from time import sleep
-from traceback import print_exc
-
-from module.Api import PackageDoesNotExists, FileDoesNotExists
-from module.network.RequestFactory import getURL
-from module.plugins.Hook import Hook
-from module.utils import formatSize
-
-
-class IRCInterface(Thread, Hook):
- __name__ = "IRCInterface"
- __type__ = "hook"
- __version__ = "0.12"
-
- __config__ = [("host", "str", "IRC-Server Address", "Enter your server here!"),
- ("port", "int", "IRC-Server Port", 6667),
- ("ident", "str", "Clients ident", "pyload-irc"),
- ("realname", "str", "Realname", "pyload-irc"),
- ("ssl", "bool", "Use SSL", False),
- ("nick", "str", "Nickname the Client will take", "pyLoad-IRC"),
- ("owner", "str", "Nickname the Client will accept commands from", "Enter your nick here!"),
- ("info_file", "bool", "Inform about every file finished", False),
- ("info_pack", "bool", "Inform about every package finished", True),
- ("captcha", "bool", "Send captcha requests", True)]
-
- __description__ = """Connect to irc and let owner perform different tasks"""
- __license__ = "GPLv3"
- __authors__ = [("Jeix", "Jeix@hasnomail.com")]
-
-
- def __init__(self, core, manager):
- Thread.__init__(self)
- Hook.__init__(self, core, manager)
- self.setDaemon(True)
-
-
- def coreReady(self):
- self.abort = False
- self.more = []
- self.new_package = {}
-
- self.start()
-
-
- def packageFinished(self, pypack):
- try:
- if self.getConfig("info_pack"):
- self.response(_("Package finished: %s") % pypack.name)
- except:
- pass
-
-
- def downloadFinished(self, pyfile):
- try:
- if self.getConfig("info_file"):
- self.response(
- _("Download finished: %(name)s @ %(plugin)s ") % {"name": pyfile.name, "plugin": pyfile.pluginname})
- except:
- pass
-
-
- def newCaptchaTask(self, task):
- if self.getConfig("captcha") and task.isTextual():
- task.handler.append(self)
- task.setWaiting(60)
-
- page = getURL("http://www.freeimagehosting.net/upload.php",
- post={"attached": (FORM_FILE, task.captchaFile)}, multipart=True)
-
- url = re.search(r"\[img\]([^\[]+)\[/img\]\[/url\]", page).group(1)
- self.response(_("New Captcha Request: %s") % url)
- self.response(_("Answer with 'c %s text on the captcha'") % task.id)
-
-
- def run(self):
- # connect to IRC etc.
- self.sock = socket.socket()
- host = self.getConfig("host")
- self.sock.connect((host, self.getConfig("port")))
-
- if self.getConfig("ssl"):
- self.sock = ssl.wrap_socket(self.sock, cert_reqs=ssl.CERT_NONE) #@TODO: support certificate
-
- nick = self.getConfig("nick")
- self.sock.send("NICK %s\r\n" % nick)
- self.sock.send("USER %s %s bla :%s\r\n" % (nick, host, nick))
- for t in self.getConfig("owner").split():
- if t.strip().startswith("#"):
- self.sock.send("JOIN %s\r\n" % t.strip())
- self.logInfo(_("Connected to"), host)
- self.logInfo(_("Switching to listening mode!"))
- try:
- self.main_loop()
-
- except IRCError, ex:
- self.sock.send("QUIT :byebye\r\n")
- print_exc()
- self.sock.close()
-
-
- def main_loop(self):
- readbuffer = ""
- while True:
- sleep(1)
- fdset = select([self.sock], [], [], 0)
- if self.sock not in fdset[0]:
- continue
-
- if self.abort:
- raise IRCError("quit")
-
- readbuffer += self.sock.recv(1024)
- temp = readbuffer.split("\n")
- readbuffer = temp.pop()
-
- for line in temp:
- line = line.rstrip()
- first = line.split()
-
- if first[0] == "PING":
- self.sock.send("PONG %s\r\n" % first[1])
-
- if first[0] == "ERROR":
- raise IRCError(line)
-
- msg = line.split(None, 3)
- if len(msg) < 4:
- continue
-
- msg = {
- "origin": msg[0][1:],
- "action": msg[1],
- "target": msg[2],
- "text": msg[3][1:]
- }
-
- self.handle_events(msg)
-
-
- def handle_events(self, msg):
- if not msg['origin'].split("!", 1)[0] in self.getConfig("owner").split():
- return
-
- if msg['target'].split("!", 1)[0] != self.getConfig("nick"):
- return
-
- if msg['action'] != "PRIVMSG":
- return
-
- # HANDLE CTCP ANTI FLOOD/BOT PROTECTION
- if msg['text'] == "\x01VERSION\x01":
- self.logDebug("Sending CTCP VERSION")
- self.sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface"))
- return
- elif msg['text'] == "\x01TIME\x01":
- self.logDebug("Sending CTCP TIME")
- self.sock.send("NOTICE %s :%d\r\n" % (msg['origin'], time.time()))
- return
- elif msg['text'] == "\x01LAG\x01":
- self.logDebug("Received CTCP LAG") #: don't know how to answer
- return
-
- trigger = "pass"
- args = None
-
- try:
- temp = msg['text'].split()
- trigger = temp[0]
- if len(temp) > 1:
- args = temp[1:]
- except:
- pass
-
- handler = getattr(self, "event_%s" % trigger, self.event_pass)
- try:
- res = handler(args)
- for line in res:
- self.response(line, msg['origin'])
- except Exception, e:
- self.logError(e)
-
-
- def response(self, msg, origin=""):
- if origin == "":
- for t in self.getConfig("owner").split():
- self.sock.send("PRIVMSG %s :%s\r\n" % (t.strip(), msg))
- else:
- self.sock.send("PRIVMSG %s :%s\r\n" % (origin.split("!", 1)[0], msg))
-
-
- #### Events
-
- def event_pass(self, args):
- return []
-
-
- def event_status(self, args):
- downloads = self.core.api.statusDownloads()
- if not downloads:
- return ["INFO: There are no active downloads currently."]
-
- temp_progress = ""
- lines = ["ID - Name - Status - Speed - ETA - Progress"]
- for data in downloads:
-
- if data.status == 5:
- temp_progress = data.format_wait
- else:
- temp_progress = "%d%% (%s)" % (data.percent, data.format_size)
-
- lines.append("#%d - %s - %s - %s - %s - %s" %
- (
- data.fid,
- data.name,
- data.statusmsg,
- "%s/s" % formatSize(data.speed),
- "%s" % data.format_eta,
- temp_progress
- ))
- return lines
-
-
- def event_queue(self, args):
- ps = self.core.api.getQueueData()
-
- if not ps:
- return ["INFO: There are no packages in queue."]
-
- lines = []
- for pack in ps:
- lines.append('PACKAGE #%s: "%s" with %d links.' % (pack.pid, pack.name, len(pack.links)))
-
- return lines
-
-
- def event_collector(self, args):
- ps = self.core.api.getCollectorData()
- if not ps:
- return ["INFO: No packages in collector!"]
-
- lines = []
- for pack in ps:
- lines.append('PACKAGE #%s: "%s" with %d links.' % (pack.pid, pack.name, len(pack.links)))
-
- return lines
-
-
- def event_info(self, args):
- if not args:
- return ["ERROR: Use info like this: info <id>"]
-
- info = None
- try:
- info = self.core.api.getFileData(int(args[0]))
-
- except FileDoesNotExists:
- return ["ERROR: Link doesn't exists."]
-
- return ['LINK #%s: %s (%s) [%s][%s]' % (info.fid, info.name, info.format_size, info.statusmsg, info.plugin)]
-
-
- def event_packinfo(self, args):
- if not args:
- return ["ERROR: Use packinfo like this: packinfo <id>"]
-
- lines = []
- pack = None
- try:
- pack = self.core.api.getPackageData(int(args[0]))
-
- except PackageDoesNotExists:
- return ["ERROR: Package doesn't exists."]
-
- id = args[0]
-
- self.more = []
-
- lines.append('PACKAGE #%s: "%s" with %d links' % (id, pack.name, len(pack.links)))
- for pyfile in pack.links:
- self.more.append('LINK #%s: %s (%s) [%s][%s]' % (pyfile.fid, pyfile.name, pyfile.format_size,
- pyfile.statusmsg, pyfile.plugin))
-
- if len(self.more) < 6:
- lines.extend(self.more)
- self.more = []
- else:
- lines.extend(self.more[:6])
- self.more = self.more[6:]
- lines.append("%d more links do display." % len(self.more))
-
- return lines
-
-
- def event_more(self, args):
- if not self.more:
- return ["No more information to display."]
-
- lines = self.more[:6]
- self.more = self.more[6:]
- lines.append("%d more links do display." % len(self.more))
-
- return lines
-
-
- def event_start(self, args):
- self.core.api.unpauseServer()
- return ["INFO: Starting downloads."]
-
-
- def event_stop(self, args):
- self.core.api.pauseServer()
- return ["INFO: No new downloads will be started."]
-
-
- def event_add(self, args):
- if len(args) < 2:
- return ['ERROR: Add links like this: "add <packagename|id> links". ',
- "This will add the link <link> to to the package <package> / the package with id <id>!"]
-
- pack = args[0].strip()
- links = [x.strip() for x in args[1:]]
-
- count_added = 0
- count_failed = 0
- try:
- id = int(pack)
- pack = self.core.api.getPackageData(id)
- if not pack:
- return ["ERROR: Package doesn't exists."]
-
- #TODO add links
-
- return ["INFO: Added %d links to Package %s [#%d]" % (len(links), pack['name'], id)]
-
- except:
- # create new package
- id = self.core.api.addPackage(pack, links, 1)
- return ["INFO: Created new Package %s [#%d] with %d links." % (pack, id, len(links))]
-
-
- def event_del(self, args):
- if len(args) < 2:
- return ["ERROR: Use del command like this: del -p|-l <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"]
-
- if args[0] == "-p":
- ret = self.core.api.deletePackages(map(int, args[1:]))
- return ["INFO: Deleted %d packages!" % len(args[1:])]
-
- elif args[0] == "-l":
- ret = self.core.api.delLinks(map(int, args[1:]))
- return ["INFO: Deleted %d links!" % len(args[1:])]
-
- else:
- return ["ERROR: Use del command like this: del <-p|-l> <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"]
-
-
- def event_push(self, args):
- if not args:
- return ["ERROR: Push package to queue like this: push <package id>"]
-
- id = int(args[0])
- try:
- info = self.core.api.getPackageInfo(id)
- except PackageDoesNotExists:
- return ["ERROR: Package #%d does not exist." % id]
-
- self.core.api.pushToQueue(id)
- return ["INFO: Pushed package #%d to queue." % id]
-
-
- def event_pull(self, args):
- if not args:
- return ["ERROR: Pull package from queue like this: pull <package id>."]
-
- id = int(args[0])
- if not self.core.api.getPackageData(id):
- return ["ERROR: Package #%d does not exist." % id]
-
- self.core.api.pullFromQueue(id)
- return ["INFO: Pulled package #%d from queue to collector." % id]
-
-
- def event_c(self, args):
- """ captcha answer """
- if not args:
- return ["ERROR: Captcha ID missing."]
-
- task = self.core.captchaManager.getTaskByID(args[0])
- if not task:
- return ["ERROR: Captcha Task with ID %s does not exists." % args[0]]
-
- task.setResult(" ".join(args[1:]))
- return ["INFO: Result %s saved." % " ".join(args[1:])]
-
-
- def event_help(self, args):
- lines = ["The following commands are available:",
- "add <package|packid> <links> [...] Adds link to package. (creates new package if it does not exist)",
- "queue Shows all packages in the queue",
- "collector Shows all packages in collector",
- "del -p|-l <id> [...] Deletes all packages|links with the ids specified",
- "info <id> Shows info of the link with id <id>",
- "packinfo <id> Shows info of the package with id <id>",
- "more Shows more info when the result was truncated",
- "start Starts all downloads",
- "stop Stops the download (but not abort active downloads)",
- "push <id> Push package to queue",
- "pull <id> Pull package from queue",
- "status Show general download status",
- "help Shows this help message"]
- return lines
-
-
-class IRCError(Exception):
-
- def __init__(self, value):
- self.value = value
-
-
- def __str__(self):
- return repr(self.value)
diff --git a/module/plugins/hooks/ImageTyperz.py b/module/plugins/hooks/ImageTyperz.py
deleted file mode 100644
index b00c5118f..000000000
--- a/module/plugins/hooks/ImageTyperz.py
+++ /dev/null
@@ -1,157 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-import re
-
-from base64 import b64encode
-from pycurl import FORM_FILE, LOW_SPEED_TIME
-from thread import start_new_thread
-
-from module.network.RequestFactory import getURL, getRequest
-from module.plugins.Hook import Hook
-
-
-class ImageTyperzException(Exception):
-
- def __init__(self, err):
- self.err = err
-
-
- def getCode(self):
- return self.err
-
-
- def __str__(self):
- return "<ImageTyperzException %s>" % self.err
-
-
- def __repr__(self):
- return "<ImageTyperzException %s>" % self.err
-
-
-class ImageTyperz(Hook):
- __name__ = "ImageTyperz"
- __type__ = "hook"
- __version__ = "0.04"
-
- __config__ = [("username", "str", "Username", ""),
- ("passkey", "password", "Password", ""),
- ("force", "bool", "Force IT even if client is connected", False)]
-
- __description__ = """Send captchas to ImageTyperz.com"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- SUBMIT_URL = "http://captchatypers.com/Forms/UploadFileAndGetTextNEW.ashx"
- RESPOND_URL = "http://captchatypers.com/Forms/SetBadImage.ashx"
- GETCREDITS_URL = "http://captchatypers.com/Forms/RequestBalance.ashx"
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
-
-
- def getCredits(self):
- res = getURL(self.GETCREDITS_URL,
- post={'action': "REQUESTBALANCE",
- 'username': self.getConfig("username"),
- 'password': self.getConfig("passkey")})
-
- if res.startswith('ERROR'):
- raise ImageTyperzException(res)
-
- try:
- balance = float(res)
- except:
- raise ImageTyperzException("Invalid response")
-
- self.logInfo(_("Account balance: $%s left") % res)
- return balance
-
-
- def submit(self, captcha, captchaType="file", match=None):
- req = getRequest()
- #raise timeout threshold
- req.c.setopt(LOW_SPEED_TIME, 80)
-
- try:
- #workaround multipart-post bug in HTTPRequest.py
- if re.match("^\w*$", self.getConfig("passkey")):
- multipart = True
- data = (FORM_FILE, captcha)
- else:
- multipart = False
- with open(captcha, 'rb') as f:
- data = f.read()
- data = b64encode(data)
-
- res = req.load(self.SUBMIT_URL,
- post={'action': "UPLOADCAPTCHA",
- 'username': self.getConfig("username"),
- 'password': self.getConfig("passkey"), "file": data},
- multipart=multipart)
- finally:
- req.close()
-
- if res.startswith("ERROR"):
- raise ImageTyperzException(res)
- else:
- data = res.split('|')
- if len(data) == 2:
- ticket, result = data
- else:
- raise ImageTyperzException("Unknown response: %s" % res)
-
- return ticket, result
-
-
- def newCaptchaTask(self, task):
- if "service" in task.data:
- return False
-
- if not task.isTextual():
- return False
-
- if not self.getConfig("username") or not self.getConfig("passkey"):
- return False
-
- if self.core.isClientConnected() and not self.getConfig("force"):
- return False
-
- if self.getCredits() > 0:
- task.handler.append(self)
- task.data['service'] = self.__name__
- task.setWaiting(100)
- start_new_thread(self.processCaptcha, (task,))
-
- else:
- self.logInfo(_("Your %s account has not enough credits") % self.__name__)
-
-
- def captchaInvalid(self, task):
- if task.data['service'] == self.__name__ and "ticket" in task.data:
- res = getURL(self.RESPOND_URL,
- post={'action': "SETBADIMAGE",
- 'username': self.getConfig("username"),
- 'password': self.getConfig("passkey"),
- 'imageid': task.data['ticket']})
-
- if res == "SUCCESS":
- self.logInfo(_("Bad captcha solution received, requested refund"))
- else:
- self.logError(_("Bad captcha solution received, refund request failed"), res)
-
-
- def processCaptcha(self, task):
- c = task.captchaFile
- try:
- ticket, result = self.submit(c)
- except ImageTyperzException, e:
- task.error = e.getCode()
- return
-
- task.data['ticket'] = ticket
- task.setResult(result)
diff --git a/module/plugins/hooks/LinkdecrypterCom.py b/module/plugins/hooks/LinkdecrypterCom.py
deleted file mode 100644
index 0c5f6e754..000000000
--- a/module/plugins/hooks/LinkdecrypterCom.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.network.RequestFactory import getURL
-from module.plugins.Hook import Hook
-from module.utils import remove_chars
-
-
-class LinkdecrypterCom(Hook):
- __name__ = "LinkdecrypterCom"
- __type__ = "hook"
- __version__ = "0.20"
-
- __description__ = """Linkdecrypter.com hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- def coreReady(self):
- try:
- self.loadPatterns()
- except Exception, e:
- self.logError(e)
-
-
- def loadPatterns(self):
- html = getURL("http://linkdecrypter.com/")
-
- m = re.search(r'<title>', html)
- if m is None:
- self.logError(_("Linkdecrypter site is down"))
- return
-
- m = re.search(r'<b>Supported\(\d+\)</b>: <i>([^+<]*)', html)
- if m is None:
- self.logError(_("Crypter list not found"))
- return
-
- builtin = [name.lower() for name in self.core.pluginManager.crypterPlugins.keys()]
- builtin.append("downloadserienjunkiesorg")
-
- crypter_pattern = re.compile("(\w[\w.-]+)")
- online = []
- for crypter in m.group(1).split(', '):
- m = re.match(crypter_pattern, crypter)
- if m and remove_chars(m.group(1), "-.") not in builtin:
- online.append(m.group(1).replace(".", "\\."))
-
- if not online:
- self.logError(_("Crypter list is empty"))
- return
-
- regexp = r'https?://([^.]+\.)*?(%s)/.*' % '|'.join(online)
-
- dict = self.core.pluginManager.crypterPlugins[self.__name__]
- dict['pattern'] = regexp
- dict['re'] = re.compile(regexp)
-
- self.logDebug("Loaded pattern: %s" % regexp)
diff --git a/module/plugins/hooks/LinksnappyCom.py b/module/plugins/hooks/LinksnappyCom.py
deleted file mode 100644
index 82edc30fd..000000000
--- a/module/plugins/hooks/LinksnappyCom.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.common.json_layer import json_loads
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class LinksnappyCom(MultiHoster):
- __name__ = "LinksnappyCom"
- __type__ = "hook"
- __version__ = "0.01"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", ""),
- ("unloadFailing", "bool", "Revert to standard download if download fails", False),
- ("interval", "int", "Reload interval in hours (0 to disable)", 24)]
-
- __description__ = """Linksnappy.com hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- def getHoster(self):
- json_data = getURL("http://gen.linksnappy.com/lseAPI.php", get={'act': "FILEHOSTS"})
- json_data = json_loads(json_data)
-
- return json_data['return'].keys()
diff --git a/module/plugins/hooks/MegaDebridEu.py b/module/plugins/hooks/MegaDebridEu.py
deleted file mode 100644
index 4f627b7e9..000000000
--- a/module/plugins/hooks/MegaDebridEu.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.common.json_layer import json_loads
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class MegaDebridEu(MultiHoster):
- __name__ = "MegaDebridEu"
- __type__ = "hook"
- __version__ = "0.02"
-
- __config__ = [("unloadFailing", "bool", "Revert to standard download if download fails", False)]
-
- __description__ = """mega-debrid.eu hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("D.Ducatel", "dducatel@je-geek.fr")]
-
-
- def getHoster(self):
- reponse = getURL("http://www.mega-debrid.eu/api.php", get={'action': "getHosters"})
- json_data = json_loads(reponse)
-
- if json_data['response_code'] == "ok":
- host_list = [element[0] for element in json_data['hosters']]
- else:
- self.logError(_("Unable to retrieve hoster list"))
- host_list = list()
-
- return host_list
diff --git a/module/plugins/hooks/MergeFiles.py b/module/plugins/hooks/MergeFiles.py
deleted file mode 100644
index e6f8bb26f..000000000
--- a/module/plugins/hooks/MergeFiles.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import os
-import re
-
-from traceback import print_exc
-
-from module.plugins.Hook import Hook, threaded
-from module.utils import save_join, fs_encode
-
-
-class MergeFiles(Hook):
- __name__ = "MergeFiles"
- __type__ = "hook"
- __version__ = "0.12"
-
- __config__ = [("activated", "bool", "Activated", True)]
-
- __description__ = """Merges parts splitted with hjsplit"""
- __license__ = "GPLv3"
- __authors__ = [("and9000", "me@has-no-mail.com")]
-
-
- BUFFER_SIZE = 4096
-
-
- def setup(self):
- # nothing to do
- pass
-
-
- @threaded
- def packageFinished(self, pack):
- files = {}
- fid_dict = {}
- for fid, data in pack.getChildren().iteritems():
- if re.search("\.\d{3}$", data['name']):
- if data['name'][:-4] not in files:
- files[data['name'][:-4]] = []
- files[data['name'][:-4]].append(data['name'])
- files[data['name'][:-4]].sort()
- fid_dict[data['name']] = fid
-
- download_folder = self.config['general']['download_folder']
-
- if self.config['general']['folder_per_package']:
- download_folder = save_join(download_folder, pack.folder)
-
- for name, file_list in files.iteritems():
- self.logInfo(_("Starting merging of"), name)
- final_file = open(save_join(download_folder, name), "wb")
-
- for splitted_file in file_list:
- self.logDebug("Merging part", splitted_file)
- pyfile = self.core.files.getFile(fid_dict[splitted_file])
- pyfile.setStatus("processing")
- try:
- s_file = open(os.path.join(download_folder, splitted_file), "rb")
- size_written = 0
- s_file_size = int(os.path.getsize(os.path.join(download_folder, splitted_file)))
- while True:
- f_buffer = s_file.read(self.BUFFER_SIZE)
- if f_buffer:
- final_file.write(f_buffer)
- size_written += self.BUFFER_SIZE
- pyfile.setProgress((size_written * 100) / s_file_size)
- else:
- break
- s_file.close()
- self.logDebug("Finished merging part", splitted_file)
- except Exception, e:
- print_exc()
- finally:
- pyfile.setProgress(100)
- pyfile.setStatus("finished")
- pyfile.release()
-
- final_file.close()
- self.logInfo(_("Finished merging of"), name)
diff --git a/module/plugins/hooks/MultiHome.py b/module/plugins/hooks/MultiHome.py
deleted file mode 100644
index 228e6027d..000000000
--- a/module/plugins/hooks/MultiHome.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from time import time
-
-from module.plugins.Hook import Hook
-
-
-class MultiHome(Hook):
- __name__ = "MultiHome"
- __type__ = "hook"
- __version__ = "0.11"
-
- __config__ = [("interfaces", "str", "Interfaces", "None")]
-
- __description__ = """Ip address changer"""
- __license__ = "GPLv3"
- __authors__ = [("mkaay", "mkaay@mkaay.de")]
-
-
- def setup(self):
- self.register = {}
- self.interfaces = []
- self.parseInterfaces(self.getConfig("interfaces").split(";"))
- if not self.interfaces:
- self.parseInterfaces([self.config['download']['interface']])
- self.setConfig("interfaces", self.toConfig())
-
-
- def toConfig(self):
- return ";".join([i.adress for i in self.interfaces])
-
-
- def parseInterfaces(self, interfaces):
- for interface in interfaces:
- if not interface or str(interface).lower() == "none":
- continue
- self.interfaces.append(Interface(interface))
-
-
- def coreReady(self):
- requestFactory = self.core.requestFactory
- oldGetRequest = requestFactory.getRequest
-
-
- def getRequest(pluginName, account=None):
- iface = self.bestInterface(pluginName, account)
- if iface:
- iface.useFor(pluginName, account)
- requestFactory.iface = lambda: iface.adress
- self.logDebug("Using address", iface.adress)
- return oldGetRequest(pluginName, account)
-
- requestFactory.getRequest = getRequest
-
-
- def bestInterface(self, pluginName, account):
- best = None
- for interface in self.interfaces:
- if not best or interface.lastPluginAccess(pluginName, account) < best.lastPluginAccess(pluginName, account):
- best = interface
- return best
-
-
-class Interface(object):
-
- def __init__(self, adress):
- self.adress = adress
- self.history = {}
-
-
- def lastPluginAccess(self, pluginName, account):
- if (pluginName, account) in self.history:
- return self.history[(pluginName, account)]
- return 0
-
-
- def useFor(self, pluginName, account):
- self.history[(pluginName, account)] = time()
-
-
- def __repr__(self):
- return "<Interface - %s>" % self.adress
diff --git a/module/plugins/hooks/MultishareCz.py b/module/plugins/hooks/MultishareCz.py
deleted file mode 100644
index 0e651393d..000000000
--- a/module/plugins/hooks/MultishareCz.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class MultishareCz(MultiHoster):
- __name__ = "MultishareCz"
- __type__ = "hook"
- __version__ = "0.04"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", "uloz.to")]
-
- __description__ = """MultiShare.cz hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- HOSTER_PATTERN = r'<img class="logo-shareserveru"[^>]*?alt="([^"]+)"></td>\s*<td class="stav">[^>]*?alt="OK"'
-
-
- def getHoster(self):
- page = getURL("http://www.multishare.cz/monitoring/")
- return re.findall(self.HOSTER_PATTERN, page)
diff --git a/module/plugins/hooks/MyfastfileCom.py b/module/plugins/hooks/MyfastfileCom.py
deleted file mode 100644
index 0cf2c6c22..000000000
--- a/module/plugins/hooks/MyfastfileCom.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.common.json_layer import json_loads
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class MyfastfileCom(MultiHoster):
- __name__ = "MyfastfileCom"
- __type__ = "hook"
- __version__ = "0.02"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", ""),
- ("unloadFailing", "bool", "Revert to standard download if download fails", False),
- ("interval", "int", "Reload interval in hours (0 to disable)", 24)]
-
- __description__ = """Myfastfile.com hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- def getHoster(self):
- json_data = getURL("http://myfastfile.com/api.php", get={'hosts': ""}, decode=True)
- self.logDebug("JSON data", json_data)
- json_data = json_loads(json_data)
-
- return json_data['hosts']
diff --git a/module/plugins/hooks/OverLoadMe.py b/module/plugins/hooks/OverLoadMe.py
deleted file mode 100644
index baa9b0e0a..000000000
--- a/module/plugins/hooks/OverLoadMe.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class OverLoadMe(MultiHoster):
- __name__ = "OverLoadMe"
- __type__ = "hook"
- __version__ = "0.01"
-
- __config__ = [("https", "bool", "Enable HTTPS", True),
- ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", ""),
- ("unloadFailing", "bool", "Revert to standard download if download fails", False),
- ("interval", "int", "Reload interval in hours (0 to disable)", 12)]
-
- __description__ = """Over-Load.me hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("marley", "marley@over-load.me")]
-
-
- def getHoster(self):
- https = "https" if self.getConfig("https") else "http"
- page = getURL(https + "://api.over-load.me/hoster.php",
- get={'auth': "0001-cb1f24dadb3aa487bda5afd3b76298935329be7700cd7-5329be77-00cf-1ca0135f"}).replace("\"", "").strip()
- self.logDebug("Hosterlist", page)
-
- return [x.strip() for x in page.split(",") if x.strip()]
diff --git a/module/plugins/hooks/PremiumTo.py b/module/plugins/hooks/PremiumTo.py
deleted file mode 100644
index 7be46945f..000000000
--- a/module/plugins/hooks/PremiumTo.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class PremiumTo(MultiHoster):
- __name__ = "PremiumTo"
- __type__ = "hook"
- __version__ = "0.04"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for downloads from supported hosters:", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", "")]
-
- __description__ = """Premium.to hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- def getHoster(self):
- page = getURL("http://premium.to/api/hosters.php",
- get={'username': self.account.username, 'password': self.account.password})
- return [x.strip() for x in page.replace("\"", "").split(";")]
-
-
- def coreReady(self):
- self.account = self.core.accountManager.getAccountPlugin("PremiumTo")
-
- user = self.account.selectAccount()[0]
-
- if not user:
- self.logError(_("Please add your premium.to account first and restart pyLoad"))
- return
-
- return MultiHoster.coreReady(self)
diff --git a/module/plugins/hooks/PremiumizeMe.py b/module/plugins/hooks/PremiumizeMe.py
deleted file mode 100644
index c18e8cf8e..000000000
--- a/module/plugins/hooks/PremiumizeMe.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.common.json_layer import json_loads
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class PremiumizeMe(MultiHoster):
- __name__ = "PremiumizeMe"
- __type__ = "hook"
- __version__ = "0.12"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", ""),
- ("unloadFailing", "bool", "Revert to stanard download if download fails", False),
- ("interval", "int", "Reload interval in hours (0 to disable)", 24)]
-
- __description__ = """Premiumize.me hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Florian Franzen", "FlorianFranzen@gmail.com")]
-
-
- def getHoster(self):
- # If no accounts are available there will be no hosters available
- if not self.account or not self.account.canUse():
- return []
-
- # Get account data
- (user, data) = self.account.selectAccount()
-
- # Get supported hosters list from premiumize.me using the
- # json API v1 (see https://secure.premiumize.me/?show=api)
- answer = getURL("https://api.premiumize.me/pm-api/v1.php"
- get={'method': "hosterlist", 'params[login]': user, 'params[pass]': data['password']})
- data = json_loads(answer)
-
- # If account is not valid thera are no hosters available
- if data['status'] != 200:
- return []
-
- # Extract hosters from json file
- return data['result']['hosterlist']
-
-
- def coreReady(self):
- # Get account plugin and check if there is a valid account available
- self.account = self.core.accountManager.getAccountPlugin("PremiumizeMe")
- if not self.account.canUse():
- self.account = None
- self.logError(_("Please add a valid premiumize.me account first and restart pyLoad"))
- return
-
- # Run the overwriten core ready which actually enables the multihoster hook
- return MultiHoster.coreReady(self)
diff --git a/module/plugins/hooks/RPNetBiz.py b/module/plugins/hooks/RPNetBiz.py
deleted file mode 100644
index e38aa8ea0..000000000
--- a/module/plugins/hooks/RPNetBiz.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.common.json_layer import json_loads
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class RPNetBiz(MultiHoster):
- __name__ = "RPNetBiz"
- __type__ = "hook"
- __version__ = "0.1"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", ""),
- ("unloadFailing", "bool", "Revert to stanard download if download fails", False),
- ("interval", "int", "Reload interval in hours (0 to disable)", 24)]
-
- __description__ = """RPNet.biz hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Dman", "dmanugm@gmail.com")]
-
-
- def getHoster(self):
- # No hosts supported if no account
- if not self.account or not self.account.canUse():
- return []
-
- # Get account data
- (user, data) = self.account.selectAccount()
-
- res = getURL("https://premium.rpnet.biz/client_api.php",
- get={'username': user, 'password': data['password'], 'action': "showHosterList"})
- hoster_list = json_loads(res)
-
- # If account is not valid thera are no hosters available
- if 'error' in hoster_list:
- return []
-
- # Extract hosters from json file
- return hoster_list['hosters']
-
-
- def coreReady(self):
- # Get account plugin and check if there is a valid account available
- self.account = self.core.accountManager.getAccountPlugin("RPNetBiz")
- if not self.account.canUse():
- self.account = None
- self.logError(_("Please enter your %s account or deactivate this plugin") % "rpnet")
- return
-
- # Run the overwriten core ready which actually enables the multihoster hook
- return MultiHoster.coreReady(self)
diff --git a/module/plugins/hooks/RealdebridCom.py b/module/plugins/hooks/RealdebridCom.py
deleted file mode 100644
index 50cc81f0c..000000000
--- a/module/plugins/hooks/RealdebridCom.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class RealdebridCom(MultiHoster):
- __name__ = "RealdebridCom"
- __type__ = "hook"
- __version__ = "0.43"
-
- __config__ = [("https", "bool", "Enable HTTPS", False),
- ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported):", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", ""),
- ("unloadFailing", "bool", "Revert to stanard download if download fails", False),
- ("interval", "int", "Reload interval in hours (0 to disable)", 24)]
-
- __description__ = """Real-Debrid.com hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Devirex Hazzard", "naibaf_11@yahoo.de")]
-
-
- def getHoster(self):
- https = "https" if self.getConfig("https") else "http"
- page = getURL(https + "://real-debrid.com/api/hosters.php").replace("\"", "").strip()
-
- return [x.strip() for x in page.split(",") if x.strip()]
diff --git a/module/plugins/hooks/RehostTo.py b/module/plugins/hooks/RehostTo.py
deleted file mode 100644
index 1bf7d2555..000000000
--- a/module/plugins/hooks/RehostTo.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class RehostTo(MultiHoster):
- __name__ = "RehostTo"
- __type__ = "hook"
- __version__ = "0.43"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", ""),
- ("unloadFailing", "bool", "Revert to stanard download if download fails", False),
- ("interval", "int", "Reload interval in hours (0 to disable)", 24)]
-
- __description__ = """Rehost.to hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org")]
-
-
- def getHoster(self):
- page = getURL("http://rehost.to/api.php",
- get={'cmd': "get_supported_och_dl", 'long_ses': self.long_ses})
- return [x.strip() for x in page.replace("\"", "").split(",")]
-
-
- def coreReady(self):
- self.account = self.core.accountManager.getAccountPlugin("RehostTo")
-
- user = self.account.selectAccount()[0]
-
- if not user:
- self.logError(_("Please add your rehost.to account first and restart pyLoad"))
- return
-
- data = self.account.getAccountInfo(user)
- self.ses = data['ses']
- self.long_ses = data['long_ses']
-
- return MultiHoster.coreReady(self)
diff --git a/module/plugins/hooks/RestartFailed.py b/module/plugins/hooks/RestartFailed.py
deleted file mode 100644
index 07fb80967..000000000
--- a/module/plugins/hooks/RestartFailed.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Hook import Hook
-
-
-class RestartFailed(Hook):
- __name__ = "RestartFailed"
- __type__ = "hook"
- __version__ = "1.57"
-
- __config__ = [("interval", "int", "Check interval in minutes", 90)]
-
- __description__ = """Periodically restart all failed downloads in queue"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- # event_list = ["pluginConfigChanged"]
-
- MIN_INTERVAL = 15 * 60 #: 15m minimum check interval (value is in seconds)
-
-
- def pluginConfigChanged(self, plugin, name, value):
- if name == "interval":
- interval = value * 60
- if self.MIN_INTERVAL <= interval != self.interval:
- self.core.scheduler.removeJob(self.cb)
- self.interval = interval
- self.initPeriodical()
- else:
- self.logDebug("Invalid interval value, kept current")
-
-
- def periodical(self):
- self.logDebug(_("Restart failed downloads"))
- self.core.api.restartFailed()
-
-
- def setup(self):
- self.interval = self.MIN_INTERVAL
-
-
- def coreReady(self):
- self.pluginConfigChanged(self.__name__, "interval", self.getConfig("interval"))
diff --git a/module/plugins/hooks/SimplyPremiumCom.py b/module/plugins/hooks/SimplyPremiumCom.py
deleted file mode 100644
index cc7e9183c..000000000
--- a/module/plugins/hooks/SimplyPremiumCom.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.common.json_layer import json_loads
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class SimplyPremiumCom(MultiHoster):
- __name__ = "SimplyPremiumCom"
- __type__ = "hook"
- __version__ = "0.02"
-
- __config__ = [("activated", "bool", "Activated", "False"),
- ("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", ""),
- ("unloadFailing", "bool", "Revert to standard download if download fails", "False"),
- ("interval", "int", "Reload interval in hours (0 to disable)", "24")]
-
- __description__ = """Simply-Premium.com hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("EvolutionClip", "evolutionclip@live.de")]
-
-
- def getHoster(self):
- json_data = getURL("http://www.simply-premium.com/api/hosts.php", get={'format': "json", 'online': 1})
- json_data = json_loads(json_data)
-
- host_list = [element['regex'] for element in json_data['result']]
-
- return host_list
diff --git a/module/plugins/hooks/SimplydebridCom.py b/module/plugins/hooks/SimplydebridCom.py
deleted file mode 100644
index 173206e75..000000000
--- a/module/plugins/hooks/SimplydebridCom.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class SimplydebridCom(MultiHoster):
- __name__ = "SimplydebridCom"
- __type__ = "hook"
- __version__ = "0.01"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", "")]
-
- __description__ = """Simply-Debrid.com hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Kagenoshin", "kagenoshin@gmx.ch")]
-
-
- def getHoster(self):
- page = getURL("http://simply-debrid.com/api.php", get={'list': 1})
- return [x.strip() for x in page.rstrip(';').replace("\"", "").split(";")]
diff --git a/module/plugins/hooks/UnSkipOnFail.py b/module/plugins/hooks/UnSkipOnFail.py
deleted file mode 100644
index f97d12431..000000000
--- a/module/plugins/hooks/UnSkipOnFail.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from os.path import basename
-
-from module.PyFile import PyFile
-from module.plugins.Hook import Hook
-from module.utils import fs_encode
-
-
-class UnSkipOnFail(Hook):
- __name__ = "UnSkipOnFail"
- __type__ = "hook"
- __version__ = "0.01"
-
- __config__ = [("activated", "bool", "Activated", True)]
-
- __description__ = """When a download fails, restart skipped duplicates"""
- __license__ = "GPLv3"
- __authors__ = [("hagg", None)]
-
-
- def downloadFailed(self, pyfile):
- pyfile_name = basename(pyfile.name)
- pid = pyfile.package().id
- msg = _('look for skipped duplicates for %s (pid:%s)')
- self.logInfo(msg % (pyfile_name, pid))
- dups = self.findDuplicates(pyfile)
- for link in dups:
- # check if link is "skipped"(=4)
- if link.status == 4:
- lpid = link.packageID
- self.logInfo(_('restart "%s" (pid:%s)') % (pyfile_name, lpid))
- self.setLinkStatus(link, "queued")
-
-
- def findDuplicates(self, pyfile):
- """ Search all packages for duplicate links to "pyfile".
- Duplicates are links that would overwrite "pyfile".
- To test on duplicity the package-folder and link-name
- of twolinks are compared (basename(link.name)).
- So this method returns a list of all links with equal
- package-folders and filenames as "pyfile", but except
- the data for "pyfile" iotselöf.
- It does MOT check the link's status.
- """
- dups = []
- pyfile_name = fs_encode(basename(pyfile.name))
- # get packages (w/o files, as most file data is useless here)
- queue = self.core.api.getQueue()
- for package in queue:
- # check if package-folder equals pyfile's package folder
- if fs_encode(package.folder) == fs_encode(pyfile.package().folder):
- # now get packaged data w/ files/links
- pdata = self.core.api.getPackageData(package.pid)
- if pdata.links:
- for link in pdata.links:
- link_name = fs_encode(basename(link.name))
- # check if link name collides with pdata's name
- if link_name == pyfile_name:
- # at last check if it is not pyfile itself
- if link.fid != pyfile.id:
- dups.append(link)
- return dups
-
-
- def setLinkStatus(self, link, new_status):
- """ Change status of "link" to "new_status".
- "link" has to be a valid FileData object,
- "new_status" has to be a valid status name
- (i.e. "queued" for this Plugin)
- It creates a temporary PyFile object using
- "link" data, changes its status, and tells
- the core.files-manager to save its data.
- """
- pyfile = PyFile(self.core.files,
- link.fid,
- link.url,
- link.name,
- link.size,
- link.status,
- link.error,
- link.plugin,
- link.packageID,
- link.order)
- pyfile.setStatus(new_status)
- self.core.files.save()
- pyfile.release()
diff --git a/module/plugins/hooks/UnrestrictLi.py b/module/plugins/hooks/UnrestrictLi.py
deleted file mode 100644
index d87265ef4..000000000
--- a/module/plugins/hooks/UnrestrictLi.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.common.json_layer import json_loads
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class UnrestrictLi(MultiHoster):
- __name__ = "UnrestrictLi"
- __type__ = "hook"
- __version__ = "0.02"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", ""),
- ("unloadFailing", "bool", "Revert to standard download if download fails", False),
- ("interval", "int", "Reload interval in hours (0 to disable)", 24),
- ("history", "bool", "Delete History", False)]
-
- __description__ = """Unrestrict.li hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- def getHoster(self):
- json_data = getURL("http://unrestrict.li/api/jdownloader/hosts.php", get={'format': "json"})
- json_data = json_loads(json_data)
-
- host_list = [element['host'] for element in json_data['result']]
-
- return host_list
diff --git a/module/plugins/hooks/UpdateManager.py b/module/plugins/hooks/UpdateManager.py
deleted file mode 100644
index c72699228..000000000
--- a/module/plugins/hooks/UpdateManager.py
+++ /dev/null
@@ -1,310 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-import re
-import sys
-
-from operator import itemgetter
-from os import path, remove, stat
-
-from module.network.RequestFactory import getURL
-from module.plugins.Hook import Expose, Hook, threaded
-from module.utils import save_join
-
-
-class UpdateManager(Hook):
- __name__ = "UpdateManager"
- __type__ = "hook"
- __version__ = "0.42"
-
- __config__ = [("activated" , "bool" , "Activated" , True ),
- ("mode" , "pyLoad + plugins;plugins only", "Check updates for" , "pyLoad + plugins"),
- ("interval" , "int" , "Check interval in hours" , 8 ),
- ("autorestart" , "bool" , "Automatically restart pyLoad when required" , True ),
- ("reloadplugins", "bool" , "Monitor plugins for code changes in debug mode", True ),
- ("nodebugupdate", "bool" , "Don't check for updates in debug mode" , True )]
-
- __description__ = """ Check for updates """
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- # event_list = ["pluginConfigChanged"]
-
- SERVER_URL = "http://updatemanager.pyload.org"
- VERSION = re.compile(r'__version__.*=.*("|\')([\d.]+)')
- MIN_INTERVAL = 3 * 60 * 60 #: 3h minimum check interval (value is in seconds)
-
-
- def pluginConfigChanged(self, plugin, name, value):
- if name == "interval":
- interval = value * 60 * 60
- if self.MIN_INTERVAL <= interval != self.interval:
- self.core.scheduler.removeJob(self.cb)
- self.interval = interval
- self.initPeriodical()
- else:
- self.logDebug("Invalid interval value, kept current")
-
- elif name == "reloadplugins":
- if self.cb2:
- self.core.scheduler.removeJob(self.cb2)
- if value is True and self.core.debug:
- self.periodical2()
-
-
- def coreReady(self):
- self.pluginConfigChanged(self.__name__, "interval", self.getConfig("interval"))
- x = lambda: self.pluginConfigChanged(self.__name__, "reloadplugins", self.getConfig("reloadplugins"))
- self.core.scheduler.addJob(10, x, threaded=False)
-
-
- def unload(self):
- self.pluginConfigChanged(self.__name__, "reloadplugins", False)
-
-
- def setup(self):
- self.cb2 = None
- self.interval = self.MIN_INTERVAL
- self.updating = False
- self.info = {'pyload': False, 'version': None, 'plugins': False}
- self.mtimes = {} #: store modification time for each plugin
-
-
- def periodical2(self):
- if not self.updating:
- self.autoreloadPlugins()
-
- self.cb2 = self.core.scheduler.addJob(4, self.periodical2, threaded=False)
-
-
- @Expose
- def autoreloadPlugins(self):
- """ reload and reindex all modified plugins """
- modules = filter(
- lambda m: m and (m.__name__.startswith("module.plugins.") or
- m.__name__.startswith("userplugins.")) and
- m.__name__.count(".") >= 2, sys.modules.itervalues()
- )
-
- reloads = []
-
- for m in modules:
- root, type, name = m.__name__.rsplit(".", 2)
- id = (type, name)
- if type in self.core.pluginManager.plugins:
- f = m.__file__.replace(".pyc", ".py")
- if not path.isfile(f):
- continue
-
- mtime = stat(f).st_mtime
-
- if id not in self.mtimes:
- self.mtimes[id] = mtime
- elif self.mtimes[id] < mtime:
- reloads.append(id)
- self.mtimes[id] = mtime
-
- return True if self.core.pluginManager.reloadPlugins(reloads) else False
-
-
- def periodical(self):
- if not self.info['pyload'] and not (self.getConfig("nodebugupdate") and self.core.debug):
- self.updateThread()
-
-
- def server_request(self):
- try:
- return getURL(self.SERVER_URL, get={'v': self.core.api.getServerVersion()}).splitlines()
- except:
- self.logWarning(_("Unable to contact server to get updates"))
-
-
- @threaded
- def updateThread(self):
- self.updating = True
-
- status = self.update(onlyplugin=self.getConfig("mode") == "plugins only")
-
- if status is 2 and self.getConfig("autorestart"):
- self.core.api.restart()
- else:
- self.updating = False
-
-
- @Expose
- def updatePlugins(self):
- """ simple wrapper for calling plugin update quickly """
- return self.update(onlyplugin=True)
-
-
- @Expose
- def update(self, onlyplugin=False):
- """ check for updates """
- data = self.server_request()
-
- if not data:
- exitcode = 0
-
- elif data[0] == "None":
- self.logInfo(_("No new pyLoad version available"))
- updates = data[1:]
- exitcode = self._updatePlugins(updates)
-
- elif onlyplugin:
- exitcode = 0
-
- else:
- newversion = data[0]
- self.logInfo(_("*** New pyLoad Version %s available ***") % newversion)
- self.logInfo(_("*** Get it here: https://github.com/pyload/pyload/releases ***"))
- exitcode = 3
- self.info['pyload'] = True
- self.info['version'] = newversion
-
- return exitcode #: 0 = No plugins updated; 1 = Plugins updated; 2 = Plugins updated, but restart required; 3 = No plugins updated, new pyLoad version available
-
-
- def _updatePlugins(self, updates):
- """ check for plugin updates """
-
- if self.info['plugins']:
- return False #: plugins were already updated
-
- exitcode = 0
- updated = []
-
- url = updates[0]
- schema = updates[1].split('|')
-
- if "BLACKLIST" in updates:
- blacklist = updates[updates.index('BLACKLIST') + 1:]
- updates = updates[2:updates.index('BLACKLIST')]
- else:
- blacklist = None
- updates = updates[2:]
-
- upgradable = [dict(zip(schema, x.split('|'))) for x in updates]
- blacklisted = [(x.split('|')[0], x.split('|')[1].rsplit('.', 1)[0]) for x in blacklist] if blacklist else []
-
- if blacklist:
- # Protect UpdateManager from self-removing
- try:
- blacklisted.remove(("hook", "UpdateManager"))
- except:
- pass
-
- for t, n in blacklisted:
- for idx, plugin in enumerate(upgradable):
- if n == plugin['name'] and t == plugin['type']:
- upgradable.pop(idx)
- break
-
- for t, n in self.removePlugins(sorted(blacklisted)):
- self.logInfo(_("Removed blacklisted plugin [%(type)s] %(name)s") % {
- 'type': t,
- 'name': n,
- })
-
- for plugin in sorted(upgradable, key=itemgetter("type", "name")):
- filename = plugin['name']
- prefix = plugin['type']
- version = plugin['version']
-
- if filename.endswith(".pyc"):
- name = filename[:filename.find("_")]
- else:
- name = filename.replace(".py", "")
-
- #@TODO: obsolete after 0.4.10
- if prefix.endswith("s"):
- type = prefix[:-1]
- else:
- type = prefix
-
- plugins = getattr(self.core.pluginManager, "%sPlugins" % type)
-
- oldver = float(plugins[name]['v']) if name in plugins else None
- newver = float(version)
-
- if not oldver:
- msg = "New plugin: [%(type)s] %(name)s (v%(newver).2f)"
- elif newver > oldver:
- msg = "New version of plugin: [%(type)s] %(name)s (v%(oldver).2f -> v%(newver).2f)"
- else:
- continue
-
- self.logInfo(_(msg) % {'type' : type,
- 'name' : name,
- 'oldver': oldver,
- 'newver': newver})
- try:
- content = getURL(url % plugin)
- m = self.VERSION.search(content)
-
- if m and m.group(2) == version:
- with open(save_join("userplugins", prefix, filename), "wb") as f:
- f.write(content)
-
- updated.append((prefix, name))
- else:
- raise Exception, _("Version mismatch")
-
- except Exception, e:
- self.logError(_("Error updating plugin: %s") % filename, str(e))
-
- if updated:
- reloaded = self.core.pluginManager.reloadPlugins(updated)
- if reloaded:
- self.logInfo(_("Plugins updated and reloaded"))
- exitcode = 1
- else:
- self.logInfo(_("*** Plugins have been updated, but need a pyLoad restart to be reloaded ***"))
- self.info['plugins'] = True
- exitcode = 2
- else:
- self.logInfo(_("No plugin updates available"))
-
- return exitcode #: 0 = No plugins updated; 1 = Plugins updated; 2 = Plugins updated, but restart required
-
-
- @Expose
- def removePlugins(self, type_plugins):
- """ delete plugins from disk """
-
- if not type_plugins:
- return
-
- self.logDebug("Requested deletion of plugins: %s" % type_plugins)
-
- removed = []
-
- for type, name in type_plugins:
- err = False
- file = name + ".py"
-
- for root in ("userplugins", path.join(pypath, "module", "plugins")):
-
- filename = save_join(root, type, file)
- try:
- remove(filename)
- except Exception, e:
- self.logDebug("Error removing: %s" % path.basename(filename), str(e))
- err = True
-
- filename += "c"
- if path.isfile(filename):
- try:
- if type == "hook":
- self.manager.deactivateHook(name)
- remove(filename)
- except Exception, e:
- self.logDebug("Error removing: %s" % path.basename(filename), str(e))
- err = True
-
- if not err:
- id = (type, name)
- removed.append(id)
-
- return removed #: return a list of the plugins successfully removed
diff --git a/module/plugins/hooks/WindowsPhoneToastNotify.py b/module/plugins/hooks/WindowsPhoneToastNotify.py
deleted file mode 100644
index 053ea47d0..000000000
--- a/module/plugins/hooks/WindowsPhoneToastNotify.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import httplib
-import time
-
-from module.plugins.Hook import Hook
-
-
-class WindowsPhoneToastNotify(Hook):
- __name__ = "WindowsPhoneToastNotify"
- __type__ = "hook"
- __version__ = "0.02"
-
- __config__ = [("force", "bool", "Force even if client is connected", False),
- ("pushId", "str", "pushId", ""),
- ("pushUrl", "str", "pushUrl", ""),
- ("pushTimeout", "int", "Timeout between notifications in seconds", 0)]
-
- __description__ = """Send push notifications to Windows Phone"""
- __license__ = "GPLv3"
- __authors__ = [("Andy Voigt", "phone-support@hotmail.de")]
-
-
- def setup(self):
- self.info = {} #@TODO: Remove in 0.4.10
-
-
- def getXmlData(self):
- myxml = ("<?xml version='1.0' encoding='utf-8'?> <wp:Notification xmlns:wp='WPNotification'> "
- "<wp:Toast> <wp:Text1>Pyload Mobile</wp:Text1> <wp:Text2>Captcha waiting!</wp:Text2> "
- "</wp:Toast> </wp:Notification>")
- return myxml
-
-
- def doRequest(self):
- URL = self.getConfig("pushUrl")
- request = self.getXmlData()
- webservice = httplib.HTTP(URL)
- webservice.putrequest("POST", self.getConfig("pushId"))
- webservice.putheader("Host", URL)
- webservice.putheader("Content-type", "text/xml")
- webservice.putheader("X-NotificationClass", "2")
- webservice.putheader("X-WindowsPhone-Target", "toast")
- webservice.putheader("Content-length", "%d" % len(request))
- webservice.endheaders()
- webservice.send(request)
- webservice.close()
- self.setStorage("LAST_NOTIFY", time.time())
-
-
- def newCaptchaTask(self, task):
- if not self.getConfig("pushId") or not self.getConfig("pushUrl"):
- return False
-
- if self.core.isClientConnected() and not self.getConfig("force"):
- return False
-
- if (time.time() - float(self.getStorage("LAST_NOTIFY", 0))) < self.getConf("pushTimeout"):
- return False
-
- self.doRequest()
diff --git a/module/plugins/hooks/XFileSharingPro.py b/module/plugins/hooks/XFileSharingPro.py
deleted file mode 100644
index fe955beb9..000000000
--- a/module/plugins/hooks/XFileSharingPro.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Hook import Hook
-
-
-class XFileSharingPro(Hook):
- __name__ = "XFileSharingPro"
- __type__ = "hook"
- __version__ = "0.25"
-
- __config__ = [("activated", "bool", "Activated", True),
- ("use_hoster_list", "bool", "Load listed hosters only", True),
- ("use_crypter_list", "bool", "Load listed crypters only", False),
- ("use_builtin_list", "bool", "Load built-in plugin list", True),
- ("hoster_list", "str", "Hoster list (comma separated)", ""),
- ("crypter_list", "str", "Crypter list (comma separated)", "")]
-
- __description__ = """Load XFileSharingPro based hosters and crypter which don't need a own plugin to run"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- # event_list = ["pluginConfigChanged"]
- regexp = {'hoster' : (r'https?://(?:www\.)?([\w.^_]+(?:\.[a-zA-Z]{2,})(?:\:\d+)?)/(?:embed-)?\w{12}(?:\W|$)',
- r'https?://(?:[^/]+\.)?(%s)/(?:embed-)?\w+'),
- 'crypter': (r'https?://(?:www\.)?([\w.^_]+(?:\.[a-zA-Z]{2,})(?:\:\d+)?)/(?:user|folder)s?/\w+',
- r'https?://(?:[^/]+\.)?(%s)/(?:user|folder)s?/\w+')}
-
- HOSTER_LIST = [#WORKING HOSTERS:
- "eyesfile.ca", "file4safe.com", "fileband.com", "filedwon.com", "filevice.com", "hostingbulk.com",
- "linestorage.com", "ravishare.com", "sharesix.com", "thefile.me", "verzend.be", "xvidstage.com",
- #NOT TESTED:
- "101shared.com", "4upfiles.com", "filemaze.ws", "filenuke.com", "linkzhost.com", "mightyupload.com",
- "rockdizfile.com", "sharebeast.com", "sharerepo.com", "shareswift.com", "uploadbaz.com", "uploadc.com",
- "vidbull.com", "zalaa.com", "zomgupload.com",
- #NOT WORKING:
- "amonshare.com", "banicrazy.info", "boosterking.com", "host4desi.com", "laoupload.com", "rd-fs.com"]
- CRYPTER_LIST = []
-
-
- # def pluginConfigChanged(self.__name__, plugin, name, value):
- # self.loadPattern()
-
-
- def coreReady(self):
- self.loadPattern()
-
-
- def loadPattern(self):
- use_builtin_list = self.getConfig('use_builtin_list')
-
- for type, plugin in (("hoster", "XFileSharingPro"),
- ("crypter", "XFileSharingProFolder")):
- every_plugin = not self.getConfig("use_%s_list" % type)
-
- if every_plugin:
- self.logInfo(_("Handling any %s I can!") % type)
- pattern = self.regexp[type][0]
- else:
- s = self.getConfig('%s_list' % type).replace('\\', '').replace('|', ',').replace(';', ',').lower()
- plugin_list = set([x.strip() for x in s.split(',')])
-
- if use_builtin_list:
- plugin_list |= set([x.lower() for x in getattr(self, "%s_LIST" % type.upper())])
-
- plugin_list -= set(('', u''))
-
- if not plugin_list:
- self.logInfo(_("No %s to handle") % type)
- self._unload(type, plugin)
- return
-
- match_list = '|'.join(sorted(plugin_list))
-
- len_match_list = len(plugin_list)
- self.logInfo(_("Handling %d %s%s: %s") % (len_match_list, type, "" if len_match_list is 1 else "s", match_list.replace('|', ', ')))
-
- pattern = self.regexp[type][1] % match_list.replace('.', '\.')
-
- dict = self.core.pluginManager.plugins[type][plugin]
- dict['pattern'] = pattern
- dict['re'] = re.compile(pattern)
-
- self.logDebug("Loaded %s pattern: %s" % (type, pattern))
-
-
- def _unload(self, type, plugin):
- dict = self.core.pluginManager.plugins[type][plugin]
- dict['pattern'] = r'^unmatchable$'
- dict['re'] = re.compile(dict['pattern'])
-
-
- def unload(self):
- for type, plugin in (("hoster", "XFileSharingPro"),
- ("crypter", "XFileSharingProFolder")):
- self._unload(type, plugin)
diff --git a/module/plugins/hooks/XMPPInterface.py b/module/plugins/hooks/XMPPInterface.py
deleted file mode 100644
index bbeab4341..000000000
--- a/module/plugins/hooks/XMPPInterface.py
+++ /dev/null
@@ -1,252 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from pyxmpp import streamtls
-from pyxmpp.all import JID, Message
-from pyxmpp.interface import implements
-from pyxmpp.interfaces import *
-from pyxmpp.jabber.client import JabberClient
-
-from module.plugins.hooks.IRCInterface import IRCInterface
-
-
-class XMPPInterface(IRCInterface, JabberClient):
- __name__ = "XMPPInterface"
- __type__ = "hook"
- __version__ = "0.11"
-
- __config__ = [("jid", "str", "Jabber ID", "user@exmaple-jabber-server.org"),
- ("pw", "str", "Password", ""),
- ("tls", "bool", "Use TLS", False),
- ("owners", "str", "List of JIDs accepting commands from", "me@icq-gateway.org;some@msn-gateway.org"),
- ("info_file", "bool", "Inform about every file finished", False),
- ("info_pack", "bool", "Inform about every package finished", True),
- ("captcha", "bool", "Send captcha requests", True)]
-
- __description__ = """Connect to jabber and let owner perform different tasks"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org")]
-
-
- implements(IMessageHandlersProvider)
-
-
- def __init__(self, core, manager):
- IRCInterface.__init__(self, core, manager)
-
- self.jid = JID(self.getConfig("jid"))
- password = self.getConfig("pw")
-
- # if bare JID is provided add a resource -- it is required
- if not self.jid.resource:
- self.jid = JID(self.jid.node, self.jid.domain, "pyLoad")
-
- if self.getConfig("tls"):
- tls_settings = streamtls.TLSSettings(require=True, verify_peer=False)
- auth = ("sasl:PLAIN", "sasl:DIGEST-MD5")
- else:
- tls_settings = None
- auth = ("sasl:DIGEST-MD5", "digest")
-
- # setup client with provided connection information
- # and identity data
- JabberClient.__init__(self, self.jid, password,
- disco_name="pyLoad XMPP Client", disco_type="bot",
- tls_settings=tls_settings, auth_methods=auth)
-
- self.interface_providers = [
- VersionHandler(self),
- self,
- ]
-
-
- def coreReady(self):
- self.new_package = {}
-
- self.start()
-
-
- def packageFinished(self, pypack):
- try:
- if self.getConfig("info_pack"):
- self.announce(_("Package finished: %s") % pypack.name)
- except:
- pass
-
-
- def downloadFinished(self, pyfile):
- try:
- if self.getConfig("info_file"):
- self.announce(
- _("Download finished: %(name)s @ %(plugin)s") % {"name": pyfile.name, "plugin": pyfile.pluginname})
- except:
- pass
-
-
- def run(self):
- # connect to IRC etc.
- self.connect()
- try:
- self.loop()
- except Exception, ex:
- self.logError(ex)
-
-
- def stream_state_changed(self, state, arg):
- """This one is called when the state of stream connecting the component
- to a server changes. This will usually be used to let the user
- know what is going on."""
- self.logDebug("*** State changed: %s %r ***" % (state, arg))
-
-
- def disconnected(self):
- self.logDebug("Client was disconnected")
-
-
- def stream_closed(self, stream):
- self.logDebug("Stream was closed", stream)
-
-
- def stream_error(self, err):
- self.logDebug("Stream Error", err)
-
-
- def get_message_handlers(self):
- """Return list of (message_type, message_handler) tuples.
-
- The handlers returned will be called when matching message is received
- in a client session."""
- return [("normal", self.message)]
-
-
- def message(self, stanza):
- """Message handler for the component."""
- subject = stanza.get_subject()
- body = stanza.get_body()
- t = stanza.get_type()
- self.logDebug("Message from %s received." % unicode(stanza.get_from()))
- self.logDebug("Body: %s Subject: %s Type: %s" % (body, subject, t))
-
- if t == "headline":
- # 'headline' messages should never be replied to
- return True
- if subject:
- subject = u"Re: " + subject
-
- to_jid = stanza.get_from()
- from_jid = stanza.get_to()
-
- #j = JID()
- to_name = to_jid.as_utf8()
- from_name = from_jid.as_utf8()
-
- names = self.getConfig("owners").split(";")
-
- if to_name in names or to_jid.node + "@" + to_jid.domain in names:
- messages = []
-
- trigger = "pass"
- args = None
-
- try:
- temp = body.split()
- trigger = temp[0]
- if len(temp) > 1:
- args = temp[1:]
- except:
- pass
-
- handler = getattr(self, "event_%s" % trigger, self.event_pass)
- try:
- res = handler(args)
- for line in res:
- m = Message(
- to_jid=to_jid,
- from_jid=from_jid,
- stanza_type=stanza.get_type(),
- subject=subject,
- body=line)
-
- messages.append(m)
- except Exception, e:
- self.logError(e)
-
- return messages
-
- else:
- return True
-
-
- def response(self, msg, origin=""):
- return self.announce(msg)
-
-
- def announce(self, message):
- """ send message to all owners"""
- for user in self.getConfig("owners").split(";"):
- self.logDebug("Send message to", user)
-
- to_jid = JID(user)
-
- m = Message(from_jid=self.jid,
- to_jid=to_jid,
- stanza_type="chat",
- body=message)
-
- stream = self.get_stream()
- if not stream:
- self.connect()
- stream = self.get_stream()
-
- stream.send(m)
-
-
- def beforeReconnecting(self, ip):
- self.disconnect()
-
-
- def afterReconnecting(self, ip):
- self.connect()
-
-
-class VersionHandler(object):
- """Provides handler for a version query.
-
- This class will answer version query and announce 'jabber:iq:version' namespace
- in the client's disco#info results."""
-
- implements(IIqHandlersProvider, IFeaturesProvider)
-
-
- def __init__(self, client):
- """Just remember who created this."""
- self.client = client
-
-
- def get_features(self):
- """Return namespace which should the client include in its reply to a
- disco#info query."""
- return ["jabber:iq:version"]
-
-
- def get_iq_get_handlers(self):
- """Return list of tuples (element_name, namespace, handler) describing
- handlers of <iq type='get'/> stanzas"""
- return [("query", "jabber:iq:version", self.get_version)]
-
-
- def get_iq_set_handlers(self):
- """Return empty list, as this class provides no <iq type='set'/> stanza handler."""
- return []
-
-
- def get_version(self, iq):
- """Handler for jabber:iq:version queries.
-
- jabber:iq:version queries are not supported directly by PyXMPP, so the
- XML node is accessed directly through the libxml2 API. This should be
- used very carefully!"""
- iq = iq.make_result_response()
- q = iq.new_query("jabber:iq:version")
- q.newTextChild(q.ns(), "name", "Echo component")
- q.newTextChild(q.ns(), "version", "1.0")
- return iq
diff --git a/module/plugins/hooks/ZeveraCom.py b/module/plugins/hooks/ZeveraCom.py
deleted file mode 100644
index 6fafb9666..000000000
--- a/module/plugins/hooks/ZeveraCom.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.MultiHoster import MultiHoster
-
-
-class ZeveraCom(MultiHoster):
- __name__ = "ZeveraCom"
- __type__ = "hook"
- __version__ = "0.02"
-
- __config__ = [("hosterListMode", "all;listed;unlisted", "Use for hosters (if supported)", "all"),
- ("hosterList", "str", "Hoster list (comma separated)", "")]
-
- __description__ = """Real-Debrid.com hook plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- def getHoster(self):
- page = getURL("http://www.zevera.com/jDownloader.ashx", get={'cmd': "gethosters"})
- return [x.strip() for x in page.replace("\"", "").split(",")]
diff --git a/module/plugins/hooks/__init__.py b/module/plugins/hooks/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/plugins/hooks/__init__.py
+++ /dev/null
diff --git a/module/plugins/hoster/AlldebridCom.py b/module/plugins/hoster/AlldebridCom.py
deleted file mode 100644
index bdd8ccdff..000000000
--- a/module/plugins/hoster/AlldebridCom.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from random import randrange
-from urllib import unquote
-
-from module.common.json_layer import json_loads
-from module.plugins.Hoster import Hoster
-from module.utils import parseFileSize
-
-
-class AlldebridCom(Hoster):
- __name__ = "AlldebridCom"
- __type__ = "hoster"
- __version__ = "0.34"
-
- __pattern__ = r'https?://(?:[^/]*\.)?alldebrid\..*'
-
- __description__ = """Alldebrid.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Andy Voigt", "spamsales@online.de")]
-
-
- def getFilename(self, url):
- try:
- name = unquote(url.rsplit("/", 1)[1])
- except IndexError:
- name = "Unknown_Filename..."
- if name.endswith("..."): # incomplete filename, append random stuff
- name += "%s.tmp" % randrange(100, 999)
- return name
-
-
- def setup(self):
- self.chunkLimit = 16
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- if re.match(self.__pattern__, pyfile.url):
- new_url = pyfile.url
- elif not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "AllDebrid")
- self.fail(_("No AllDebrid account provided"))
- else:
- self.logDebug("Old URL: %s" % pyfile.url)
- password = self.getPassword().splitlines()[0] or ""
-
- data = json_loads(self.load("http://www.alldebrid.com/service.php",
- get={'link': pyfile.url, 'json': "true", 'pw': password}))
-
- self.logDebug("Json data", data)
-
- if data['error']:
- if data['error'] == "This link isn't available on the hoster website.":
- self.offline()
- else:
- self.logWarning(data['error'])
- self.tempOffline()
- else:
- if pyfile.name and not pyfile.name.endswith('.tmp'):
- pyfile.name = data['filename']
- pyfile.size = parseFileSize(data['filesize'])
- new_url = data['link']
-
- if self.getConfig("https"):
- new_url = new_url.replace("http://", "https://")
- else:
- new_url = new_url.replace("https://", "http://")
-
- if new_url != pyfile.url:
- self.logDebug("New URL: %s" % new_url)
-
- if pyfile.name.startswith("http") or pyfile.name.startswith("Unknown"):
- #only use when name wasnt already set
- pyfile.name = self.getFilename(new_url)
-
- self.download(new_url, disposition=True)
-
- check = self.checkDownload({'error': "<title>An error occured while processing your request</title>",
- 'empty': re.compile(r"^$")})
-
- if check == "error":
- self.retry(wait_time=60, reason=_("An error occured while generating link"))
- elif check == "empty":
- self.retry(wait_time=60, reason=_("Downloaded File was empty"))
diff --git a/module/plugins/hoster/BasePlugin.py b/module/plugins/hoster/BasePlugin.py
deleted file mode 100644
index c8d632dc7..000000000
--- a/module/plugins/hoster/BasePlugin.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urllib import unquote
-from urlparse import urljoin, urlparse
-
-from module.network.HTTPRequest import BadHeader
-from module.plugins.internal.SimpleHoster import create_getInfo
-from module.plugins.Hoster import Hoster
-
-
-class BasePlugin(Hoster):
- __name__ = "BasePlugin"
- __type__ = "hoster"
- __version__ = "0.25"
-
- __pattern__ = r'^unmatchable$'
-
- __description__ = """Base Plugin when any other didnt fit"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- @classmethod
- def getInfo(cls, url="", html=""): #@TODO: Move to hoster class in 0.4.10
- return {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 3 if url else 1, 'url': unquote(url) or ""}
-
-
- def setup(self):
- self.chunkLimit = -1
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- """main function"""
-
- pyfile.name = self.getInfo(pyfile.url)['name']
-
- if not pyfile.url.startswith("http"):
- self.fail(_("No plugin matched"))
-
- for _i in xrange(5):
- try:
- self.downloadFile(pyfile)
-
- except BadHeader, e:
- if e.code is 404:
- self.offline()
-
- elif e.code in (401, 403):
- self.logDebug("Auth required", "Received HTTP status code: %d" % e.code)
-
- account = self.core.accountManager.getAccountPlugin('Http')
- servers = [x['login'] for x in account.getAllAccounts()]
- server = urlparse(pyfile.url).netloc
-
- if server in servers:
- self.logDebug("Logging on to %s" % server)
- self.req.addAuth(account.accounts[server]['password'])
- else:
- for pwd in self.getPassword().splitlines():
- if ":" in pwd:
- self.req.addAuth(pwd.strip())
- break
- else:
- self.fail(_("Authorization required"))
- else:
- self.fail(e)
- else:
- break
- else:
- self.fail(_("No file downloaded")) #@TODO: Move to hoster class in 0.4.10
-
- if self.checkDownload({'empty': re.compile(r"^$")}) is "empty": #@TODO: Move to hoster in 0.4.10
- self.fail(_("Empty file"))
-
-
- def downloadFile(self, pyfile):
- url = pyfile.url
-
- for i in xrange(1, 7): #@TODO: retrieve the pycurl.MAXREDIRS value set by req
- header = self.load(url, ref=True, cookies=True, just_header=True, decode=True)
-
- if 'location' not in header or not header['location']:
- if 'code' in header and header['code'] not in (200, 201, 203, 206):
- self.logDebug("Received HTTP status code: %d" % header['code'])
- self.fail(_("File not found"))
- else:
- break
-
- location = header['location']
-
- self.logDebug("Redirect #%d to: %s" % (i, location))
-
- if urlparse(location).scheme:
- url = location
- else:
- p = urlparse(url)
- base = "%s://%s" % (p.scheme, p.netloc)
- url = urljoin(base, location)
- else:
- self.fail(_("Too many redirects"))
-
- self.download(unquote(url), disposition=True)
diff --git a/module/plugins/hoster/BayfilesCom.py b/module/plugins/hoster/BayfilesCom.py
deleted file mode 100644
index 24c86f5be..000000000
--- a/module/plugins/hoster/BayfilesCom.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import time
-
-from module.common.json_layer import json_loads
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class BayfilesCom(SimpleHoster):
- __name__ = "BayfilesCom"
- __type__ = "hoster"
- __version__ = "0.08"
-
- __pattern__ = r'https?://(?:www\.)?bayfiles\.(com|net)/file/(?P<ID>\w+/\w+/[^/]+)'
-
- __description__ = """Bayfiles.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- INFO_PATTERN = r'<p title="(?P<N>[^"]+)">[^<]*<strong>(?P<S>[\d .,]+)(?P<U>[\w^_]+)</strong></p>'
- OFFLINE_PATTERN = r'(<p>The requested file could not be found.</p>|<title>404 Not Found</title>)'
-
- WAIT_PATTERN = r'>Your IP [\d.]* has recently downloaded a file\. Upgrade to premium or wait (\d+) minutes\.<'
- VARS_PATTERN = r'var vfid = (\d+);\s*var delay = (\d+);'
- FREE_LINK_PATTERN = r'javascript:window\.location\.href = \'(.+?)\';'
- PREMIUM_LINK_PATTERN = r'(?:<a class="highlighted-btn" href="|(?=http://s\d+\.baycdn\.com/dl/))(.*?)"'
-
-
- def handleFree(self):
- m = re.search(self.WAIT_PATTERN, self.html)
- if m:
- self.retry(wait_time=int(m.group(1)) * 60)
-
- # Get download token
- m = re.search(self.VARS_PATTERN, self.html)
- if m is None:
- self.error(_("VARS_PATTERN not found"))
- vfid, delay = m.groups()
-
- res = json_loads(self.load('http://bayfiles.com/ajax_download',
- get={"_": time() * 1000,
- "action": "startTimer",
- "vfid": vfid}, decode=True))
-
- if not "token" in res or not res['token']:
- self.fail(_("No token"))
-
- self.wait(int(delay))
-
- self.html = self.load('http://bayfiles.com/ajax_download', get={
- "token": res['token'],
- "action": "getLink",
- "vfid": vfid})
-
- # Get final link and download
- m = re.search(self.FREE_LINK_PATTERN, self.html)
- if m is None:
- self.error(_("Free link"))
- self.startDownload(m.group(1))
-
-
- def handlePremium(self):
- m = re.search(self.PREMIUM_LINK_PATTERN, self.html)
- if m is None:
- self.error(_("Premium link"))
- self.startDownload(m.group(1))
-
-
- def startDownload(self, url):
- self.logDebug("%s URL: %s" % ("Premium" if self.premium else "Free", url))
- self.download(url)
- # check download
- check = self.checkDownload({
- "waitforfreeslots": re.compile(r"<title>BayFiles</title>"),
- "notfound": re.compile(r"<title>404 Not Found</title>")
- })
- if check == "waitforfreeslots":
- self.retry(30, 5 * 60, "Wait for free slot")
- elif check == "notfound":
- self.retry(30, 5 * 60, "404 Not found")
-
-
-getInfo = create_getInfo(BayfilesCom)
diff --git a/module/plugins/hoster/BezvadataCz.py b/module/plugins/hoster/BezvadataCz.py
deleted file mode 100644
index 3a0b8e58c..000000000
--- a/module/plugins/hoster/BezvadataCz.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class BezvadataCz(SimpleHoster):
- __name__ = "BezvadataCz"
- __type__ = "hoster"
- __version__ = "0.25"
-
- __pattern__ = r'http://(?:www\.)?bezvadata\.cz/stahnout/.*'
-
- __description__ = """BezvaData.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<p><b>Soubor: (?P<N>[^<]+)</b></p>'
- SIZE_PATTERN = r'<li><strong>Velikost:</strong> (?P<S>[^<]+)</li>'
- OFFLINE_PATTERN = r'<title>BezvaData \| Soubor nenalezen</title>'
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
-
-
- def handleFree(self):
- #download button
- m = re.search(r'<a class="stahnoutSoubor".*?href="(.*?)"', self.html)
- if m is None:
- self.error(_("Page 1 URL not found"))
- url = "http://bezvadata.cz%s" % m.group(1)
-
- #captcha form
- self.html = self.load(url)
- self.checkErrors()
- for _i in xrange(5):
- action, inputs = self.parseHtmlForm('frm-stahnoutFreeForm')
- if not inputs:
- self.error(_("FreeForm"))
-
- m = re.search(r'<img src="data:image/png;base64,(.*?)"', self.html)
- if m is None:
- self.error(_("Wrong captcha image"))
-
- #captcha image is contained in html page as base64encoded data but decryptCaptcha() expects image url
- self.load, proper_load = self.loadcaptcha, self.load
- try:
- inputs['captcha'] = self.decryptCaptcha(m.group(1), imgtype='png')
- finally:
- self.load = proper_load
-
- if '<img src="data:image/png;base64' in self.html:
- self.invalidCaptcha()
- else:
- self.correctCaptcha()
- break
- else:
- self.fail(_("No valid captcha code entered"))
-
- #download url
- self.html = self.load("http://bezvadata.cz%s" % action, post=inputs)
- self.checkErrors()
- m = re.search(r'<a class="stahnoutSoubor2" href="(.*?)">', self.html)
- if m is None:
- self.error(_("Page 2 URL not found"))
- url = "http://bezvadata.cz%s" % m.group(1)
- self.logDebug("DL URL %s" % url)
-
- #countdown
- m = re.search(r'id="countdown">(\d\d):(\d\d)<', self.html)
- wait_time = (int(m.group(1)) * 60 + int(m.group(2))) if m else 120
- self.wait(wait_time, False)
-
- self.download(url)
-
-
- def checkErrors(self):
- if 'images/button-download-disable.png' in self.html:
- self.longWait(5 * 60, 24) #: parallel dl limit
- elif '<div class="infobox' in self.html:
- self.tempOffline()
-
- self.info.pop('error', None)
-
-
- def loadcaptcha(self, data, *args, **kwargs):
- return data.decode("base64")
-
-
-getInfo = create_getInfo(BezvadataCz)
diff --git a/module/plugins/hoster/BillionuploadsCom.py b/module/plugins/hoster/BillionuploadsCom.py
deleted file mode 100644
index b20ace0f1..000000000
--- a/module/plugins/hoster/BillionuploadsCom.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class BillionuploadsCom(XFSHoster):
- __name__ = "BillionuploadsCom"
- __type__ = "hoster"
- __version__ = "0.04"
-
- __pattern__ = r'http://(?:www\.)?billionuploads\.com/\w{12}'
-
- __description__ = """Billionuploads.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- HOSTER_DOMAIN = "billionuploads.com"
-
- NAME_PATTERN = r'<td class="dofir" title="(?P<N>.+?)"'
- SIZE_PATTERN = r'<td class="dofir">(?P<S>[\d.,]+) (?P<U>[\w^_]+)'
-
-
-getInfo = create_getInfo(BillionuploadsCom)
diff --git a/module/plugins/hoster/BitshareCom.py b/module/plugins/hoster/BitshareCom.py
deleted file mode 100644
index 1483e5d1e..000000000
--- a/module/plugins/hoster/BitshareCom.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-import re
-
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class BitshareCom(SimpleHoster):
- __name__ = "BitshareCom"
- __type__ = "hoster"
- __version__ = "0.51"
-
- __pattern__ = r'http://(?:www\.)?bitshare\.com/(files/(?P<id1>\w+)(/(?P<name>.*?)\.html)?|\?f=(?P<id2>\w+))'
-
- __description__ = """Bitshare.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Paul King", None),
- ("fragonib", "fragonib[AT]yahoo[DOT]es")]
-
-
- INFO_PATTERN = r'Downloading (?P<N>.+) - (?P<S>[\d.,]+) (?P<U>[\w^_]+)</h1>'
- OFFLINE_PATTERN = r'(>We are sorry, but the requested file was not found in our database|>Error - File not available<|The file was deleted either by the uploader, inactivity or due to copyright claim)'
-
- COOKIES = [("bitshare.com", "language_selection", "EN")]
-
- AJAXID_PATTERN = r'var ajaxdl = "(.*?)";'
- TRAFFIC_USED_UP = r'Your Traffic is used up for today. Upgrade to premium to continue!'
-
-
- def setup(self):
- self.multiDL = self.premium
- self.chunkLimit = 1
-
-
- def process(self, pyfile):
- if self.premium:
- self.account.relogin(self.user)
-
- self.pyfile = pyfile
-
- # File id
- m = re.match(self.__pattern__, pyfile.url)
- self.file_id = max(m.group('id1'), m.group('id2'))
- self.logDebug("File id is [%s]" % self.file_id)
-
- # Load main page
- self.html = self.load(pyfile.url, ref=False, decode=True)
-
- # Check offline
- if re.search(self.OFFLINE_PATTERN, self.html):
- self.offline()
-
- # Check Traffic used up
- if re.search(self.TRAFFIC_USED_UP, self.html):
- self.logInfo(_("Your Traffic is used up for today"))
- self.wait(30 * 60, True)
- self.retry()
-
- # File name
- m = re.match(self.__pattern__, pyfile.url)
- name1 = m.group('name') if m else None
- m = re.search(self.INFO_PATTERN, self.html)
- name2 = m.group('N') if m else None
- pyfile.name = max(name1, name2)
-
- # Ajax file id
- self.ajaxid = re.search(self.AJAXID_PATTERN, self.html).group(1)
- self.logDebug("File ajax id is [%s]" % self.ajaxid)
-
- # This may either download our file or forward us to an error page
- url = self.getDownloadUrl()
- self.download(url)
-
- check = self.checkDownload({"404": ">404 Not Found<", "Error": ">Error occured<"})
- if check == "404":
- self.retry(3, 60, 'Error 404')
- elif check == "error":
- self.retry(5, 5 * 60, "Bitshare host : Error occured")
-
-
- def getDownloadUrl(self):
- # Return location if direct download is active
- if self.premium:
- header = self.load(self.pyfile.url, cookies=True, just_header=True)
- if 'location' in header:
- return header['location']
-
- # Get download info
- self.logDebug("Getting download info")
- res = self.load("http://bitshare.com/files-ajax/" + self.file_id + "/request.html",
- post={"request": "generateID", "ajaxid": self.ajaxid})
- self.handleErrors(res, ':')
- parts = res.split(":")
- filetype = parts[0]
- wait = int(parts[1])
- captcha = int(parts[2])
- self.logDebug("Download info [type: '%s', waiting: %d, captcha: %d]" % (filetype, wait, captcha))
-
- # Waiting
- if wait > 0:
- self.logDebug("Waiting %d seconds." % wait)
- if wait < 120:
- self.wait(wait, False)
- else:
- self.wait(wait - 55, True)
- self.retry()
-
- # Resolve captcha
- if captcha == 1:
- self.logDebug("File is captcha protected")
- recaptcha = ReCaptcha(self)
-
- # Try up to 3 times
- for i in xrange(3):
- challenge, code = recaptcha.challenge()
- res = self.load("http://bitshare.com/files-ajax/" + self.file_id + "/request.html",
- post={"request": "validateCaptcha", "ajaxid": self.ajaxid,
- "recaptcha_challenge_field": challenge, "recaptcha_response_field": code})
- if self.handleCaptchaErrors(res):
- break
-
- # Get download URL
- self.logDebug("Getting download url")
- res = self.load("http://bitshare.com/files-ajax/" + self.file_id + "/request.html",
- post={"request": "getDownloadURL", "ajaxid": self.ajaxid})
- self.handleErrors(res, '#')
- url = res.split("#")[-1]
-
- return url
-
-
- def handleErrors(self, res, separator):
- self.logDebug("Checking response [%s]" % res)
- if "ERROR:Session timed out" in res:
- self.retry()
- elif "ERROR" in res:
- msg = res.split(separator)[-1]
- self.fail(msg)
-
-
- def handleCaptchaErrors(self, res):
- self.logDebug("Result of captcha resolving [%s]" % res)
- if "SUCCESS" in res:
- self.correctCaptcha()
- return True
- elif "ERROR:SESSION ERROR" in res:
- self.retry()
-
- self.invalidCaptcha()
-
-
-getInfo = create_getInfo(BitshareCom)
diff --git a/module/plugins/hoster/BoltsharingCom.py b/module/plugins/hoster/BoltsharingCom.py
deleted file mode 100644
index 924545a29..000000000
--- a/module/plugins/hoster/BoltsharingCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class BoltsharingCom(DeadHoster):
- __name__ = "BoltsharingCom"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?boltsharing\.com/\w{12}'
-
- __description__ = """Boltsharing.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(BoltsharingCom)
diff --git a/module/plugins/hoster/CatShareNet.py b/module/plugins/hoster/CatShareNet.py
deleted file mode 100644
index 0caac5913..000000000
--- a/module/plugins/hoster/CatShareNet.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-from module.plugins.internal.CaptchaService import ReCaptcha
-
-
-class CatShareNet(SimpleHoster):
- __name__ = "CatShareNet"
- __type__ = "hoster"
- __version__ = "0.08"
-
- __pattern__ = r'http://(?:www\.)?catshare\.net/\w{16}'
-
- __description__ = """CatShare.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("z00nx", "z00nx0@gmail.com"),
- ("prOq", None),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- TEXT_ENCODING = True
-
- INFO_PATTERN = r'<title>(?P<N>.+) \((?P<S>[\d.,]+) (?P<U>[\w^_]+)\)<'
- OFFLINE_PATTERN = ur'Podany plik został usunięty\s*</div>'
-
- IP_BLOCKED_PATTERN = ur'>Nasz serwis wykrył ÅŒe Twój adres IP nie pochodzi z Polski.<'
- SECONDS_PATTERN = 'var\scount\s=\s(\d+);'
- LINK_PATTERN = r'<form action="(.+?)" method="GET">'
-
-
- def setup(self):
- self.multiDL = self.premium
- self.resumeDownload = True
-
-
- def getFileInfo(self):
- m = re.search(self.IP_BLOCKED_PATTERN, self.html)
- if m:
- self.fail(_("Only connections from Polish IP address are allowed"))
- return super(CatShareNet, self).getFileInfo()
-
-
- def handleFree(self):
- m = re.search(self.SECONDS_PATTERN, self.html)
- if m:
- wait_time = int(m.group(1))
- self.wait(wait_time, True)
-
- recaptcha = ReCaptcha(self)
-
- challenge, code = recaptcha.challenge()
- self.html = self.load(self.pyfile.url,
- post={'recaptcha_challenge_field': challenge,
- 'recaptcha_response_field': code})
-
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.invalidCaptcha()
- self.retry(reason=_("Wrong captcha entered"))
-
- dl_link = m.group(1)
- self.download(dl_link, disposition=True)
-
-
-getInfo = create_getInfo(CatShareNet)
diff --git a/module/plugins/hoster/CloudzerNet.py b/module/plugins/hoster/CloudzerNet.py
deleted file mode 100644
index c5440391f..000000000
--- a/module/plugins/hoster/CloudzerNet.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class CloudzerNet(DeadHoster):
- __name__ = "CloudzerNet"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'https?://(?:www\.)?(cloudzer\.net/file/|clz\.to/(file/)?)\w+'
-
- __description__ = """Cloudzer.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("gs", "I-_-I-_-I@web.de"),
- ("z00nx", "z00nx0@gmail.com"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(CloudzerNet)
diff --git a/module/plugins/hoster/CramitIn.py b/module/plugins/hoster/CramitIn.py
deleted file mode 100644
index 4f1ad1ff1..000000000
--- a/module/plugins/hoster/CramitIn.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class CramitIn(XFSHoster):
- __name__ = "CramitIn"
- __type__ = "hoster"
- __version__ = "0.07"
-
- __pattern__ = r'http://(?:www\.)?cramit\.in/\w{12}'
-
- __description__ = """Cramit.in hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- HOSTER_DOMAIN = "cramit.in"
-
- INFO_PATTERN = r'<span class=t2>\s*(?P<N>.*?)</span>.*?<small>\s*\((?P<S>.*?)\)'
- LINK_PATTERN = r'href="(http://cramit\.in/file_download/.*?)"'
-
-
-getInfo = create_getInfo(CramitIn)
diff --git a/module/plugins/hoster/CrockoCom.py b/module/plugins/hoster/CrockoCom.py
deleted file mode 100644
index e5f94800b..000000000
--- a/module/plugins/hoster/CrockoCom.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class CrockoCom(SimpleHoster):
- __name__ = "CrockoCom"
- __type__ = "hoster"
- __version__ = "0.17"
-
- __pattern__ = r'http://(?:www\.)?(crocko|easy-share)\.com/\w+'
-
- __description__ = """Crocko hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<span class="fz24">Download:\s*<strong>(?P<N>.*)'
- SIZE_PATTERN = r'<span class="tip1"><span class="inner">(?P<S>[^<]+)</span></span>'
- OFFLINE_PATTERN = r'<h1>Sorry,<br />the page you\'re looking for <br />isn\'t here.</h1>|File not found'
-
- CAPTCHA_PATTERN = re.compile(r"u='(/file_contents/captcha/\w+)';\s*w='(\d+)';")
-
- FORM_PATTERN = r'<form method="post" action="([^"]+)">(.*?)</form>'
- FORM_INPUT_PATTERN = r'<input[^>]* name="?([^" ]+)"? value="?([^" ]+)"?[^>]*>'
-
- NAME_REPLACEMENTS = [(r'<[^>]*>', '')]
-
-
- def handleFree(self):
- if "You need Premium membership to download this file." in self.html:
- self.fail(_("You need Premium membership to download this file"))
-
- for _i in xrange(5):
- m = re.search(self.CAPTCHA_PATTERN, self.html)
- if m:
- url, wait_time = 'http://crocko.com' + m.group(1), int(m.group(2))
- self.wait(wait_time)
- self.html = self.load(url)
- else:
- break
-
- m = re.search(self.FORM_PATTERN, self.html, re.S)
- if m is None:
- self.error(_("FORM_PATTERN not found"))
-
- action, form = m.groups()
- inputs = dict(re.findall(self.FORM_INPUT_PATTERN, form))
- recaptcha = ReCaptcha(self)
-
- for _i in xrange(5):
- inputs['recaptcha_challenge_field'], inputs['recaptcha_response_field'] = recaptcha.challenge()
- self.download(action, post=inputs)
-
- check = self.checkDownload({
- "captcha_err": recaptcha.KEY_AJAX_PATTERN
- })
-
- if check == "captcha_err":
- self.invalidCaptcha()
- else:
- break
- else:
- self.fail(_("No valid captcha solution received"))
-
-
-getInfo = create_getInfo(CrockoCom)
diff --git a/module/plugins/hoster/CyberlockerCh.py b/module/plugins/hoster/CyberlockerCh.py
deleted file mode 100644
index b26909096..000000000
--- a/module/plugins/hoster/CyberlockerCh.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class CyberlockerCh(DeadHoster):
- __name__ = "CyberlockerCh"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?cyberlocker\.ch/\w+'
-
- __description__ = """Cyberlocker.ch hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(CyberlockerCh)
diff --git a/module/plugins/hoster/CzshareCom.py b/module/plugins/hoster/CzshareCom.py
deleted file mode 100644
index 72f1bb45d..000000000
--- a/module/plugins/hoster/CzshareCom.py
+++ /dev/null
@@ -1,152 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://czshare.com/5278880/random.bin
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-from module.utils import parseFileSize
-
-
-class CzshareCom(SimpleHoster):
- __name__ = "CzshareCom"
- __type__ = "hoster"
- __version__ = "0.95"
-
- __pattern__ = r'http://(?:www\.)?(czshare|sdilej)\.(com|cz)/(\d+/|download\.php\?).*'
-
- __description__ = """CZshare.com hoster plugin, now Sdilej.cz"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<div class="tab" id="parameters">\s*<p>\s*Cel. n.zev: <a href=[^>]*>(?P<N>[^<]+)</a>'
- SIZE_PATTERN = r'<div class="tab" id="category">(?:\s*<p>[^\n]*</p>)*\s*Velikost:\s*(?P<S>[\d .,]+)(?P<U>[\w^_]+)\s*</div>'
- OFFLINE_PATTERN = r'<div class="header clearfix">\s*<h2 class="red">'
-
- SIZE_REPLACEMENTS = [(' ', '')]
- URL_REPLACEMENTS = [(r'http://[^/]*/download.php\?.*?id=(\w+).*', r'http://sdilej.cz/\1/x/')]
-
- FORCE_CHECK_TRAFFIC = True
-
- FREE_URL_PATTERN = r'<a href="([^"]+)" class="page-download">[^>]*alt="([^"]+)" /></a>'
- FREE_FORM_PATTERN = r'<form action="download\.php" method="post">\s*<img src="captcha\.php" id="captcha" />(.*?)</form>'
- PREMIUM_FORM_PATTERN = r'<form action="/profi_down\.php" method="post">(.*?)</form>'
- FORM_INPUT_PATTERN = r'<input[^>]* name="([^"]+)" value="([^"]+)"[^>]*/>'
- MULTIDL_PATTERN = r'<p><font color=\'red\'>Z[^<]*PROFI.</font></p>'
- USER_CREDIT_PATTERN = r'<div class="credit">\s*kredit: <strong>([\d .,]+)(\w+)</strong>\s*</div><!-- .credit -->'
-
-
- def checkTrafficLeft(self):
- # check if user logged in
- m = re.search(self.USER_CREDIT_PATTERN, self.html)
- if m is None:
- self.account.relogin(self.user)
- self.html = self.load(self.pyfile.url, cookies=True, decode=True)
- m = re.search(self.USER_CREDIT_PATTERN, self.html)
- if m is None:
- return False
-
- # check user credit
- try:
- credit = parseFileSize(m.group(1).replace(' ', ''), m.group(2))
- self.logInfo(_("Premium download for %i KiB of Credit") % (self.pyfile.size / 1024))
- self.logInfo(_("User %s has %i KiB left") % (self.user, credit / 1024))
- if credit < self.pyfile.size:
- self.logInfo(_("Not enough credit to download file: %s") % self.pyfile.name)
- return False
- except Exception, e:
- # let's continue and see what happens...
- self.logError(e)
-
- return True
-
-
- def handlePremium(self):
- # parse download link
- try:
- form = re.search(self.PREMIUM_FORM_PATTERN, self.html, re.S).group(1)
- inputs = dict(re.findall(self.FORM_INPUT_PATTERN, form))
- except Exception, e:
- self.logError(e)
- self.resetAccount()
-
- # download the file, destination is determined by pyLoad
- self.download("http://sdilej.cz/profi_down.php", post=inputs, disposition=True)
- self.checkDownloadedFile()
-
-
- def handleFree(self):
- # get free url
- m = re.search(self.FREE_URL_PATTERN, self.html)
- if m is None:
- self.error(_("FREE_URL_PATTERN not found"))
- parsed_url = "http://sdilej.cz" + m.group(1)
- self.logDebug("PARSED_URL:" + parsed_url)
-
- # get download ticket and parse html
- self.html = self.load(parsed_url, cookies=True, decode=True)
- if re.search(self.MULTIDL_PATTERN, self.html):
- self.longWait(5 * 60, 12)
-
- try:
- form = re.search(self.FREE_FORM_PATTERN, self.html, re.S).group(1)
- inputs = dict(re.findall(self.FORM_INPUT_PATTERN, form))
- self.pyfile.size = int(inputs['size'])
- except Exception, e:
- self.logError(e)
- self.error(_("Form"))
-
- # get and decrypt captcha
- captcha_url = 'http://sdilej.cz/captcha.php'
- for _i in xrange(5):
- inputs['captchastring2'] = self.decryptCaptcha(captcha_url)
- self.html = self.load(parsed_url, cookies=True, post=inputs, decode=True)
- if u"<li>ZadanÃœ ověřovací kód nesouhlasí!</li>" in self.html:
- self.invalidCaptcha()
- elif re.search(self.MULTIDL_PATTERN, self.html):
- self.longWait(5 * 60, 12)
- else:
- self.correctCaptcha()
- break
- else:
- self.fail(_("No valid captcha code entered"))
-
- m = re.search("countdown_number = (\d+);", self.html)
- self.setWait(int(m.group(1)) if m else 50)
-
- # download the file, destination is determined by pyLoad
- self.logDebug("WAIT URL", self.req.lastEffectiveURL)
- m = re.search("free_wait.php\?server=(.*?)&(.*)", self.req.lastEffectiveURL)
- if m is None:
- self.error(_("Download URL not found"))
-
- url = "http://%s/download.php?%s" % (m.group(1), m.group(2))
-
- self.wait()
- self.download(url)
- self.checkDownloadedFile()
-
-
- def checkDownloadedFile(self):
- # check download
- check = self.checkDownload({
- "temp_offline": re.compile(r"^Soubor je do.*asn.* nedostupn.*$"),
- "credit": re.compile(r"^Nem.*te dostate.*n.* kredit.$"),
- "multi_dl": re.compile(self.MULTIDL_PATTERN),
- "captcha_err": "<li>ZadanÃœ ověřovací kód nesouhlasí!</li>"
- })
-
- if check == "temp_offline":
- self.fail(_("File not available - try later"))
- if check == "credit":
- self.resetAccount()
- elif check == "multi_dl":
- self.longWait(5 * 60, 12)
- elif check == "captcha_err":
- self.invalidCaptcha()
- self.retry()
-
-
-getInfo = create_getInfo(CzshareCom)
diff --git a/module/plugins/hoster/DailymotionCom.py b/module/plugins/hoster/DailymotionCom.py
deleted file mode 100644
index 09b2f626d..000000000
--- a/module/plugins/hoster/DailymotionCom.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.PyFile import statusMap
-from module.common.json_layer import json_loads
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
-
-
-def getInfo(urls):
- result = []
- regex = re.compile(DailymotionCom.__pattern__)
- apiurl = "https://api.dailymotion.com/video/%s"
- request = {"fields": "access_error,status,title"}
-
- for url in urls:
- id = regex.match(url).group("ID")
- page = getURL(apiurl % id, get=request)
- info = json_loads(page)
-
- name = info['title'] + ".mp4" if "title" in info else url
-
- if "error" in info or info['access_error']:
- status = "offline"
- else:
- status = info['status']
- if status in ("ready", "published"):
- status = "online"
- elif status in ("waiting", "processing"):
- status = "temp. offline"
- else:
- status = "offline"
-
- result.append((name, 0, statusMap[status], url))
-
- return result
-
-
-class DailymotionCom(Hoster):
- __name__ = "DailymotionCom"
- __type__ = "hoster"
- __version__ = "0.2"
-
- __pattern__ = r'https?://(?:www\.)?dailymotion\.com/.*video/(?P<ID>[\w^_]+)'
- __config__ = [("quality", "Lowest;LD 144p;LD 240p;SD 384p;HQ 480p;HD 720p;HD 1080p;Highest", "Quality", "Highest")]
-
- __description__ = """Dailymotion.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
-
-
- def getStreams(self):
- streams = []
-
- for result in re.finditer(r"\"(?P<URL>http:\\/\\/www.dailymotion.com\\/cdn\\/H264-(?P<QF>.*?)\\.*?)\"",
- self.html):
- url = result.group("URL")
- qf = result.group("QF")
-
- link = url.replace("\\", "")
- quality = tuple(int(x) for x in qf.split("x"))
-
- streams.append((quality, link))
-
- return sorted(streams, key=lambda x: x[0][::-1])
-
-
- def getQuality(self):
- q = self.getConfig("quality")
-
- if q == "Lowest":
- quality = 0
- elif q == "Highest":
- quality = -1
- else:
- quality = int(q.rsplit(" ")[1][:-1])
-
- return quality
-
-
- def getLink(self, streams, quality):
- if quality > 0:
- for x, s in reversed([item for item in enumerate(streams)]):
- qf = s[0][1]
- if qf <= quality:
- idx = x
- break
- else:
- idx = 0
- else:
- idx = quality
-
- s = streams[idx]
-
- self.logInfo(_("Download video quality %sx%s") % s[0])
-
- return s[1]
-
-
- def checkInfo(self, pyfile):
- pyfile.name, pyfile.size, pyfile.status, pyfile.url = getInfo([pyfile.url])[0]
-
- if pyfile.status == 1:
- self.offline()
-
- elif pyfile.status == 6:
- self.tempOffline()
-
-
- def process(self, pyfile):
- self.checkInfo(pyfile)
-
- id = re.match(self.__pattern__, pyfile.url).group("ID")
- self.html = self.load("http://www.dailymotion.com/embed/video/" + id, decode=True)
-
- streams = self.getStreams()
- quality = self.getQuality()
-
- self.download(self.getLink(streams, quality))
diff --git a/module/plugins/hoster/DataHu.py b/module/plugins/hoster/DataHu.py
deleted file mode 100644
index 437fea7cd..000000000
--- a/module/plugins/hoster/DataHu.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://data.hu/get/6381232/random.bin
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class DataHu(SimpleHoster):
- __name__ = "DataHu"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?data\.hu/get/\w+'
-
- __description__ = """Data.hu hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("crash", None),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- INFO_PATTERN = ur'<title>(?P<N>.*) \((?P<S>[^)]+)\) let\xf6lt\xe9se</title>'
- OFFLINE_PATTERN = ur'Az adott f\xe1jl nem l\xe9tezik'
- LINK_PATTERN = r'<div class="download_box_button"><a href="([^"]+)">'
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = self.premium
-
-
- def handleFree(self):
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("LINK_PATTERN not found"))
-
- self.download(m.group(1), disposition=True)
-
-
-getInfo = create_getInfo(DataHu)
diff --git a/module/plugins/hoster/DataportCz.py b/module/plugins/hoster/DataportCz.py
deleted file mode 100644
index b9e6fd370..000000000
--- a/module/plugins/hoster/DataportCz.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class DataportCz(SimpleHoster):
- __name__ = "DataportCz"
- __type__ = "hoster"
- __version__ = "0.40"
-
- __pattern__ = r'http://(?:www\.)?dataport\.cz/file/(.*)'
-
- __description__ = """Dataport.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<span itemprop="name">(?P<N>[^<]+)</span>'
- SIZE_PATTERN = r'<td class="fil">Velikost</td>\s*<td>(?P<S>[^<]+)</td>'
- OFFLINE_PATTERN = r'<h2>Soubor nebyl nalezen</h2>'
-
- CAPTCHA_PATTERN = r'<section id="captcha_bg">\s*<img src="(.*?)"'
- FREE_SLOTS_PATTERN = ur'Počet volnÜch slotů: <span class="darkblue">(\d+)</span><br />'
-
-
- def handleFree(self):
- captchas = {"1": "jkeG", "2": "hMJQ", "3": "vmEK", "4": "ePQM", "5": "blBd"}
-
- for _i in xrange(60):
- action, inputs = self.parseHtmlForm('free_download_form')
- self.logDebug(action, inputs)
- if not action or not inputs:
- self.error(_("free_download_form"))
-
- if "captchaId" in inputs and inputs['captchaId'] in captchas:
- inputs['captchaCode'] = captchas[inputs['captchaId']]
- else:
- self.error(_("captcha"))
-
- self.html = self.download("http://www.dataport.cz%s" % action, post=inputs)
-
- check = self.checkDownload({"captcha": 'alert("\u0160patn\u011b opsan\u00fd k\u00f3d z obr\u00e1zu");',
- "slot": 'alert("Je n\u00e1m l\u00edto, ale moment\u00e1ln\u011b nejsou'})
- if check == "captcha":
- self.error(_("invalid captcha"))
- elif check == "slot":
- self.logDebug("No free slots - wait 60s and retry")
- self.wait(60, False)
- self.html = self.load(self.pyfile.url, decode=True)
- continue
- else:
- break
-
-
-getInfo = create_getInfo(DataportCz)
diff --git a/module/plugins/hoster/DateiTo.py b/module/plugins/hoster/DateiTo.py
deleted file mode 100644
index e4bff8458..000000000
--- a/module/plugins/hoster/DateiTo.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class DateiTo(SimpleHoster):
- __name__ = "DateiTo"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'http://(?:www\.)?datei\.to/datei/(?P<ID>\w+)\.html'
-
- __description__ = """Datei.to hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'Dateiname:</td>\s*<td colspan="2"><strong>(?P<N>.*?)</'
- SIZE_PATTERN = r'Dateigr&ouml;&szlig;e:</td>\s*<td colspan="2">(?P<S>.*?)</'
- OFFLINE_PATTERN = r'>Datei wurde nicht gefunden<|>Bitte wÀhle deine Datei aus... <'
-
- WAIT_PATTERN = r'countdown\({seconds: (\d+)'
- MULTIDL_PATTERN = r'>Du lÀdst bereits eine Datei herunter<'
-
- DATA_PATTERN = r'url: "(.*?)", data: "(.*?)",'
-
-
- def handleFree(self):
- url = 'http://datei.to/ajax/download.php'
- data = {'P': 'I', 'ID': self.info['pattern']['ID']}
- recaptcha = ReCaptcha(self)
-
- for _i in xrange(10):
- self.logDebug("URL", url, "POST", data)
- self.html = self.load(url, post=data)
- self.checkErrors()
-
- if url.endswith('download.php') and 'P' in data:
- if data['P'] == 'I':
- self.doWait()
-
- elif data['P'] == 'IV':
- break
-
- m = re.search(self.DATA_PATTERN, self.html)
- if m is None:
- self.error(_("data"))
- url = 'http://datei.to/' + m.group(1)
- data = dict(x.split('=') for x in m.group(2).split('&'))
-
- if url.endswith('recaptcha.php'):
- data['recaptcha_challenge_field'], data['recaptcha_response_field'] = recaptcha.challenge()
- else:
- self.fail(_("Too bad..."))
-
- self.download(self.html)
-
-
- def checkErrors(self):
- m = re.search(self.MULTIDL_PATTERN, self.html)
- if m:
- m = re.search(self.WAIT_PATTERN, self.html)
- wait_time = int(m.group(1)) if m else 30
-
- errmsg = self.info['error'] = _("Parallel downloads")
- self.retry(wait_time=wait_time, reason=errmsg)
-
- self.info.pop('error', None)
-
-
- def doWait(self):
- m = re.search(self.WAIT_PATTERN, self.html)
- wait_time = int(m.group(1)) if m else 30
-
- self.load('http://datei.to/ajax/download.php', post={'P': 'Ads'})
- self.wait(wait_time, False)
-
-
-getInfo = create_getInfo(DateiTo)
diff --git a/module/plugins/hoster/DdlstorageCom.py b/module/plugins/hoster/DdlstorageCom.py
deleted file mode 100644
index a45ef27e9..000000000
--- a/module/plugins/hoster/DdlstorageCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class DdlstorageCom(DeadHoster):
- __name__ = "DdlstorageCom"
- __type__ = "hoster"
- __version__ = "1.02"
-
- __pattern__ = r'https?://(?:www\.)?ddlstorage\.com/\w+'
-
- __description__ = """DDLStorage.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(DdlstorageCom)
diff --git a/module/plugins/hoster/DebridItaliaCom.py b/module/plugins/hoster/DebridItaliaCom.py
deleted file mode 100644
index 17342b8cd..000000000
--- a/module/plugins/hoster/DebridItaliaCom.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Hoster import Hoster
-from module.plugins.internal.SimpleHoster import replace_patterns
-
-
-class DebridItaliaCom(Hoster):
- __name__ = "DebridItaliaCom"
- __type__ = "hoster"
- __version__ = "0.07"
-
- __pattern__ = r'http://s\d+\.debriditalia\.com/dl/\d+'
-
- __description__ = """Debriditalia.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- URL_REPLACEMENTS = [(r'(/dl/\d+)$', '\1/')]
-
-
- def setup(self):
- self.chunkLimit = -1
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- pyfile.url = replace_patterns(pyfile.url, cls.URL_REPLACEMENTS)
-
- if re.match(self.__pattern__, pyfile.url):
- link = pyfile.url
-
- elif not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "DebridItalia")
- self.fail(_("No DebridItalia account provided"))
-
- else:
- html = self.load("http://www.debriditalia.com/api.php", get={'generate': "", 'link': pyfile.url})
-
- if "ERROR" in html:
- self.fail(re.search(r'ERROR:(.*)', html).strip())
-
- link = html.strip()
-
- self.download(link, disposition=True)
-
- check = self.checkDownload({'empty': re.compile(r'^$')})
-
- if check == "empty":
- self.retry(5, 2 * 60, "Empty file downloaded")
diff --git a/module/plugins/hoster/DepositfilesCom.py b/module/plugins/hoster/DepositfilesCom.py
deleted file mode 100644
index 6588a3b37..000000000
--- a/module/plugins/hoster/DepositfilesCom.py
+++ /dev/null
@@ -1,123 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urllib import unquote
-
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class DepositfilesCom(SimpleHoster):
- __name__ = "DepositfilesCom"
- __type__ = "hoster"
- __version__ = "0.51"
-
- __pattern__ = r'https?://(?:www\.)?(depositfiles\.com|dfiles\.(eu|ru))(/\w{1,3})?/files/(?P<ID>\w+)'
-
- __description__ = """Depositfiles.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("spoob", "spoob@pyload.org"),
- ("zoidberg", "zoidberg@mujmail.cz"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- NAME_PATTERN = r'<script type="text/javascript">eval\( unescape\(\'(?P<N>.*?)\''
- SIZE_PATTERN = r': <b>(?P<S>[\d.,]+)&nbsp;(?P<U>[\w^_]+)</b>'
- OFFLINE_PATTERN = r'<span class="html_download_api-not_exists"></span>'
-
- NAME_REPLACEMENTS = [(r'\%u([0-9A-Fa-f]{4})', lambda m: unichr(int(m.group(1), 16))),
- (r'.*<b title="(?P<N>[^"]+).*', "\g<N>")]
- URL_REPLACEMENTS = [(__pattern__ + ".*", "https://dfiles.eu/files/\g<ID>")]
-
- COOKIES = [("dfiles.eu", "lang_current", "en")]
-
- FREE_LINK_PATTERN = r'<form id="downloader_file_form" action="(http://.+?\.(dfiles\.eu|depositfiles\.com)/.+?)" method="post"'
- PREMIUM_LINK_PATTERN = r'class="repeat"><a href="(.+?)"'
- PREMIUM_MIRROR_PATTERN = r'class="repeat_mirror"><a href="(.+?)"'
-
-
- def handleFree(self):
- self.html = self.load(self.pyfile.url, post={"gateway_result": "1"}, cookies=True)
-
- if re.search(r'File is checked, please try again in a minute.', self.html) is not None:
- self.logInfo(_("The file is being checked. Waiting 1 minute"))
- self.retry(wait_time=60)
-
- wait = re.search(r'html_download_api-limit_interval\">(\d+)</span>', self.html)
- if wait:
- wait_time = int(wait.group(1))
- self.logInfo(_("Traffic used up. Waiting %d seconds") % wait_time)
- self.wait(wait_time, True)
- self.retry()
-
- wait = re.search(r'>Try in (\d+) minutes or use GOLD account', self.html)
- if wait:
- wait_time = int(wait.group(1))
- self.logInfo(_("All free slots occupied. Waiting %d minutes") % wait_time)
- self.setWait(wait_time * 60, False)
-
- wait = re.search(r'Please wait (\d+) sec', self.html)
- if wait:
- self.setWait(int(wait.group(1)))
-
- m = re.search(r"var fid = '(\w+)';", self.html)
- if m is None:
- self.retry(wait_time=5)
- params = {'fid': m.group(1)}
- self.logDebug("FID: %s" % params['fid'])
-
- self.wait()
- recaptcha = ReCaptcha(self)
- captcha_key = recaptcha.detect_key()
- if captcha_key is None:
- self.error(_("ReCaptcha key not found"))
-
- for _i in xrange(5):
- self.html = self.load("https://dfiles.eu/get_file.php", get=params)
-
- if '<input type=button value="Continue" onclick="check_recaptcha' in self.html:
- if 'response' in params:
- self.invalidCaptcha()
- params['challenge'], params['response'] = recaptcha.challenge(captcha_key)
- self.logDebug(params)
- continue
-
- m = re.search(self.FREE_LINK_PATTERN, self.html)
- if m:
- if 'response' in params:
- self.correctCaptcha()
- link = unquote(m.group(1))
- self.logDebug("LINK: %s" % link)
- break
- else:
- self.error(_("Download link"))
- else:
- self.fail(_("No valid captcha response received"))
-
- try:
- self.download(link, disposition=True)
- except:
- self.retry(wait_time=60)
-
-
- def handlePremium(self):
- if '<span class="html_download_api-gold_traffic_limit">' in self.html:
- self.logWarning(_("Download limit reached"))
- self.retry(25, 60 * 60, "Download limit reached")
- elif 'onClick="show_gold_offer' in self.html:
- self.account.relogin(self.user)
- self.retry()
- else:
- link = re.search(self.PREMIUM_LINK_PATTERN, self.html)
- mirror = re.search(self.PREMIUM_MIRROR_PATTERN, self.html)
- if link:
- dlink = link.group(1)
- elif mirror:
- dlink = mirror.group(1)
- else:
- self.error(_("No direct download link or mirror found"))
- self.download(dlink, disposition=True)
-
-
-getInfo = create_getInfo(DepositfilesCom)
diff --git a/module/plugins/hoster/DevhostSt.py b/module/plugins/hoster/DevhostSt.py
deleted file mode 100644
index 85e36edb3..000000000
--- a/module/plugins/hoster/DevhostSt.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://d-h.st/mM8
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class DevhostSt(SimpleHoster):
- __name__ = "DevhostSt"
- __type__ = "hoster"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?d-h\.st/(?!users/)\w{3}'
-
- __description__ = """d-h.st hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zapp-brannigan", "fuerst.reinje@web.de")]
-
-
- NAME_PATTERN = r'>Filename:</span> <div title="(?P<N>.+?)"'
- SIZE_PATTERN = r'>Size:</span> (?P<S>[\d.,]+) (?P<U>[\w^_]+)'
-
- OFFLINE_PATTERN = r'>File Not Found<'
- LINK_PATTERN = r'id="downloadfile" href="(.+?)"'
-
-
- def setup(self):
- self.multiDL = True
- self.chunkLimit = 1
-
-
- def handleFree(self):
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("Download link not found"))
-
- dl_url = m.group(1)
- self.download(dl_url, disposition=True)
-
- check = self.checkDownload({'html': re.compile("html")})
- if check == "html":
- self.error(_("Downloaded file is an html page"))
-
-
-getInfo = create_getInfo(DevhostSt)
diff --git a/module/plugins/hoster/DlFreeFr.py b/module/plugins/hoster/DlFreeFr.py
deleted file mode 100644
index f07d46768..000000000
--- a/module/plugins/hoster/DlFreeFr.py
+++ /dev/null
@@ -1,214 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pycurl
-import re
-
-from module.common.json_layer import json_loads
-from module.network.Browser import Browser
-from module.network.CookieJar import CookieJar
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, replace_patterns
-
-
-class CustomBrowser(Browser):
-
- def __init__(self, bucket=None, options={}):
- Browser.__init__(self, bucket, options)
-
-
- def load(self, *args, **kwargs):
- post = kwargs.get("post")
-
- if post is None and len(args) > 2:
- post = args[2]
-
- if post:
- self.http.c.setopt(pycurl.FOLLOWLOCATION, 0)
- self.http.c.setopt(pycurl.POST, 1)
- self.http.c.setopt(pycurl.CUSTOMREQUEST, "POST")
- else:
- self.http.c.setopt(pycurl.FOLLOWLOCATION, 1)
- self.http.c.setopt(pycurl.POST, 0)
- self.http.c.setopt(pycurl.CUSTOMREQUEST, "GET")
-
- return Browser.load(self, *args, **kwargs)
-
-
-class AdYouLike:
- """
- Class to support adyoulike captcha service
- """
- ADYOULIKE_INPUT_PATTERN = r'Adyoulike\.create\((.*?)\);'
- ADYOULIKE_CALLBACK = r'Adyoulike\.g\._jsonp_5579316662423138'
- ADYOULIKE_CHALLENGE_PATTERN = ADYOULIKE_CALLBACK + r'\((.*?)\)'
-
-
- def __init__(self, plugin, engine="adyoulike"):
- self.plugin = plugin
- self.engine = engine
-
-
- def challenge(self, html):
- adyoulike_data_string = None
- m = re.search(self.ADYOULIKE_INPUT_PATTERN, html)
- if m:
- adyoulike_data_string = m.group(1)
- else:
- self.plugin.fail("Can't read AdYouLike input data")
-
- # {"adyoulike":{"key":"P~zQ~O0zV0WTiAzC-iw0navWQpCLoYEP"},
- # "all":{"element_id":"ayl_private_cap_92300","lang":"fr","env":"prod"}}
- ayl_data = json_loads(adyoulike_data_string)
-
- res = self.plugin.load("http://api-ayl.appspot.com/challenge",
- get={'key' : ayl_data[self.engine]['key'],
- 'env' : ayl_data['all']['env'],
- 'callback': self.ADYOULIKE_CALLBACK})
-
- m = re.search(self.ADYOULIKE_CHALLENGE_PATTERN, res)
- challenge_string = None
- if m:
- challenge_string = m.group(1)
- else:
- self.plugin.fail("Invalid AdYouLike challenge")
- challenge_data = json_loads(challenge_string)
-
- return ayl_data, challenge_data
-
-
- def result(self, ayl, challenge):
- """
- Adyoulike.g._jsonp_5579316662423138
- ({"translations":{"fr":{"instructions_visual":"Recopiez « Soonnight » ci-dessous :"}},
- "site_under":true,"clickable":true,"pixels":{"VIDEO_050":[],"DISPLAY":[],"VIDEO_000":[],"VIDEO_100":[],
- "VIDEO_025":[],"VIDEO_075":[]},"medium_type":"image/adyoulike",
- "iframes":{"big":"<iframe src=\"http://www.soonnight.com/campagn.html\" scrolling=\"no\"
- height=\"250\" width=\"300\" frameborder=\"0\"></iframe>"},"shares":{},"id":256,
- "token":"e6QuI4aRSnbIZJg02IsV6cp4JQ9~MjA1","formats":{"small":{"y":300,"x":0,"w":300,"h":60},
- "big":{"y":0,"x":0,"w":300,"h":250},"hover":{"y":440,"x":0,"w":300,"h":60}},
- "tid":"SqwuAdxT1EZoi4B5q0T63LN2AkiCJBg5"})
- """
- response = None
- try:
- instructions_visual = challenge['translations'][ayl['all']['lang']]['instructions_visual']
- m = re.search(u".*«(.*)».*", instructions_visual)
- if m:
- response = m.group(1).strip()
- else:
- self.plugin.fail("Can't parse instructions visual")
- except KeyError:
- self.plugin.fail("No instructions visual")
-
- #TODO: Supports captcha
-
- if not response:
- self.plugin.fail("AdYouLike result failed")
-
- return {"_ayl_captcha_engine": self.engine,
- "_ayl_env": ayl['all']['env'],
- "_ayl_tid": challenge['tid'],
- "_ayl_token_challenge": challenge['token'],
- "_ayl_response": response}
-
-
-class DlFreeFr(SimpleHoster):
- __name__ = "DlFreeFr"
- __type__ = "hoster"
- __version__ = "0.25"
-
- __pattern__ = r'http://(?:www\.)?dl\.free\.fr/(\w+|getfile\.pl\?file=/\w+)'
-
- __description__ = """Dl.free.fr hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("the-razer", "daniel_ AT gmx DOT net"),
- ("zoidberg", "zoidberg@mujmail.cz"),
- ("Toilal", "toilal.dev@gmail.com")]
-
-
- NAME_PATTERN = r'Fichier:</td>\s*<td[^>]*>(?P<N>[^>]*)</td>'
- SIZE_PATTERN = r'Taille:</td>\s*<td[^>]*>(?P<S>[\d.,]+\w)o'
- OFFLINE_PATTERN = r'Erreur 404 - Document non trouv|Fichier inexistant|Le fichier demand&eacute; n\'a pas &eacute;t&eacute; trouv&eacute;'
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
- self.limitDL = 5
- self.chunkLimit = 1
-
-
- def init(self):
- factory = self.core.requestFactory
- self.req = CustomBrowser(factory.bucket, factory.getOptions())
-
-
- def process(self, pyfile):
- pyfile.url = replace_patterns(pyfile.url, self.URL_REPLACEMENTS)
- valid_url = pyfile.url
- headers = self.load(valid_url, just_header=True)
-
- if headers.get('code') == 302:
- valid_url = headers.get('location')
- headers = self.load(valid_url, just_header=True)
-
- if headers.get('code') == 200:
- content_type = headers.get('content-type')
- if content_type and content_type.startswith("text/html"):
- # Undirect acces to requested file, with a web page providing it (captcha)
- self.html = self.load(valid_url)
- self.handleFree()
- else:
- # Direct access to requested file for users using free.fr as Internet Service Provider.
- self.download(valid_url, disposition=True)
- elif headers.get('code') == 404:
- self.offline()
- else:
- self.fail(_("Invalid return code: ") + str(headers.get('code')))
-
-
- def handleFree(self):
- action, inputs = self.parseHtmlForm('action="getfile.pl"')
-
- adyoulike = AdYouLike(self)
- ayl, challenge = adyoulike.challenge(self.html)
- result = adyoulike.result(ayl, challenge)
- inputs.update(result)
-
- self.load("http://dl.free.fr/getfile.pl", post=inputs)
- headers = self.getLastHeaders()
- if headers.get("code") == 302 and "set-cookie" in headers and "location" in headers:
- m = re.search("(.*?)=(.*?); path=(.*?); domain=(.*?)", headers.get("set-cookie"))
- cj = CookieJar(__name__)
- if m:
- cj.setCookie(m.group(4), m.group(1), m.group(2), m.group(3))
- else:
- self.fail(_("Cookie error"))
- location = headers.get("location")
- self.req.setCookieJar(cj)
- self.download(location, disposition=True)
- else:
- self.fail(_("Invalid response"))
-
-
- def getLastHeaders(self):
- #parse header
- header = {"code": self.req.code}
- for line in self.req.http.header.splitlines():
- line = line.strip()
- if not line or ":" not in line:
- continue
-
- key, none, value = line.partition(":")
- key = key.lower().strip()
- value = value.strip()
-
- if key in header:
- if type(header[key]) == list:
- header[key].append(value)
- else:
- header[key] = [header[key], value]
- else:
- header[key] = value
- return header
-
-
-getInfo = create_getInfo(DlFreeFr)
diff --git a/module/plugins/hoster/DropboxCom.py b/module/plugins/hoster/DropboxCom.py
deleted file mode 100644
index 658974d13..000000000
--- a/module/plugins/hoster/DropboxCom.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class DropboxCom(SimpleHoster):
- __name__ = "DropboxCom"
- __type__ = "hoster"
- __version__ = "0.03"
-
- __pattern__ = r'https?://(?:www\.)?dropbox\.com/.+'
-
- __description__ = """Dropbox.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zapp-brannigan", "fuerst.reinje@web.de")]
-
-
- NAME_PATTERN = r'<title>Dropbox - (?P<N>.+?)<'
- SIZE_PATTERN = r'&nbsp;&middot;&nbsp; (?P<S>[\d.,]+) (?P<U>[\w^_]+)'
-
- OFFLINE_PATTERN = r'<title>Dropbox - (404|Shared link error)<'
-
- COOKIES = [("dropbox.com", "lang", "en")]
-
-
- def setup(self):
- self.multiDL = True
- self.chunkLimit = 1
- self.resumeDownload = True
-
-
- def handleFree(self):
- self.download(self.pyfile.url, get={'dl': "1"})
-
- check = self.checkDownload({'html': re.compile("html")})
- if check == "html":
- self.error(_("Downloaded file is an html page"))
-
-
-getInfo = create_getInfo(DropboxCom)
diff --git a/module/plugins/hoster/DuploadOrg.py b/module/plugins/hoster/DuploadOrg.py
deleted file mode 100644
index 73702eb67..000000000
--- a/module/plugins/hoster/DuploadOrg.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class DuploadOrg(DeadHoster):
- __name__ = "DuploadOrg"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?dupload\.org/\w{12}'
-
- __description__ = """Dupload.grg hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(DuploadOrg)
diff --git a/module/plugins/hoster/EasybytezCom.py b/module/plugins/hoster/EasybytezCom.py
deleted file mode 100644
index cd54bdc70..000000000
--- a/module/plugins/hoster/EasybytezCom.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class EasybytezCom(XFSHoster):
- __name__ = "EasybytezCom"
- __type__ = "hoster"
- __version__ = "0.23"
-
- __pattern__ = r'http://(?:www\.)?easybytez\.com/\w{12}'
-
- __description__ = """Easybytez.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- HOSTER_DOMAIN = "easybytez.com"
-
- OFFLINE_PATTERN = r'>File not available'
-
- LINK_PATTERN = r'(http://(\w+\.(easybytez|easyload|ezbytez|zingload)\.(com|to)|\d+\.\d+\.\d+\.\d+)/files/\d+/\w+/.+?)["\'<]'
-
-
-getInfo = create_getInfo(EasybytezCom)
diff --git a/module/plugins/hoster/EdiskCz.py b/module/plugins/hoster/EdiskCz.py
deleted file mode 100644
index b9dc45e78..000000000
--- a/module/plugins/hoster/EdiskCz.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class EdiskCz(SimpleHoster):
- __name__ = "EdiskCz"
- __type__ = "hoster"
- __version__ = "0.22"
-
- __pattern__ = r'http://(?:www\.)?edisk\.(cz|sk|eu)/(stahni|sk/stahni|en/download)/.*'
-
- __description__ = """Edisk.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- INFO_PATTERN = r'<span class="fl" title="(?P<N>[^"]+)">\s*.*?\((?P<S>[\d.,]+) (?P<U>[\w^_]+)\)</h1></span>'
- OFFLINE_PATTERN = r'<h3>This file does not exist due to one of the following:</h3><ul><li>'
-
- ACTION_PATTERN = r'/en/download/(\d+/.*\.html)'
- LINK_PATTERN = r'http://.*edisk\.cz.*\.html'
-
-
- def setup(self):
- self.multiDL = False
-
-
- def process(self, pyfile):
- url = re.sub("/(stahni|sk/stahni)/", "/en/download/", pyfile.url)
-
- self.logDebug("URL:" + url)
-
- m = re.search(self.ACTION_PATTERN, url)
- if m is None:
- self.error(_("ACTION_PATTERN not found"))
- action = m.group(1)
-
- self.html = self.load(url, decode=True)
- self.getFileInfo()
-
- self.html = self.load(re.sub("/en/download/", "/en/download-slow/", url))
-
- url = self.load(re.sub("/en/download/", "/x-download/", url), post={
- "action": action
- })
-
- if not re.match(self.LINK_PATTERN, url):
- self.fail(_("Unexpected server response"))
-
- self.download(url)
-
-
-getInfo = create_getInfo(EdiskCz)
diff --git a/module/plugins/hoster/EgoFilesCom.py b/module/plugins/hoster/EgoFilesCom.py
deleted file mode 100644
index 9a2f50ed4..000000000
--- a/module/plugins/hoster/EgoFilesCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class EgoFilesCom(DeadHoster):
- __name__ = "EgoFilesCom"
- __type__ = "hoster"
- __version__ = "0.16"
-
- __pattern__ = r'https?://(?:www\.)?egofiles\.com/\w+'
-
- __description__ = """Egofiles.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(EgoFilesCom)
diff --git a/module/plugins/hoster/EnteruploadCom.py b/module/plugins/hoster/EnteruploadCom.py
deleted file mode 100644
index bbd613f57..000000000
--- a/module/plugins/hoster/EnteruploadCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class EnteruploadCom(DeadHoster):
- __name__ = "EnteruploadCom"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?enterupload\.com/\w+'
-
- __description__ = """EnterUpload.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(EnteruploadCom)
diff --git a/module/plugins/hoster/EpicShareNet.py b/module/plugins/hoster/EpicShareNet.py
deleted file mode 100644
index 8ac8cdaf2..000000000
--- a/module/plugins/hoster/EpicShareNet.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class EpicShareNet(DeadHoster):
- __name__ = "EpicShareNet"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'https?://(?:www\.)?epicshare\.net/\w{12}'
-
- __description__ = """EpicShare.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")]
-
-
-getInfo = create_getInfo(EpicShareNet)
diff --git a/module/plugins/hoster/EuroshareEu.py b/module/plugins/hoster/EuroshareEu.py
deleted file mode 100644
index 705a8d18a..000000000
--- a/module/plugins/hoster/EuroshareEu.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class EuroshareEu(SimpleHoster):
- __name__ = "EuroshareEu"
- __type__ = "hoster"
- __version__ = "0.26"
-
- __pattern__ = r'http://(?:www\.)?euroshare\.(eu|sk|cz|hu|pl)/file/.*'
-
- __description__ = """Euroshare.eu hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- INFO_PATTERN = r'<span style="float: left;"><strong>(?P<N>.+?)</strong> \((?P<S>.+?)\)</span>'
- OFFLINE_PATTERN = ur'<h2>S.bor sa nena.iel</h2>|Poşadovaná stránka neexistuje!'
-
- FREE_URL_PATTERN = r'<a href="(/file/\d+/[^/]*/download/)"><div class="downloadButton"'
- ERR_PARDL_PATTERN = r'<h2>Prebieha s.ahovanie</h2>|<p>Naraz je z jednej IP adresy mo.n. s.ahova. iba jeden s.bor'
- ERR_NOT_LOGGED_IN_PATTERN = r'href="/customer-zone/login/"'
-
- URL_REPLACEMENTS = [(r"(http://[^/]*\.)(sk|cz|hu|pl)/", r"\1eu/")]
-
-
- def setup(self):
- self.multiDL = self.resumeDownload = self.premium
- self.req.setOption("timeout", 120)
-
-
- def handlePremium(self):
- if self.ERR_NOT_LOGGED_IN_PATTERN in self.html:
- self.account.relogin(self.user)
- self.retry(reason=_("User not logged in"))
-
- self.download(self.pyfile.url.rstrip('/') + "/download/")
-
- check = self.checkDownload({"login": re.compile(self.ERR_NOT_LOGGED_IN_PATTERN),
- "json": re.compile(r'\{"status":"error".*?"message":"(.*?)"')})
- if check == "login" or (check == "json" and self.lastCheck.group(1) == "Access token expired"):
- self.account.relogin(self.user)
- self.retry(reason=_("Access token expired"))
- elif check == "json":
- self.fail(self.lastCheck.group(1))
-
-
- def handleFree(self):
- if re.search(self.ERR_PARDL_PATTERN, self.html) is not None:
- self.longWait(5 * 60, 12)
-
- m = re.search(self.FREE_URL_PATTERN, self.html)
- if m is None:
- self.error(_("FREE_URL_PATTERN not found"))
- parsed_url = "http://euroshare.eu%s" % m.group(1)
- self.logDebug("URL", parsed_url)
- self.download(parsed_url, disposition=True)
-
- check = self.checkDownload({"multi_dl": re.compile(self.ERR_PARDL_PATTERN)})
- if check == "multi_dl":
- self.longWait(5 * 60, 12)
-
-
-getInfo = create_getInfo(EuroshareEu)
diff --git a/module/plugins/hoster/ExtabitCom.py b/module/plugins/hoster/ExtabitCom.py
deleted file mode 100644
index fc38a79a9..000000000
--- a/module/plugins/hoster/ExtabitCom.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.common.json_layer import json_loads
-
-from module.plugins.hoster.UnrestrictLi import secondsToMidnight
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class ExtabitCom(SimpleHoster):
- __name__ = "ExtabitCom"
- __type__ = "hoster"
- __version__ = "0.62"
-
- __pattern__ = r'http://(?:www\.)?extabit\.com/(file|go|fid)/(?P<ID>\w+)'
-
- __description__ = """Extabit.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<th>File:</th>\s*<td class="col-fileinfo">\s*<div title="(?P<N>[^"]+)">'
- SIZE_PATTERN = r'<th>Size:</th>\s*<td class="col-fileinfo">(?P<S>[^<]+)</td>'
- OFFLINE_PATTERN = r'>File not found<'
- TEMP_OFFLINE_PATTERN = r'>(File is temporary unavailable|No download mirror)<'
-
- LINK_PATTERN = r'[\'"](http://guest\d+\.extabit\.com/\w+/.*?)[\'"]'
-
-
- def handleFree(self):
- if r">Only premium users can download this file" in self.html:
- self.fail(_("Only premium users can download this file"))
-
- m = re.search(r"Next free download from your ip will be available in <b>(\d+)\s*minutes", self.html)
- if m:
- self.wait(int(m.group(1)) * 60, True)
- elif "The daily downloads limit from your IP is exceeded" in self.html:
- self.logWarning(_("You have reached your daily downloads limit for today"))
- self.wait(secondsToMidnight(gmt=2), True)
-
- self.logDebug("URL: " + self.req.http.lastEffectiveURL)
- m = re.match(self.__pattern__, self.req.http.lastEffectiveURL)
- fileID = m.group('ID') if m else self.info('ID')
-
- m = re.search(r'recaptcha/api/challenge\?k=(\w+)', self.html)
- if m:
- recaptcha = ReCaptcha(self)
- captcha_key = m.group(1)
-
- for _i in xrange(5):
- get_data = {"type": "recaptcha"}
- get_data['challenge'], get_data['capture'] = recaptcha.challenge(captcha_key)
- res = json_loads(self.load("http://extabit.com/file/%s/" % fileID, get=get_data))
- if "ok" in res:
- self.correctCaptcha()
- break
- else:
- self.invalidCaptcha()
- else:
- self.fail(_("Invalid captcha"))
- else:
- self.error(_("Captcha"))
-
- if not "href" in res:
- self.error(_("Bad JSON response"))
-
- self.html = self.load("http://extabit.com/file/%s%s" % (fileID, res['href']))
-
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("LINK_PATTERN not found"))
-
- url = m.group(1)
- self.download(url)
-
-
-getInfo = create_getInfo(ExtabitCom)
diff --git a/module/plugins/hoster/FastixRu.py b/module/plugins/hoster/FastixRu.py
deleted file mode 100644
index 1e47638ea..000000000
--- a/module/plugins/hoster/FastixRu.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from random import randrange
-from urllib import unquote
-
-from module.common.json_layer import json_loads
-from module.plugins.Hoster import Hoster
-
-
-class FastixRu(Hoster):
- __name__ = "FastixRu"
- __type__ = "hoster"
- __version__ = "0.04"
-
- __pattern__ = r'http://(?:www\.)?fastix\.(ru|it)/file/(?P<ID>\w{24})'
-
- __description__ = """Fastix hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Massimo Rosamilia", "max@spiritix.eu")]
-
-
- def getFilename(self, url):
- try:
- name = unquote(url.rsplit("/", 1)[1])
- except IndexError:
- name = "Unknown_Filename..."
- if name.endswith("..."): # incomplete filename, append random stuff
- name += "%s.tmp" % randrange(100, 999)
- return name
-
-
- def setup(self):
- self.chunkLimit = 3
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- if re.match(self.__pattern__, pyfile.url):
- new_url = pyfile.url
- elif not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "Fastix")
- self.fail(_("No Fastix account provided"))
- else:
- self.logDebug("Old URL: %s" % pyfile.url)
- api_key = self.account.getAccountData(self.user)
- api_key = api_key['api']
-
- page = self.load("http://fastix.ru/api_v2/",
- get={'apikey': api_key, 'sub': "getdirectlink", 'link': pyfile.url})
- data = json_loads(page)
-
- self.logDebug("Json data", data)
-
- if "error\":true" in page:
- self.offline()
- else:
- new_url = data['downloadlink']
-
- if new_url != pyfile.url:
- self.logDebug("New URL: %s" % new_url)
-
- if pyfile.name.startswith("http") or pyfile.name.startswith("Unknown"):
- #only use when name wasnt already set
- pyfile.name = self.getFilename(new_url)
-
- self.download(new_url, disposition=True)
-
- check = self.checkDownload({"error": "<title>An error occurred while processing your request</title>",
- "empty": re.compile(r"^$")})
-
- if check == "error":
- self.retry(wait_time=60, reason=_("An error occurred while generating link"))
- elif check == "empty":
- self.retry(wait_time=60, reason=_("Downloaded File was empty"))
diff --git a/module/plugins/hoster/FastshareCz.py b/module/plugins/hoster/FastshareCz.py
deleted file mode 100644
index 1f1e9e6ee..000000000
--- a/module/plugins/hoster/FastshareCz.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urljoin
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class FastshareCz(SimpleHoster):
- __name__ = "FastshareCz"
- __type__ = "hoster"
- __version__ = "0.24"
-
- __pattern__ = r'http://(?:www\.)?fastshare\.cz/\d+/.+'
-
- __description__ = """FastShare.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
- URL_REPLACEMENTS = [("#.*", "")]
-
- COOKIES = [("fastshare.cz", "lang", "en")]
- CONTENT_DISPOSITION = True
-
- INFO_PATTERN = r'<h1 class="dwp">(?P<N>[^<]+)</h1>\s*<div class="fileinfo">\s*Size\s*: (?P<S>\d+) (?P<U>[\w^_]+),'
- OFFLINE_PATTERN = r'>(The file has been deleted|Requested page not found)'
-
- LINK_FREE_PATTERN = r'action=(/free/.*?)>\s*<img src="([^"]*)"><br'
- LINK_PREMIUM_PATTERN = r'(http://data\d+\.fastshare\.cz/download\.php\?id=\d+&)'
-
- SLOT_ERROR = "> 100% of FREE slots are full"
- CREDIT_ERROR = " credit for "
-
-
- def checkErrors(self):
- if self.SLOT_ERROR in self.html:
- errmsg = self.info['error'] = _("No free slots")
- self.retry(12, 60, errmsg)
-
- if self.CREDIT_ERROR in self.html:
- errmsg = self.info['error'] = _("Not enough traffic left")
- self.logWarning(errmsg)
- self.resetAccount()
-
- self.info.pop('error', None)
-
-
- def handleFree(self):
- m = re.search(self.FREE_URL_PATTERN, self.html)
- if m:
- action, captcha_src = m.groups()
- else:
- self.error(_("FREE_URL_PATTERN not found"))
-
- baseurl = "http://www.fastshare.cz"
- captcha = self.decryptCaptcha(urljoin(baseurl, captcha_src))
- self.download(urljoin(baseurl, action), post={'code': captcha, 'btn.x': 77, 'btn.y': 18})
-
-
- def checkFile(self):
- check = self.checkDownload({
- 'paralell_dl' : re.compile(r"<title>FastShare.cz</title>|<script>alert\('Pres FREE muzete stahovat jen jeden soubor najednou.'\)"),
- 'wrong_captcha': re.compile(r'Download for FREE'),
- 'credit' : re.compile(self.CREDIT_ERROR)
- })
-
- if check == "paralell_dl":
- self.retry(6, 10 * 60, _("Paralell download"))
-
- elif check == "wrong_captcha":
- self.retry(max_tries=5, reason=_("Wrong captcha"))
-
- elif check == "credit":
- self.resetAccount()
-
-
-getInfo = create_getInfo(FastshareCz)
diff --git a/module/plugins/hoster/FileApeCom.py b/module/plugins/hoster/FileApeCom.py
deleted file mode 100644
index db843586b..000000000
--- a/module/plugins/hoster/FileApeCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class FileApeCom(DeadHoster):
- __name__ = "FileApeCom"
- __type__ = "hoster"
- __version__ = "0.12"
-
- __pattern__ = r'http://(?:www\.)?fileape\.com/(index\.php\?act=download\&id=|dl/)\w+'
-
- __description__ = """FileApe.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("espes", None)]
-
-
-getInfo = create_getInfo(FileApeCom)
diff --git a/module/plugins/hoster/FileParadoxIn.py b/module/plugins/hoster/FileParadoxIn.py
deleted file mode 100644
index 7ed0e9b7a..000000000
--- a/module/plugins/hoster/FileParadoxIn.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class FileParadoxIn(XFSHoster):
- __name__ = "FileParadoxIn"
- __type__ = "hoster"
- __version__ = "0.04"
-
- __pattern__ = r'https?://(?:www\.)?fileparadox\.in/\w{12}'
-
- __description__ = """FileParadox.in hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RazorWing", "muppetuk1@hotmail.com")]
-
-
- HOSTER_DOMAIN = "fileparadox.in"
-
- SIZE_PATTERN = r'</font>\s*\(\s*(?P<S>[^)]+)\s*\)</font>'
-
-
-getInfo = create_getInfo(FileParadoxIn)
diff --git a/module/plugins/hoster/FileSharkPl.py b/module/plugins/hoster/FileSharkPl.py
deleted file mode 100644
index ea2b56821..000000000
--- a/module/plugins/hoster/FileSharkPl.py
+++ /dev/null
@@ -1,140 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urljoin
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class FileSharkPl(SimpleHoster):
- __name__ = "FileSharkPl"
- __type__ = "hoster"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?fileshark\.pl/pobierz/\d{6}/\w{5}'
-
- __description__ = """FileShark.pl hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("prOq", None),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- CONTENT_DISPOSITION = True
-
- NAME_PATTERN = r'<h2 class="name-file">(?P<N>.+)</h2>'
- SIZE_PATTERN = r'<p class="size-file">(.*?)<strong>(?P<S>\d+\.?\d*)\s(?P<U>\w+)</strong></p>'
-
- OFFLINE_PATTERN = '(P|p)lik zosta. (usuni.ty|przeniesiony)'
-
- LINK_FREE_PATTERN = r'<a href="(.*?)" class="btn-upload-free">'
- LINK_PREMIUM_PATTERN = r'<a href="(.*?)" class="btn-upload-premium">'
-
- WAIT_PATTERN = r'var timeToDownload = (\d+);'
- ERROR_PATTERN = r'<p class="lead text-center alert alert-warning">(.*?)</p>'
- IP_ERROR_PATTERN = r'Strona jest dost.pna wy..cznie dla u.ytkownik.w znajduj.cych si. na terenie Polski'
- SLOT_ERROR_PATTERN = r'Osi.gni.to maksymaln. liczb. .ci.ganych jednocze.nie plik.w\.'
-
- CAPTCHA_PATTERN = '<img src="data:image/jpeg;base64,(.*?)" title="captcha"'
- TOKEN_PATTERN = r'name="form\[_token\]" value="(.*?)" />'
-
-
- def setup(self):
- self.resumeDownload = True
- if self.premium:
- self.multiDL = True
- self.limitDL = 20
- else:
- self.multiDL = False
-
-
- def checkErrors(self):
- # check if file is now available for download (-> file name can be found in html body)
- m = re.search(self.WAIT_PATTERN, self.html)
- if m:
- errmsg = self.info['error'] = _("Another download already run")
- self.retry(15, int(m.group(1)), errmsg)
-
- m = re.search(self.ERROR_PATTERN, self.html):
- if m:
- alert = m.group(1)
-
- if re.match(self.IP_ERROR_PATTERN, alert):
- self.fail(_("Only connections from Polish IP are allowed"))
-
- elif re.match(self.SLOT_ERROR_PATTERN, alert):
- errmsg = self.info['error'] = _("No free download slots available")
- self.logWarning(errmsg)
- self.retry(10, 30 * 60, _("Still no free download slots available"))
-
- else:
- self.info['error'] = alert
- self.retry(10, 10 * 60, _("Try again later"))
-
- self.info.pop('error', None)
-
-
- #@NOTE: handlePremium method was never been tested
- def handlePremium(self):
- super(FilerNet, self).handlePremium()
- if self.link:
- self.link = urljoin("http://fileshark.pl/", self.link)
-
-
- def handleFree(self):
- m = re.search(self.LINK_FREE_PATTERN, self.html)
- if m is None:
- self.error(_("Download url not found"))
-
- link = urljoin("http://fileshark.pl", m.group(1))
-
- m = re.search(self.WAIT_PATTERN, self.html)
- if m:
- seconds = int(m.group(1))
- self.logDebug("Wait %s seconds" % seconds)
- self.wait(seconds)
-
- action, inputs = self.parseHtmlForm('action=""')
-
- m = re.search(self.TOKEN_PATTERN, self.html)
- if m is None:
- self.retry(reason=_("Captcha form not found"))
-
- inputs['form[_token]'] = m.group(1)
-
- m = re.search(self.CAPTCHA_PATTERN, self.html)
- if m is None:
- self.retry(reason=_("Captcha image not found"))
-
- tmp_load = self.load
- self.load = self._decode64 #: work-around: injects decode64 inside decryptCaptcha
-
- inputs['form[captcha]'] = self.decryptCaptcha(m.group(1), imgtype='jpeg')
- inputs['form[start]'] = ""
-
- self.load = tmp_load
-
- self.download(link, post=inputs, cookies=True, disposition=True)
-
-
- def checkFile(self):
- check = self.checkDownload({'wrong_captcha': re.compile(r'<label for="form_captcha" generated="true" class="error">(.*?)</label>'),
- 'wait_pattern' : re.compile(self.SECONDS_PATTERN),
- 'DL-found' : re.compile('<a href="(.*)">')})
-
- if check == "DL-found":
- self.correctCaptcha()
-
- elif check == "wrong_captcha":
- self.invalidCaptcha()
- self.retry(10, 1, _("Wrong captcha solution"))
-
- elif check == "wait_pattern":
- self.retry()
-
-
- def _decode64(self, data, *args, **kwargs):
- return data.decode("base64")
-
-
-getInfo = create_getInfo(FileSharkPl)
diff --git a/module/plugins/hoster/FileStoreTo.py b/module/plugins/hoster/FileStoreTo.py
deleted file mode 100644
index e1bd8da71..000000000
--- a/module/plugins/hoster/FileStoreTo.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class FileStoreTo(SimpleHoster):
- __name__ = "FileStoreTo"
- __type__ = "hoster"
- __version__ = "0.01"
-
- __pattern__ = r'http://(?:www\.)?filestore\.to/\?d=(?P<ID>\w+)'
-
- __description__ = """FileStore.to hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- INFO_PATTERN = r'File: <span[^>]*>(?P<N>.+)</span><br />Size: (?P<S>[\d.,]+) (?P<U>[\w^_]+)'
- OFFLINE_PATTERN = r'>Download-Datei wurde nicht gefunden<'
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
-
-
- def handleFree(self):
- self.wait(10)
- ldc = re.search(r'wert="(\w+)"', self.html).group(1)
- link = self.load("http://filestore.to/ajax/download.php", get={"LDC": ldc})
- self.download(link)
-
-
-getInfo = create_getInfo(FileStoreTo)
diff --git a/module/plugins/hoster/FilebeerInfo.py b/module/plugins/hoster/FilebeerInfo.py
deleted file mode 100644
index ff1194ecc..000000000
--- a/module/plugins/hoster/FilebeerInfo.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class FilebeerInfo(DeadHoster):
- __name__ = "FilebeerInfo"
- __type__ = "hoster"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?filebeer\.info/(?!\d*~f)(?P<ID>\w+).*'
-
- __description__ = """Filebeer.info plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(FilebeerInfo)
diff --git a/module/plugins/hoster/FilecloudIo.py b/module/plugins/hoster/FilecloudIo.py
deleted file mode 100644
index 495f2268d..000000000
--- a/module/plugins/hoster/FilecloudIo.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.common.json_layer import json_loads
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class FilecloudIo(SimpleHoster):
- __name__ = "FilecloudIo"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'http://(?:www\.)?(?:filecloud\.io|ifile\.it|mihd\.net)/(?P<ID>\w+).*'
-
- __description__ = """Filecloud.io hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- SIZE_PATTERN = r'{var __ab1 = (?P<S>\d+);}'
- NAME_PATTERN = r'id="aliasSpan">(?P<N>.*?)&nbsp;&nbsp;<'
- OFFLINE_PATTERN = r'l10n\.(FILES__DOESNT_EXIST|REMOVED)'
- TEMP_OFFLINE_PATTERN = r'l10n\.FILES__WARNING'
-
- UKEY_PATTERN = r'\'ukey\'\s*:\'(\w+)'
- AB1_PATTERN = r'if\( __ab1 == \'(\w+)\' \)'
- ERROR_MSG_PATTERN = r'var __error_msg\s*=\s*l10n\.(.*?);'
- RECAPTCHA_PATTERN = r'var __recaptcha_public\s*=\s*\'(.+?)\';'
-
- LINK_PATTERN = r'"(http://s\d+\.filecloud\.io/%s/\d+/.*?)"'
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
- self.chunkLimit = 1
-
-
- def handleFree(self):
- data = {"ukey": self.info['pattern']['ID']}
-
- m = re.search(self.AB1_PATTERN, self.html)
- if m is None:
- self.error(_("__AB1"))
- data['__ab1'] = m.group(1)
-
- recaptcha = ReCaptcha(self)
-
- m = re.search(self.RECAPTCHA_PATTERN, self.html)
- captcha_key = m.group(1) if m else recaptcha.detect_key()
-
- if captcha_key is None:
- self.error(_("ReCaptcha key not found"))
-
- if not self.account:
- self.fail(_("User not logged in"))
- elif not self.account.logged_in:
- captcha_challenge, captcha_response = recaptcha.challenge(captcha_key)
- self.account.form_data = {"recaptcha_challenge_field": captcha_challenge,
- "recaptcha_response_field": captcha_response}
- self.account.relogin(self.user)
- self.retry(2)
-
- json_url = "http://filecloud.io/download-request.json"
- res = self.load(json_url, post=data)
- self.logDebug(res)
- res = json_loads(res)
-
- if "error" in res and res['error']:
- self.fail(res)
-
- self.logDebug(res)
- if res['captcha']:
- data['ctype'] = "recaptcha"
-
- for _i in xrange(5):
- data['recaptcha_challenge'], data['recaptcha_response'] = recaptcha.challenge(captcha_key)
-
- json_url = "http://filecloud.io/download-request.json"
- res = self.load(json_url, post=data)
- self.logDebug(res)
- res = json_loads(res)
-
- if "retry" in res and res['retry']:
- self.invalidCaptcha()
- else:
- self.correctCaptcha()
- break
- else:
- self.fail(_("Incorrect captcha"))
-
- if res['dl']:
- self.html = self.load('http://filecloud.io/download.html')
-
- m = re.search(self.LINK_PATTERN % self.info['pattern']['ID'], self.html)
- if m is None:
- self.error(_("LINK_PATTERN not found"))
-
- if "size" in self.info and self.info['size']:
- self.check_data = {"size": int(self.info['size'])}
-
- download_url = m.group(1)
- self.download(download_url)
- else:
- self.fail(_("Unexpected server response"))
-
-
- def handlePremium(self):
- akey = self.account.getAccountData(self.user)['akey']
- ukey = self.info['pattern']['ID']
- self.logDebug("Akey: %s | Ukey: %s" % (akey, ukey))
- rep = self.load("http://api.filecloud.io/api-fetch_download_url.api",
- post={"akey": akey, "ukey": ukey})
- self.logDebug("FetchDownloadUrl: " + rep)
- rep = json_loads(rep)
- if rep['status'] == 'ok':
- self.download(rep['download_url'], disposition=True)
- else:
- self.fail(rep['message'])
-
-
-getInfo = create_getInfo(FilecloudIo)
diff --git a/module/plugins/hoster/FilefactoryCom.py b/module/plugins/hoster/FilefactoryCom.py
deleted file mode 100644
index ada498a51..000000000
--- a/module/plugins/hoster/FilefactoryCom.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urljoin
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.SimpleHoster import SimpleHoster, parseFileInfo
-
-
-def getInfo(urls):
- for url in urls:
- h = getURL(url, just_header=True)
- m = re.search(r'Location: (.+)\r\n', h)
- if m and not re.match(m.group(1), FilefactoryCom.__pattern__): #: It's a direct link! Skipping
- yield (url, 0, 3, url)
- else: #: It's a standard html page
- yield parseFileInfo(FilefactoryCom, url, getURL(url))
-
-
-class FilefactoryCom(SimpleHoster):
- __name__ = "FilefactoryCom"
- __type__ = "hoster"
- __version__ = "0.52"
-
- __pattern__ = r'https?://(?:www\.)?filefactory\.com/(file|trafficshare/\w+)/\w+'
-
- __description__ = """Filefactory.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- INFO_PATTERN = r'<div id="file_name"[^>]*>\s*<h2>(?P<N>[^<]+)</h2>\s*<div id="file_info">\s*(?P<S>[\d.,]+) (?P<U>[\w^_]+) uploaded'
- OFFLINE_PATTERN = r'<h2>File Removed</h2>|This file is no longer available'
-
- LINK_PATTERN = r'"([^"]+filefactory\.com/get.+?)"'
-
- WAIT_PATTERN = r'<div id="countdown_clock" data-delay="(\d+)">'
- PREMIUM_ONLY_PATTERN = r'>Premium Account Required'
-
- COOKIES = [("filefactory.com", "locale", "en_US.utf8")]
-
-
- def handleFree(self):
- if "Currently only Premium Members can download files larger than" in self.html:
- self.fail(_("File too large for free download"))
- elif "All free download slots on this server are currently in use" in self.html:
- self.retry(50, 15 * 60, _("All free slots are busy"))
-
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("Free download link not found"))
-
- dl_link = m.group(1)
-
- m = re.search(self.WAIT_PATTERN, self.html)
- if m:
- self.wait(int(m.group(1)))
-
- self.download(dl_link, disposition=True)
-
- check = self.checkDownload({'multiple': "You are currently downloading too many files at once.",
- 'error': '<div id="errorMessage">'})
-
- if check == "multiple":
- self.logDebug("Parallel downloads detected; waiting 15 minutes")
- self.retry(wait_time=15 * 60, reason=_("Parallel downloads"))
- elif check == "error":
- self.error(_("Unknown error"))
-
-
- def handlePremium(self):
- header = self.load(self.pyfile.url, just_header=True)
-
- if 'location' in header:
- url = header['location'].strip()
- if not url.startswith("http://"):
- url = urljoin("http://www.filefactory.com", url)
- elif 'content-disposition' in header:
- url = self.pyfile.url
- else:
- html = self.load(self.pyfile.url)
- m = re.search(self.LINK_PATTERN, html)
- if m:
- url = m.group(1)
- else:
- self.error(_("Premium download link not found"))
-
- self.download(url, disposition=True)
diff --git a/module/plugins/hoster/FilejungleCom.py b/module/plugins/hoster/FilejungleCom.py
deleted file mode 100644
index 8431da7fa..000000000
--- a/module/plugins/hoster/FilejungleCom.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.hoster.FileserveCom import FileserveCom, checkFile
-from module.plugins.Plugin import chunks
-
-
-class FilejungleCom(FileserveCom):
- __name__ = "FilejungleCom"
- __type__ = "hoster"
- __version__ = "0.51"
-
- __pattern__ = r'http://(?:www\.)?filejungle\.com/f/(?P<id>[^/]+).*'
-
- __description__ = """Filejungle.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- URLS = ["http://www.filejungle.com/f/", "http://www.filejungle.com/check_links.php",
- "http://www.filejungle.com/checkReCaptcha.php"]
- LINKCHECK_TR = r'<li>\s*(<div class="col1">.*?)</li>'
- LINKCHECK_TD = r'<div class="(?:col )?col\d">(?:<[^>]*>|&nbsp;)*([^<]*)'
-
- LONG_WAIT_PATTERN = r'<h1>Please wait for (\d+) (\w+)\s*to download the next file\.</h1>'
-
-
-def getInfo(urls):
- for chunk in chunks(urls, 100):
- yield checkFile(FilejungleCom, chunk)
diff --git a/module/plugins/hoster/FileomCom.py b/module/plugins/hoster/FileomCom.py
deleted file mode 100644
index 2b6fd34db..000000000
--- a/module/plugins/hoster/FileomCom.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://fileom.com/gycaytyzdw3g/random.bin.html
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class FileomCom(XFSHoster):
- __name__ = "FileomCom"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'https?://(?:www\.)?fileom\.com/\w{12}'
-
- __description__ = """Fileom.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "fileom.com"
-
- NAME_PATTERN = r'Filename: <span>(?P<N>.+?)<'
- SIZE_PATTERN = r'File Size: <span class="size">(?P<S>[\d.,]+) (?P<U>[\w^_]+)'
-
- LINK_PATTERN = r'var url2 = \'(.+?)\';'
-
-
- def setup(self):
- self.multiDL = True
- self.chunkLimit = 1
- self.resumeDownload = self.premium
-
-
-getInfo = create_getInfo(FileomCom)
diff --git a/module/plugins/hoster/FilepostCom.py b/module/plugins/hoster/FilepostCom.py
deleted file mode 100644
index db5ea20d3..000000000
--- a/module/plugins/hoster/FilepostCom.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import time
-
-from module.common.json_layer import json_loads
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class FilepostCom(SimpleHoster):
- __name__ = "FilepostCom"
- __type__ = "hoster"
- __version__ = "0.30"
-
- __pattern__ = r'https?://(?:www\.)?(?:filepost\.com/files|fp\.io)/(?P<ID>[^/]+)'
-
- __description__ = """Filepost.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- INFO_PATTERN = r'<input type="text" id="url" value=\'<a href[^>]*>(?P<N>[^>]+?) - (?P<S>[\d.,]+) (?P<U>[\w^_]+)</a>\' class="inp_text"/>'
- OFFLINE_PATTERN = r'class="error_msg_title"> Invalid or Deleted File. </div>|<div class="file_info file_info_deleted">'
-
- PREMIUM_ONLY_PATTERN = r'members only. Please upgrade to premium|a premium membership is required to download this file'
- RECAPTCHA_PATTERN = r'Captcha.init\({\s*key:\s*\'(.+?)\''
- FLP_TOKEN_PATTERN = r'set_store_options\({token: \'(.+?)\''
-
-
- def handleFree(self):
- m = re.search(self.FLP_TOKEN_PATTERN, self.html)
- if m is None:
- self.error(_("Token"))
- flp_token = m.group(1)
-
- m = re.search(self.RECAPTCHA_PATTERN, self.html)
- if m is None:
- self.error(_("Captcha key"))
- captcha_key = m.group(1)
-
- # Get wait time
- get_dict = {'SID': self.req.cj.getCookie('SID'), 'JsHttpRequest': str(int(time() * 10000)) + '-xml'}
- post_dict = {'action': 'set_download', 'token': flp_token, 'code': self.info['pattern']['ID']}
- wait_time = int(self.getJsonResponse(get_dict, post_dict, 'wait_time'))
-
- if wait_time > 0:
- self.wait(wait_time)
-
- post_dict = {"token": flp_token, "code": self.info['pattern']['ID'], "file_pass": ''}
-
- if 'var is_pass_exists = true;' in self.html:
- # Solve password
- for file_pass in self.getPassword().splitlines():
- get_dict['JsHttpRequest'] = str(int(time() * 10000)) + '-xml'
- post_dict['file_pass'] = file_pass
- self.logInfo(_("Password protected link, trying ") + file_pass)
-
- download_url = self.getJsonResponse(get_dict, post_dict, 'link')
- if download_url:
- break
-
- else:
- self.fail(_("No or incorrect password"))
-
- else:
- # Solve recaptcha
- recaptcha = ReCaptcha(self)
-
- for i in xrange(5):
- get_dict['JsHttpRequest'] = str(int(time() * 10000)) + '-xml'
- if i:
- post_dict['recaptcha_challenge_field'], post_dict['recaptcha_response_field'] = recaptcha.challenge(
- captcha_key)
- self.logDebug(u"RECAPTCHA: %s : %s : %s" % (
- captcha_key, post_dict['recaptcha_challenge_field'], post_dict['recaptcha_response_field']))
-
- download_url = self.getJsonResponse(get_dict, post_dict, 'link')
- if download_url:
- if i:
- self.correctCaptcha()
- break
- elif i:
- self.invalidCaptcha()
-
- else:
- self.fail(_("Invalid captcha"))
-
- # Download
- self.download(download_url)
-
-
- def getJsonResponse(self, get_dict, post_dict, field):
- json_response = json_loads(self.load('https://filepost.com/files/get/', get=get_dict, post=post_dict))
- self.logDebug(json_response)
-
- if not 'js' in json_response:
- self.error(_("JSON %s 1") % field)
-
- # i changed js_answer to json_response['js'] since js_answer is nowhere set.
- # i don't know the JSON-HTTP specs in detail, but the previous author
- # accessed json_response['js']['error'] as well as js_answer['error'].
- # see the two lines commented out with "# ~?".
- if 'error' in json_response['js']:
- if json_response['js']['error'] == 'download_delay':
- self.retry(wait_time=json_response['js']['params']['next_download'])
- # ~? self.retry(wait_time=js_answer['params']['next_download'])
- elif 'Wrong file password' in json_response['js']['error']:
- return None
- elif 'You entered a wrong CAPTCHA code' in json_response['js']['error']:
- return None
- elif 'CAPTCHA Code nicht korrekt' in json_response['js']['error']:
- return None
- elif 'CAPTCHA' in json_response['js']['error']:
- self.logDebug("Error response is unknown, but mentions CAPTCHA")
- return None
- else:
- self.fail(json_response['js']['error'])
-
- if not 'answer' in json_response['js'] or not field in json_response['js']['answer']:
- self.error(_("JSON %s 2") % field)
-
- return json_response['js']['answer'][field]
-
-
-getInfo = create_getInfo(FilepostCom)
diff --git a/module/plugins/hoster/FilepupNet.py b/module/plugins/hoster/FilepupNet.py
deleted file mode 100644
index cc3e86bc9..000000000
--- a/module/plugins/hoster/FilepupNet.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://www.filepup.net/files/k5w4ZVoF1410184283.html
-# http://www.filepup.net/files/R4GBq9XH1410186553.html
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class FilepupNet(SimpleHoster):
- __name__ = "FilepupNet"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?filepup\.net/files/\w+'
-
- __description__ = """Filepup.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zapp-brannigan", "fuerst.reinje@web.de"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- NAME_PATTERN = r'>(?P<N>.+?)</h1>'
- SIZE_PATTERN = r'class="fa fa-archive"></i> \((?P<S>[\d.,]+) (?P<U>[\w^_]+)'
-
- OFFLINE_PATTERN = r'>This file has been deleted'
-
- LINK_PATTERN = r'(http://www\.filepup\.net/get/.+?)\''
-
-
- def setup(self):
- self.multiDL = False
- self.chunkLimit = 1
-
-
- def handleFree(self):
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("Download link not found"))
-
- dl_link = m.group(1)
- self.download(dl_link, post={'task': "download"})
-
- check = self.checkDownload({'html': re.compile("html")})
- if check == "html":
- self.error(_("Downloaded file is an html page"))
-
-
-getInfo = create_getInfo(FilepupNet)
diff --git a/module/plugins/hoster/FilerNet.py b/module/plugins/hoster/FilerNet.py
deleted file mode 100644
index c943a076d..000000000
--- a/module/plugins/hoster/FilerNet.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://filer.net/get/ivgf5ztw53et3ogd
-# http://filer.net/get/hgo14gzcng3scbvv
-
-import re
-
-from urlparse import urljoin
-
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class FilerNet(SimpleHoster):
- __name__ = "FilerNet"
- __type__ = "hoster"
- __version__ = "0.09"
-
- __pattern__ = r'https?://(?:www\.)?filer\.net/get/\w+'
-
- __description__ = """Filer.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- CONTENT_DISPOSITION = True
-
- INFO_PATTERN = r'<h1 class="page-header">Free Download (?P<N>\S+) <small>(?P<S>[\w.]+) (?P<U>[\w^_]+)</small></h1>'
- OFFLINE_PATTERN = r'Nicht gefunden'
-
- LINK_FREE_PATTERN = LINK_PREMIUM_PATTERN = r'href="([^"]+)">Get download</a>'
-
-
- def checkErrors(self):
- # Wait between downloads
- m = re.search(r'musst du <span id="time">(\d+)</span> Sekunden warten', self.html)
- if m:
- errmsg = self.info['error'] = _("Wait between free downloads")
- self.retry(wait_time=int(m.group(1)), reason=errmsg)
-
- self.info.pop('error', None)
-
-
- def handleFree(self):
- inputs = self.parseHtmlForm(input_names={'token': re.compile(r'.+')})[1]
- if 'token' not in inputs:
- self.error(_("Unable to detect token"))
-
- self.html = self.load(self.pyfile.url, post={'token': inputs['token']}, decode=True)
-
- inputs = self.parseHtmlForm(input_names={'hash': re.compile(r'.+')})[1]
- if 'hash' not in inputs:
- self.error(_("Unable to detect hash"))
-
- recaptcha = ReCaptcha(self)
-
- for _i in xrange(5):
- challenge, response = recaptcha.challenge()
-
- header = self.load(self.pyfile.url,
- post={'recaptcha_challenge_field': challenge,
- 'recaptcha_response_field' : response,
- 'hash' : inputs['hash']})
-
- if 'location' in header and header['location']:
- self.correctCaptcha()
- self.link = urljoin('http://filer.net', header['location'])
- return
- else:
- self.invalidCaptcha()
-
-
- def handlePremium(self):
- super(FilerNet, self).handlePremium()
- if self.link:
- self.link = urljoin("http://filer.net/", self.link)
-
-
-getInfo = create_getInfo(FilerNet)
diff --git a/module/plugins/hoster/FilerioCom.py b/module/plugins/hoster/FilerioCom.py
deleted file mode 100644
index db81f5b16..000000000
--- a/module/plugins/hoster/FilerioCom.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class FilerioCom(XFSHoster):
- __name__ = "FilerioCom"
- __type__ = "hoster"
- __version__ = "0.07"
-
- __pattern__ = r'http://(?:www\.)?(filerio\.(in|com)|filekeen\.com)/\w{12}'
-
- __description__ = """FileRio.in hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- HOSTER_DOMAIN = "filerio.in"
-
- URL_REPLACEMENTS = [(r'filekeen\.com', "filerio.in")]
-
- OFFLINE_PATTERN = r'>&quot;File Not Found|File has been removed'
-
-
-getInfo = create_getInfo(FilerioCom)
diff --git a/module/plugins/hoster/FilesMailRu.py b/module/plugins/hoster/FilesMailRu.py
deleted file mode 100644
index 92ff5a02a..000000000
--- a/module/plugins/hoster/FilesMailRu.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
-from module.plugins.Plugin import chunks
-
-
-def getInfo(urls):
- result = []
- for chunk in chunks(urls, 10):
- for url in chunk:
- html = getURL(url)
- if r'<div class="errorMessage mb10">' in html:
- result.append((url, 0, 1, url))
- elif r'Page cannot be displayed' in html:
- result.append((url, 0, 1, url))
- else:
- try:
- url_pattern = '<a href="(.+?)" onclick="return Act\(this\, \'dlink\'\, event\)">(.+?)</a>'
- file_name = re.search(url_pattern, html).group(0).split(', event)">')[1].split('</a>')[0]
- result.append((file_name, 0, 2, url))
- except:
- pass
-
- # status 1=OFFLINE, 2=OK, 3=UNKNOWN
- # result.append((#name,#size,#status,#url))
- yield result
-
-
-class FilesMailRu(Hoster):
- __name__ = "FilesMailRu"
- __type__ = "hoster"
- __version__ = "0.31"
-
- __pattern__ = r'http://(?:www\.)?files\.mail\.ru/.*'
-
- __description__ = """Files.mail.ru hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("oZiRiz", "ich@oziriz.de")]
-
-
- def setup(self):
- if not self.account:
- self.multiDL = False
-
-
- def process(self, pyfile):
- self.html = self.load(pyfile.url)
- self.url_pattern = '<a href="(.+?)" onclick="return Act\(this\, \'dlink\'\, event\)">(.+?)</a>'
-
- #marks the file as "offline" when the pattern was found on the html-page'''
- if r'<div class="errorMessage mb10">' in self.html:
- self.offline()
-
- elif r'Page cannot be displayed' in self.html:
- self.offline()
-
- #the filename that will be showed in the list (e.g. test.part1.rar)'''
- pyfile.name = self.getFileName()
-
- #prepare and download'''
- if not self.account:
- self.prepare()
- self.download(self.getFileUrl())
- self.myPostProcess()
- else:
- self.download(self.getFileUrl())
- self.myPostProcess()
-
-
- def prepare(self):
- """You have to wait some seconds. Otherwise you will get a 40Byte HTML Page instead of the file you expected"""
- self.setWait(10)
- self.wait()
- return True
-
-
- def getFileUrl(self):
- """gives you the URL to the file. Extracted from the Files.mail.ru HTML-page stored in self.html"""
- return re.search(self.url_pattern, self.html).group(0).split('<a href="')[1].split('" onclick="return Act')[0]
-
-
- def getFileName(self):
- """gives you the Name for each file. Also extracted from the HTML-Page"""
- return re.search(self.url_pattern, self.html).group(0).split(', event)">')[1].split('</a>')[0]
-
-
- def myPostProcess(self):
- # searches the file for HTMl-Code. Sometimes the Redirect
- # doesn't work (maybe a curl Problem) and you get only a small
- # HTML file and the Download is marked as "finished"
- # then the download will be restarted. It's only bad for these
- # who want download a HTML-File (it's one in a million ;-) )
- #
- # The maximum UploadSize allowed on files.mail.ru at the moment is 100MB
- # so i set it to check every download because sometimes there are downloads
- # that contain the HTML-Text and 60MB ZEROs after that in a xyzfile.part1.rar file
- # (Loading 100MB in to ram is not an option)
- check = self.checkDownload({"html": "<meta name="}, read_size=50000)
- if check == "html":
- self.logInfo(_(
- "There was HTML Code in the Downloaded File (%s)...redirect error? The Download will be restarted." %
- self.pyfile.name))
- self.retry()
diff --git a/module/plugins/hoster/FileserveCom.py b/module/plugins/hoster/FileserveCom.py
deleted file mode 100644
index 82d40fba6..000000000
--- a/module/plugins/hoster/FileserveCom.py
+++ /dev/null
@@ -1,217 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.common.json_layer import json_loads
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
-from module.plugins.Plugin import chunks
-from module.plugins.hoster.UnrestrictLi import secondsToMidnight
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.utils import parseFileSize
-
-
-def checkFile(plugin, urls):
- html = getURL(plugin.URLS[1], post={"urls": "\n".join(urls)}, decode=True)
-
- file_info = []
- for li in re.finditer(plugin.LINKCHECK_TR, html, re.S):
- try:
- cols = re.findall(plugin.LINKCHECK_TD, li.group(1))
- if cols:
- file_info.append((
- cols[1] if cols[1] != '--' else cols[0],
- parseFileSize(cols[2]) if cols[2] != '--' else 0,
- 2 if cols[3].startswith('Available') else 1,
- cols[0]))
- except Exception, e:
- continue
-
- return file_info
-
-
-class FileserveCom(Hoster):
- __name__ = "FileserveCom"
- __type__ = "hoster"
- __version__ = "0.52"
-
- __pattern__ = r'http://(?:www\.)?fileserve\.com/file/(?P<id>[^/]+).*'
-
- __description__ = """Fileserve.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de"),
- ("mkaay", "mkaay@mkaay.de"),
- ("Paul King", None),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- URLS = ["http://www.fileserve.com/file/", "http://www.fileserve.com/link-checker.php",
- "http://www.fileserve.com/checkReCaptcha.php"]
- LINKCHECK_TR = r'<tr>\s*(<td>http://www\.fileserve\.com/file/.*?)</tr>'
- LINKCHECK_TD = r'<td>(?:<[^>]*>|&nbsp;)*([^<]*)'
-
- CAPTCHA_KEY_PATTERN = r'var reCAPTCHA_publickey=\'(?P<key>.+?)\''
- LONG_WAIT_PATTERN = r'<li class="title">You need to wait (\d+) (\w+) to start another download\.</li>'
- LINK_EXPIRED_PATTERN = r'Your download link has expired'
- DAILY_LIMIT_PATTERN = r'Your daily download limit has been reached'
- NOT_LOGGED_IN_PATTERN = r'<form (name="loginDialogBoxForm"|id="login_form")|<li><a href="/login\.php">Login</a></li>'
-
-
- def setup(self):
- self.resumeDownload = self.multiDL = self.premium
- self.file_id = re.match(self.__pattern__, self.pyfile.url).group('id')
- self.url = "%s%s" % (self.URLS[0], self.file_id)
-
- self.logDebug("File ID: %s URL: %s" % (self.file_id, self.url))
-
-
- def process(self, pyfile):
- pyfile.name, pyfile.size, status, self.url = checkFile(self, [self.url])[0]
- if status != 2:
- self.offline()
- self.logDebug("File Name: %s Size: %d" % (pyfile.name, pyfile.size))
-
- if self.premium:
- self.handlePremium()
- else:
- self.handleFree()
-
-
- def handleFree(self):
- self.html = self.load(self.url)
- action = self.load(self.url, post={"checkDownload": "check"}, decode=True)
- action = json_loads(action)
- self.logDebug(action)
-
- if "fail" in action:
- if action['fail'] == "timeLimit":
- self.html = self.load(self.url, post={"checkDownload": "showError", "errorType": "timeLimit"},
- decode=True)
-
- self.doLongWait(re.search(self.LONG_WAIT_PATTERN, self.html))
-
- elif action['fail'] == "parallelDownload":
- self.logWarning(_("Parallel download error, now waiting 60s"))
- self.retry(wait_time=60, reason=_("parallelDownload"))
-
- else:
- self.fail(_("Download check returned: %s") % action['fail'])
-
- elif "success" in action:
- if action['success'] == "showCaptcha":
- self.doCaptcha()
- self.doTimmer()
- elif action['success'] == "showTimmer":
- self.doTimmer()
-
- else:
- self.error(_("Unknown server response"))
-
- # show download link
- res = self.load(self.url, post={"downloadLink": "show"}, decode=True)
- self.logDebug("Show downloadLink response: %s" % res)
- if "fail" in res:
- self.error(_("Couldn't retrieve download url"))
-
- # this may either download our file or forward us to an error page
- self.download(self.url, post={"download": "normal"})
- self.logDebug(self.req.http.lastEffectiveURL)
-
- check = self.checkDownload({"expired": self.LINK_EXPIRED_PATTERN,
- "wait": re.compile(self.LONG_WAIT_PATTERN),
- "limit": self.DAILY_LIMIT_PATTERN})
-
- if check == "expired":
- self.logDebug("Download link was expired")
- self.retry()
- elif check == "wait":
- self.doLongWait(self.lastCheck)
- elif check == "limit":
- self.logWarning(_("Download limited reached for today"))
- self.setWait(secondsToMidnight(gmt=2), True)
- self.wait()
- self.retry()
-
- self.thread.m.reconnecting.wait(3) # Ease issue with later downloads appearing to be in parallel
-
-
- def doTimmer(self):
- res = self.load(self.url, post={"downloadLink": "wait"}, decode=True)
- self.logDebug("Wait response: %s" % res[:80])
-
- if "fail" in res:
- self.fail(_("Failed getting wait time"))
-
- if self.__name__ == "FilejungleCom":
- m = re.search(r'"waitTime":(\d+)', res)
- if m is None:
- self.fail(_("Cannot get wait time"))
- wait_time = int(m.group(1))
- else:
- wait_time = int(res) + 3
-
- self.setWait(wait_time)
- self.wait()
-
-
- def doCaptcha(self):
- captcha_key = re.search(self.CAPTCHA_KEY_PATTERN, self.html).group("key")
- recaptcha = ReCaptcha(self)
-
- for _i in xrange(5):
- challenge, code = recaptcha.challenge(captcha_key)
- res = json_loads(self.load(self.URLS[2],
- post={'recaptcha_challenge_field': challenge,
- 'recaptcha_response_field': code,
- 'recaptcha_shortencode_field': self.file_id}))
- if not res['success']:
- self.invalidCaptcha()
- else:
- self.correctCaptcha()
- break
- else:
- self.fail(_("Invalid captcha"))
-
-
- def doLongWait(self, m):
- wait_time = (int(m.group(1)) * {'seconds': 1, 'minutes': 60, 'hours': 3600}[m.group(2)]) if m else 12 * 60
- self.setWait(wait_time, True)
- self.wait()
- self.retry()
-
-
- def handlePremium(self):
- premium_url = None
- if self.__name__ == "FileserveCom":
- #try api download
- res = self.load("http://app.fileserve.com/api/download/premium/",
- post={"username": self.user,
- "password": self.account.getAccountData(self.user)['password'],
- "shorten": self.file_id},
- decode=True)
- if res:
- res = json_loads(res)
- if res['error_code'] == "302":
- premium_url = res['next']
- elif res['error_code'] in ["305", "500"]:
- self.tempOffline()
- elif res['error_code'] in ["403", "605"]:
- self.resetAccount()
- elif res['error_code'] in ["606", "607", "608"]:
- self.offline()
- else:
- self.logError(res['error_code'], res['error_message'])
-
- self.download(premium_url or self.pyfile.url)
-
- if not premium_url:
- check = self.checkDownload({"login": re.compile(self.NOT_LOGGED_IN_PATTERN)})
-
- if check == "login":
- self.account.relogin(self.user)
- self.retry(reason=_("Not logged in"))
-
-
-def getInfo(urls):
- for chunk in chunks(urls, 100):
- yield checkFile(FileserveCom, chunk)
diff --git a/module/plugins/hoster/FileshareInUa.py b/module/plugins/hoster/FileshareInUa.py
deleted file mode 100644
index 08e10dccb..000000000
--- a/module/plugins/hoster/FileshareInUa.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class FileshareInUa(DeadHoster):
- __name__ = "FileshareInUa"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'https?://(?:www\.)?fileshare\.in\.ua/\w{7}'
-
- __description__ = """Fileshare.in.ua hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("fwannmacher", "felipe@warhammerproject.com")]
-
-
-getInfo = create_getInfo(FileshareInUa)
diff --git a/module/plugins/hoster/FilesonicCom.py b/module/plugins/hoster/FilesonicCom.py
deleted file mode 100644
index 8bfa0fa2e..000000000
--- a/module/plugins/hoster/FilesonicCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class FilesonicCom(DeadHoster):
- __name__ = "FilesonicCom"
- __type__ = "hoster"
- __version__ = "0.35"
-
- __pattern__ = r'http://(?:www\.)?filesonic\.com/file/\w+'
-
- __description__ = """Filesonic.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de"),
- ("paulking", None)]
-
-
-getInfo = create_getInfo(FilesonicCom)
diff --git a/module/plugins/hoster/FilezyNet.py b/module/plugins/hoster/FilezyNet.py
deleted file mode 100644
index d86ee6157..000000000
--- a/module/plugins/hoster/FilezyNet.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class FilezyNet(DeadHoster):
- __name__ = "FilezyNet"
- __type__ = "hoster"
- __version__ = "0.2"
-
- __pattern__ = r'http://(?:www\.)?filezy\.net/\w{12}'
-
- __description__ = """Filezy.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = []
-
-
-getInfo = create_getInfo(FilezyNet)
diff --git a/module/plugins/hoster/FiredriveCom.py b/module/plugins/hoster/FiredriveCom.py
deleted file mode 100644
index 0e3a4e847..000000000
--- a/module/plugins/hoster/FiredriveCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class FiredriveCom(DeadHoster):
- __name__ = "FiredriveCom"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'https?://(?:www\.)?(firedrive|putlocker)\.com/(mobile/)?(file|embed)/(?P<ID>\w+)'
-
- __description__ = """Firedrive.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
-getInfo = create_getInfo(FiredriveCom)
diff --git a/module/plugins/hoster/FlyFilesNet.py b/module/plugins/hoster/FlyFilesNet.py
deleted file mode 100644
index aa2c3993f..000000000
--- a/module/plugins/hoster/FlyFilesNet.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urllib import unquote
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.SimpleHoster import SimpleHoster
-
-
-class FlyFilesNet(SimpleHoster):
- __name__ = "FlyFilesNet"
- __type__ = "hoster"
- __version__ = "0.1"
-
- __pattern__ = r'http://(?:www\.)?flyfiles\.net/.*'
-
- __description__ = """FlyFiles.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = []
-
- SESSION_PATTERN = r'flyfiles\.net/(.*)/.*'
- NAME_PATTERN = r'flyfiles\.net/.*/(.*)'
-
-
- def process(self, pyfile):
- name = re.search(self.NAME_PATTERN, pyfile.url).group(1)
- pyfile.name = unquote_plus(name)
-
- session = re.search(self.SESSION_PATTERN, pyfile.url).group(1)
-
- url = "http://flyfiles.net"
-
- # get download URL
- parsed_url = getURL(url, post={"getDownLink": session}, cookies=True)
- self.logDebug("Parsed URL: %s" % parsed_url)
-
- if parsed_url == '#downlink|' or parsed_url == "#downlink|#":
- self.logWarning(_("Could not get the download URL. Please wait 10 minutes"))
- self.wait(10 * 60, True)
- self.retry()
-
- download_url = parsed_url.replace('#downlink|', '')
-
- self.download(download_url)
diff --git a/module/plugins/hoster/FourSharedCom.py b/module/plugins/hoster/FourSharedCom.py
deleted file mode 100644
index e1fcdab26..000000000
--- a/module/plugins/hoster/FourSharedCom.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class FourSharedCom(SimpleHoster):
- __name__ = "FourSharedCom"
- __type__ = "hoster"
- __version__ = "0.30"
-
- __pattern__ = r'https?://(?:www\.)?4shared(\-china)?\.com/(account/)?(download|get|file|document|photo|video|audio|mp3|office|rar|zip|archive|music)/.+?/.*'
-
- __description__ = """4Shared.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<meta name="title" content="(?P<N>.+?)"'
- SIZE_PATTERN = r'<span title="Size: (?P<S>[\d.,]+) (?P<U>[\w^_]+)">'
- OFFLINE_PATTERN = r'The file link that you requested is not valid\.|This file was deleted.'
-
- NAME_REPLACEMENTS = [(r"&#(\d+).", lambda m: unichr(int(m.group(1))))]
- SIZE_REPLACEMENTS = [(",", "")]
-
- DOWNLOAD_URL_PATTERN = r'name="d3link" value="(.*?)"'
- DOWNLOAD_BUTTON_PATTERN = r'id="btnLink" href="(.*?)"'
- FID_PATTERN = r'name="d3fid" value="(.*?)"'
-
-
- def handleFree(self):
- if not self.account:
- self.fail(_("User not logged in"))
-
- m = re.search(self.DOWNLOAD_BUTTON_PATTERN, self.html)
- if m:
- link = m.group(1)
- else:
- link = re.sub(r'/(download|get|file|document|photo|video|audio)/', r'/get/', self.pyfile.url)
-
- self.html = self.load(link)
-
- m = re.search(self.DOWNLOAD_URL_PATTERN, self.html)
- if m is None:
- self.error(_("Download link"))
- link = m.group(1)
-
- try:
- m = re.search(self.FID_PATTERN, self.html)
- res = self.load('http://www.4shared.com/web/d2/getFreeDownloadLimitInfo?fileId=%s' % m.group(1))
- self.logDebug(res)
- except:
- pass
-
- self.wait(20)
- self.download(link)
-
-
-getInfo = create_getInfo(FourSharedCom)
diff --git a/module/plugins/hoster/FreakshareCom.py b/module/plugins/hoster/FreakshareCom.py
deleted file mode 100644
index 00c31c528..000000000
--- a/module/plugins/hoster/FreakshareCom.py
+++ /dev/null
@@ -1,176 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Hoster import Hoster
-from module.plugins.hoster.UnrestrictLi import secondsToMidnight
-from module.plugins.internal.CaptchaService import ReCaptcha
-
-
-class FreakshareCom(Hoster):
- __name__ = "FreakshareCom"
- __type__ = "hoster"
- __version__ = "0.39"
-
- __pattern__ = r'http://(?:www\.)?freakshare\.(net|com)/files/\S*?/'
-
- __description__ = """Freakshare.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("sitacuisses", "sitacuisses@yahoo.de"),
- ("spoob", "spoob@pyload.org"),
- ("mkaay", "mkaay@mkaay.de"),
- ("Toilal", "toilal.dev@gmail.com")]
-
-
- def setup(self):
- self.multiDL = False
- self.req_opts = []
-
-
- def process(self, pyfile):
- self.pyfile = pyfile
-
- pyfile.url = pyfile.url.replace("freakshare.net/", "freakshare.com/")
-
- if self.account:
- self.html = self.load(pyfile.url, cookies=False)
- pyfile.name = self.get_file_name()
- self.download(pyfile.url)
-
- else:
- self.prepare()
- self.get_file_url()
-
- self.download(pyfile.url, post=self.req_opts)
-
- check = self.checkDownload({"bad": "bad try",
- "paralell": "> Sorry, you cant download more then 1 files at time. <",
- "empty": "Warning: Unknown: Filename cannot be empty",
- "wrong_captcha": "Wrong Captcha!",
- "downloadserver": "No Downloadserver. Please try again later!"})
-
- if check == "bad":
- self.fail(_("Bad Try"))
- elif check == "paralell":
- self.setWait(300, True)
- self.wait()
- self.retry()
- elif check == "empty":
- self.fail(_("File not downloadable"))
- elif check == "wrong_captcha":
- self.invalidCaptcha()
- self.retry()
- elif check == "downloadserver":
- self.retry(5, 15 * 60, _("No Download server"))
-
-
- def prepare(self):
- pyfile = self.pyfile
-
- self.download_html()
-
- if not self.file_exists():
- self.offline()
-
- self.setWait(self.get_waiting_time())
-
- pyfile.name = self.get_file_name()
- pyfile.size = self.get_file_size()
-
- self.wait()
-
- return True
-
-
- def download_html(self):
- self.load("http://freakshare.com/index.php", {"language": "EN"}) # Set english language in server session
- self.html = self.load(self.pyfile.url)
-
-
- def get_file_url(self):
- """ returns the absolute downloadable filepath
- """
- if not self.html:
- self.download_html()
- if not self.wantReconnect:
- self.req_opts = self.get_download_options() # get the Post options for the Request
- #file_url = self.pyfile.url
- #return file_url
- else:
- self.offline()
-
-
- def get_file_name(self):
- if not self.html:
- self.download_html()
- if not self.wantReconnect:
- file_name = re.search(r"<h1\sclass=\"box_heading\"\sstyle=\"text-align:center;\">([^ ]+)", self.html)
- if file_name is not None:
- file_name = file_name.group(1)
- else:
- file_name = self.pyfile.url
- return file_name
- else:
- return self.pyfile.url
-
-
- def get_file_size(self):
- size = 0
- if not self.html:
- self.download_html()
- if not self.wantReconnect:
- file_size_check = re.search(
- r"<h1\sclass=\"box_heading\"\sstyle=\"text-align:center;\">[^ ]+ - ([^ ]+) (\w\w)yte", self.html)
- if file_size_check is not None:
- units = float(file_size_check.group(1).replace(",", ""))
- pow = {'KB': 1, 'MB': 2, 'GB': 3}[file_size_check.group(2)]
- size = int(units * 1024 ** pow)
-
- return size
-
-
- def get_waiting_time(self):
- if not self.html:
- self.download_html()
-
- if "Your Traffic is used up for today" in self.html:
- self.wantReconnect = True
- return secondsToMidnight(gmt=2)
-
- timestring = re.search('\s*var\s(?:downloadWait|time)\s=\s(\d*)[\d.]*;', self.html)
- if timestring:
- return int(timestring.group(1))
- else:
- return 60
-
-
- def file_exists(self):
- """ returns True or False
- """
- if not self.html:
- self.download_html()
- if re.search(r"This file does not exist!", self.html) is not None:
- return False
- else:
- return True
-
-
- def get_download_options(self):
- re_envelope = re.search(r".*?value=\"Free\sDownload\".*?\n*?(.*?<.*?>\n*)*?\n*\s*?</form>",
- self.html).group(0) # get the whole request
- to_sort = re.findall(r"<input\stype=\"hidden\"\svalue=\"(.*?)\"\sname=\"(.*?)\"\s\/>", re_envelope)
- request_options = dict((n, v) for (v, n) in to_sort)
-
- herewego = self.load(self.pyfile.url, None, request_options) # the actual download-Page
-
- to_sort = re.findall(r"<input\stype=\".*?\"\svalue=\"(\S*?)\".*?name=\"(\S*?)\"\s.*?\/>", herewego)
- request_options = dict((n, v) for (v, n) in to_sort)
-
- challenge = re.search(r"http://api\.recaptcha\.net/challenge\?k=(\w+)", herewego)
-
- if challenge:
- re_captcha = ReCaptcha(self)
- (request_options['recaptcha_challenge_field'],
- request_options['recaptcha_response_field']) = re_captcha.challenge(challenge.group(1))
-
- return request_options
diff --git a/module/plugins/hoster/FreeWayMe.py b/module/plugins/hoster/FreeWayMe.py
deleted file mode 100644
index a27dc04b8..000000000
--- a/module/plugins/hoster/FreeWayMe.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Hoster import Hoster
-
-
-class FreeWayMe(Hoster):
- __name__ = "FreeWayMe"
- __type__ = "hoster"
- __version__ = "0.11"
-
- __pattern__ = r'https://(?:www\.)?free-way\.me/.*'
-
- __description__ = """FreeWayMe hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Nicolas Giese", "james@free-way.me")]
-
-
- def setup(self):
- self.resumeDownload = False
- self.multiDL = self.premium
- self.chunkLimit = 1
-
-
- def process(self, pyfile):
- if not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "FreeWayMe")
- self.fail(_("No FreeWay account provided"))
-
- self.logDebug("Old URL: %s" % pyfile.url)
-
- (user, data) = self.account.selectAccount()
-
- self.download(
- "https://www.free-way.me/load.php",
- get={"multiget": 7, "url": pyfile.url, "user": user, "pw": self.account.getpw(user), "json": ""},
- disposition=True)
diff --git a/module/plugins/hoster/FreevideoCz.py b/module/plugins/hoster/FreevideoCz.py
deleted file mode 100644
index 8c0df84b2..000000000
--- a/module/plugins/hoster/FreevideoCz.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class FreevideoCz(DeadHoster):
- __name__ = "FreevideoCz"
- __type__ = "hoster"
- __version__ = "0.3"
-
- __pattern__ = r'http://(?:www\.)?freevideo\.cz/vase-videa/.+'
-
- __description__ = """Freevideo.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(FreevideoCz) \ No newline at end of file
diff --git a/module/plugins/hoster/FshareVn.py b/module/plugins/hoster/FshareVn.py
deleted file mode 100644
index 3c230bbe2..000000000
--- a/module/plugins/hoster/FshareVn.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import strptime, mktime, gmtime
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.SimpleHoster import SimpleHoster, parseFileInfo
-
-
-def getInfo(urls):
- for url in urls:
- html = getURL("http://www.fshare.vn/check_link.php",
- post={'action': "check_link", 'arrlinks': url},
- decode=True)
-
- yield parseFileInfo(FshareVn, url, html)
-
-
-def doubleDecode(m):
- return m.group(1).decode('raw_unicode_escape')
-
-
-class FshareVn(SimpleHoster):
- __name__ = "FshareVn"
- __type__ = "hoster"
- __version__ = "0.17"
-
- __pattern__ = r'http://(?:www\.)?fshare\.vn/file/.*'
-
- __description__ = """FshareVn hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- INFO_PATTERN = r'<p>(?P<N>[^<]+)<\\/p>[\\trn\s]*<p>(?P<S>[\d.,]+)\s*(?P<U>[\w^_]+)<\\/p>'
- OFFLINE_PATTERN = r'<div class=\\"f_left file_w\\"|<\\/p>\\t\\t\\t\\t\\r\\n\\t\\t<p><\\/p>\\t\\t\\r\\n\\t\\t<p>0 KB<\\/p>'
-
- NAME_REPLACEMENTS = [("(.*)", doubleDecode)]
-
- LINK_PATTERN = r'action="(http://download.*?)[#"]'
- WAIT_PATTERN = ur'Lượt tải xuống kế tiếp là:\s*(.*?)\s*<'
-
-
- def process(self, pyfile):
- self.html = self.load('http://www.fshare.vn/check_link.php', post={
- "action": "check_link",
- "arrlinks": pyfile.url
- }, decode=True)
- self.getFileInfo()
-
- if self.premium:
- self.handlePremium()
- else:
- self.handleFree()
- self.checkDownloadedFile()
-
-
- def handleFree(self):
- self.html = self.load(self.pyfile.url, decode=True)
-
- self.checkErrors()
-
- action, inputs = self.parseHtmlForm('frm_download')
- self.url = self.pyfile.url + action
-
- if not inputs:
- self.error(_("No FORM"))
- elif 'link_file_pwd_dl' in inputs:
- for password in self.getPassword().splitlines():
- self.logInfo(_("Password protected link, trying ") + password)
- inputs['link_file_pwd_dl'] = password
- self.html = self.load(self.url, post=inputs, decode=True)
- if not 'name="link_file_pwd_dl"' in self.html:
- break
- else:
- self.fail(_("No or incorrect password"))
- else:
- self.html = self.load(self.url, post=inputs, decode=True)
-
- self.checkErrors()
-
- m = re.search(r'var count = (\d+)', self.html)
- self.setWait(int(m.group(1)) if m else 30)
-
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("LINK_PATTERN not found"))
- self.url = m.group(1)
- self.logDebug("FREE DL URL: %s" % self.url)
-
- self.wait()
- self.download(self.url)
-
-
- def handlePremium(self):
- self.download(self.pyfile.url)
-
-
- def checkErrors(self):
- if '/error.php?' in self.req.lastEffectiveURL or u"Liên kết bạn chọn khÃŽng tồn" in self.html:
- self.offline()
-
- m = re.search(self.WAIT_PATTERN, self.html)
- if m:
- self.logInfo(_("Wait until %s ICT") % m.group(1))
- wait_until = mktime(strptime(m.group(1), "%d/%m/%Y %H:%M"))
- self.wait(wait_until - mktime(gmtime()) - 7 * 60 * 60, True)
- self.retry()
- elif '<ul class="message-error">' in self.html:
- msg = "Unknown error occured or wait time not parsed"
- self.logError(msg)
- self.retry(30, 2 * 60, msg)
-
- self.info.pop('error', None)
-
-
- def checkDownloadedFile(self):
- # check download
- check = self.checkDownload({
- "not_found": "<head><title>404 Not Found</title></head>"
- })
-
- if check == "not_found":
- self.fail(_("File not m on server"))
diff --git a/module/plugins/hoster/Ftp.py b/module/plugins/hoster/Ftp.py
deleted file mode 100644
index 9f79d80b5..000000000
--- a/module/plugins/hoster/Ftp.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pycurl
-import re
-
-from urllib import quote, unquote
-from urlparse import urlparse
-
-from module.plugins.Hoster import Hoster
-
-
-class Ftp(Hoster):
- __name__ = "Ftp"
- __type__ = "hoster"
- __version__ = "0.43"
-
- __pattern__ = r'(?:ftps?|sftp)://([\w.-]+(:[\w.-]+)?@)?[\w.-]+(:\d+)?/.+'
-
- __description__ = """Download from ftp directory"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.com"),
- ("mkaay", "mkaay@mkaay.de"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- def setup(self):
- self.chunkLimit = -1
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- parsed_url = urlparse(pyfile.url)
- netloc = parsed_url.netloc
-
- pyfile.name = parsed_url.path.rpartition('/')[2]
- try:
- pyfile.name = unquote(str(pyfile.name)).decode('utf8')
- except:
- pass
-
- if not "@" in netloc:
- servers = [x['login'] for x in self.account.getAllAccounts()] if self.account else []
-
- if netloc in servers:
- self.logDebug("Logging on to %s" % netloc)
- self.req.addAuth(self.account.accounts[netloc]['password'])
- else:
- for pwd in self.getPassword().splitlines():
- if ":" in pwd:
- self.req.addAuth(pwd.strip())
- break
-
- self.req.http.c.setopt(pycurl.NOBODY, 1)
-
- try:
- res = self.load(pyfile.url)
- except pycurl.error, e:
- self.fail(_("Error %d: %s") % e.args)
-
- self.req.http.c.setopt(pycurl.NOBODY, 0)
- self.logDebug(self.req.http.header)
-
- m = re.search(r"Content-Length:\s*(\d+)", res)
- if m:
- pyfile.size = int(m.group(1))
- self.download(pyfile.url)
- else:
- #Naive ftp directory listing
- if re.search(r'^25\d.*?"', self.req.http.header, re.M):
- pyfile.url = pyfile.url.rstrip('/')
- pkgname = "/".join(pyfile.package().name, urlparse(pyfile.url).path.rpartition('/')[2])
- pyfile.url += '/'
- self.req.http.c.setopt(48, 1) # CURLOPT_DIRLISTONLY
- res = self.load(pyfile.url, decode=False)
- links = [pyfile.url + quote(x) for x in res.splitlines()]
- self.logDebug("LINKS", links)
- self.core.api.addPackage(pkgname, links)
- else:
- self.fail(_("Unexpected server response"))
diff --git a/module/plugins/hoster/GamefrontCom.py b/module/plugins/hoster/GamefrontCom.py
deleted file mode 100644
index c68866f87..000000000
--- a/module/plugins/hoster/GamefrontCom.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
-from module.utils import parseFileSize
-
-
-class GamefrontCom(Hoster):
- __name__ = "GamefrontCom"
- __type__ = "hoster"
- __version__ = "0.04"
-
- __pattern__ = r'http://(?:www\.)?gamefront\.com/files/\w+'
-
- __description__ = """Gamefront.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("fwannmacher", "felipe@warhammerproject.com")]
-
-
- PATTERN_FILENAME = r'<title>(.*?) | Game Front'
- PATTERN_FILESIZE = r'<dt>File Size:</dt>[\n\s]*<dd>(.*?)</dd>'
- PATTERN_OFFLINE = r'This file doesn\'t exist, or has been removed.'
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
- self.chunkLimit = -1
-
-
- def process(self, pyfile):
- self.pyfile = pyfile
- self.html = self.load(pyfile.url, decode=True)
-
- if not self._checkOnline():
- self.offline()
-
- pyfile.name = self._getName()
-
- link = self._getLink()
-
- if not link.startswith('http://'):
- link = "http://www.gamefront.com/" + link
-
- self.download(link)
-
-
- def _checkOnline(self):
- if re.search(self.PATTERN_OFFLINE, self.html):
- return False
- else:
- return True
-
-
- def _getName(self):
- name = re.search(self.PATTERN_FILENAME, self.html)
- if name is None:
- self.fail(_("Plugin broken")
-
- return name.group(1)
-
-
- def _getLink(self):
- self.html2 = self.load("http://www.gamefront.com/" + re.search("(files/service/thankyou\\?id=\w+)",
- self.html).group(1))
- return re.search("<a href=\"(http://media\d+\.gamefront.com/.*)\">click here</a>", self.html2).group(1).replace("&amp;", "&")
-
-
-def getInfo(urls):
- result = []
-
- for url in urls:
- html = getURL(url)
-
- if re.search(GamefrontCom.PATTERN_OFFLINE, html):
- result.append((url, 0, 1, url))
- else:
- name = re.search(GamefrontCom.PATTERN_FILENAME, html)
- if name is None:
- result.append((url, 0, 1, url))
- else:
- name = name.group(1)
- size = re.search(GamefrontCom.PATTERN_FILESIZE, html)
- size = parseFileSize(size.group(1))
-
- result.append((name, size, 3, url))
-
- yield result
diff --git a/module/plugins/hoster/GigapetaCom.py b/module/plugins/hoster/GigapetaCom.py
deleted file mode 100644
index 37af7f216..000000000
--- a/module/plugins/hoster/GigapetaCom.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from pycurl import FOLLOWLOCATION
-from random import randint
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class GigapetaCom(SimpleHoster):
- __name__ = "GigapetaCom"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?gigapeta\.com/dl/\w+'
-
- __description__ = """GigaPeta.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<img src=".*" alt="file" />-->\s*(?P<N>.*?)\s*</td>'
- SIZE_PATTERN = r'<th>\s*Size\s*</th>\s*<td>\s*(?P<S>.*?)\s*</td>'
- OFFLINE_PATTERN = r'<div id="page_error">'
-
- COOKIES = [("gigapeta.com", "lang", "us")]
-
-
- def handleFree(self):
- captcha_key = str(randint(1, 100000000))
- captcha_url = "http://gigapeta.com/img/captcha.gif?x=%s" % captcha_key
-
- self.req.http.c.setopt(FOLLOWLOCATION, 0)
-
- for _i in xrange(5):
- self.checkErrors()
-
- captcha = self.decryptCaptcha(captcha_url)
- self.html = self.load(self.pyfile.url, post={
- "captcha_key": captcha_key,
- "captcha": captcha,
- "download": "Download"})
-
- m = re.search(r'Location\s*:\s*(.+)', self.req.http.header, re.I)
- if m:
- download_url = m.group(1).rstrip() #@TODO: Remove .rstrip() in 0.4.10
- break
- elif "Entered figures don&#96;t coincide with the picture" in self.html:
- self.invalidCaptcha()
- else:
- self.fail(_("No valid captcha code entered"))
-
- self.req.http.c.setopt(FOLLOWLOCATION, 1)
- self.download(download_url)
-
-
- def checkErrors(self):
- if "All threads for IP" in self.html:
- self.logDebug("Your IP is already downloading a file")
- self.wait(5 * 60, True)
- self.retry()
-
- self.info.pop('error', None)
-
-
-getInfo = create_getInfo(GigapetaCom)
diff --git a/module/plugins/hoster/GooIm.py b/module/plugins/hoster/GooIm.py
deleted file mode 100644
index 436d825a9..000000000
--- a/module/plugins/hoster/GooIm.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# https://goo.im/devs/liquidsmooth/3.x/codina/Nightly/LS-KK-v3.2-2014-08-01-codina.zip
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class GooIm(SimpleHoster):
- __name__ = "GooIm"
- __type__ = "hoster"
- __version__ = "0.03"
-
- __pattern__ = r'https?://(?:www\.)?goo\.im/.+'
-
- __description__ = """Goo.im hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zapp-brannigan", "fuerst.reinje@web.de")]
-
-
- NAME_PATTERN = r'You will be redirected to .*(?P<N>[^/ ]+) in'
- OFFLINE_PATTERN = r'The file you requested was not found'
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
-
-
- def handleFree(self):
- url = self.pyfile.url
- self.html = self.load(url, cookies=True)
- self.wait(10)
- self.download(url, cookies=True)
-
-
-getInfo = create_getInfo(GooIm)
diff --git a/module/plugins/hoster/HellshareCz.py b/module/plugins/hoster/HellshareCz.py
deleted file mode 100644
index 117749f89..000000000
--- a/module/plugins/hoster/HellshareCz.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class HellshareCz(SimpleHoster):
- __name__ = "HellshareCz"
- __type__ = "hoster"
- __version__ = "0.83"
-
- __pattern__ = r'(http://(?:www\.)?hellshare\.(?:cz|com|sk|hu|pl)/[^?]*/\d+).*'
-
- __description__ = """Hellshare.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<h1 id="filename"[^>]*>(?P<N>[^<]+)</h1>'
- SIZE_PATTERN = r'<strong id="FileSize_master">(?P<S>[\d.,]+)&nbsp;(?P<U>[\w^_]+)</strong>'
- OFFLINE_PATTERN = r'<h1>File not found.</h1>'
- SHOW_WINDOW_PATTERN = r'<a href="([^?]+/(\d+)/\?do=(fileDownloadButton|relatedFileDownloadButton-\2)-showDownloadWindow)"'
-
-
- def setup(self):
- self.resumeDownload = self.multiDL = True if self.account else False
- self.chunkLimit = 1
-
-
- def process(self, pyfile):
- if not self.account:
- self.fail(_("User not logged in"))
- pyfile.url = re.match(self.__pattern__, pyfile.url).group(1)
- self.html = self.load(pyfile.url, decode=True)
- self.getFileInfo()
- if not self.checkTrafficLeft():
- self.fail(_("Not enough traffic left for user ") + self.user)
-
- m = re.search(self.SHOW_WINDOW_PATTERN, self.html)
- if m is None:
- self.error(_("SHOW_WINDOW_PATTERN not found"))
-
- self.url = "http://www.hellshare.com" + m.group(1)
- self.download(self.url)
-
-
-getInfo = create_getInfo(HellshareCz)
diff --git a/module/plugins/hoster/HellspyCz.py b/module/plugins/hoster/HellspyCz.py
deleted file mode 100644
index a7ca19406..000000000
--- a/module/plugins/hoster/HellspyCz.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class HellspyCz(DeadHoster):
- __name__ = "HellspyCz"
- __type__ = "hoster"
- __version__ = "0.28"
-
- __pattern__ = r'http://(?:www\.)?(?:hellspy\.(?:cz|com|sk|hu|pl)|sciagaj\.pl)(/\S+/\d+)/?.*'
-
- __description__ = """HellSpy.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(HellspyCz)
diff --git a/module/plugins/hoster/HotfileCom.py b/module/plugins/hoster/HotfileCom.py
deleted file mode 100644
index f7724faf2..000000000
--- a/module/plugins/hoster/HotfileCom.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class HotfileCom(DeadHoster):
- __name__ = "HotfileCom"
- __type__ = "hoster"
- __version__ = "0.37"
-
- __pattern__ = r'https?://(?:www\.)?hotfile\.com/dl/\d+/\w+'
-
- __description__ = """Hotfile.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("sitacuisses", "sitacuisses@yhoo.de"),
- ("spoob", "spoob@pyload.org"),
- ("mkaay", "mkaay@mkaay.de"),
- ("JoKoT3", "jokot3@gmail.com")]
-
-
-getInfo = create_getInfo(HotfileCom)
diff --git a/module/plugins/hoster/HugefilesNet.py b/module/plugins/hoster/HugefilesNet.py
deleted file mode 100644
index f7221f8c5..000000000
--- a/module/plugins/hoster/HugefilesNet.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class HugefilesNet(XFSHoster):
- __name__ = "HugefilesNet"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'http://(?:www\.)?hugefiles\.net/\w{12}'
-
- __description__ = """Hugefiles.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- HOSTER_DOMAIN = "hugefiles.net"
-
- SIZE_PATTERN = r'File Size:</span>\s*<span[^>]*>(?P<S>[^<]+)</span></div>'
-
- FORM_INPUTS_MAP = {'ctype': re.compile(r'\d+')}
-
-
-getInfo = create_getInfo(HugefilesNet)
diff --git a/module/plugins/hoster/HundredEightyUploadCom.py b/module/plugins/hoster/HundredEightyUploadCom.py
deleted file mode 100644
index 48e0c3d8c..000000000
--- a/module/plugins/hoster/HundredEightyUploadCom.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://180upload.com/js9qdm6kjnrs
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class HundredEightyUploadCom(XFSHoster):
- __name__ = "HundredEightyUploadCom"
- __type__ = "hoster"
- __version__ = "0.04"
-
- __pattern__ = r'http://(?:www\.)?180upload\.com/\w{12}'
-
- __description__ = """180upload.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- HOSTER_DOMAIN = "180upload.com"
-
- NAME_PATTERN = r'Filename:</b></td><td nowrap>(?P<N>.+)</td></tr>-->'
- SIZE_PATTERN = r'Size:</b></td><td>(?P<S>[\d.,]+) (?P<U>[\w^_]+)\s*<small>'
-
-
-getInfo = create_getInfo(HundredEightyUploadCom)
diff --git a/module/plugins/hoster/IFileWs.py b/module/plugins/hoster/IFileWs.py
deleted file mode 100644
index b4f225c4b..000000000
--- a/module/plugins/hoster/IFileWs.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class IFileWs(DeadHoster):
- __name__ = "IFileWs"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?ifile\.ws/\w{12}'
-
- __description__ = """Ifile.ws hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("z00nx", "z00nx0@gmail.com")]
-
-
-getInfo = create_getInfo(IFileWs)
diff --git a/module/plugins/hoster/IcyFilesCom.py b/module/plugins/hoster/IcyFilesCom.py
deleted file mode 100644
index d8a28ef72..000000000
--- a/module/plugins/hoster/IcyFilesCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class IcyFilesCom(DeadHoster):
- __name__ = "IcyFilesCom"
- __type__ = "hoster"
- __version__ = "0.06"
-
- __pattern__ = r'http://(?:www\.)?icyfiles\.com/(.*)'
-
- __description__ = """IcyFiles.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("godofdream", "soilfiction@gmail.com")]
-
-
-getInfo = create_getInfo(IcyFilesCom)
diff --git a/module/plugins/hoster/IfileIt.py b/module/plugins/hoster/IfileIt.py
deleted file mode 100644
index b7e37457d..000000000
--- a/module/plugins/hoster/IfileIt.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.common.json_layer import json_loads
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class IfileIt(SimpleHoster):
- __name__ = "IfileIt"
- __type__ = "hoster"
- __version__ = "0.28"
-
- __pattern__ = r'^unmatchable$'
-
- __description__ = """Ifile.it"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- LINK_PATTERN = r'</span> If it doesn\'t, <a target="_blank" href="([^"]+)">'
- RECAPTCHA_PATTERN = r'var __recaptcha_public\s*=\s*\'(.+?)\''
- INFO_PATTERN = r'<span style="cursor: default;[^>]*>\s*(?P<N>.*?)\s*&nbsp;\s*<strong>\s*(?P<S>[\d.,]+)\s*(?P<U>[\w^_]+)\s*</strong>\s*</span>'
- OFFLINE_PATTERN = r'<span style="cursor: default;[^>]*>\s*&nbsp;\s*<strong>\s*</strong>\s*</span>'
- TEMP_OFFLINE_PATTERN = r'<span class="msg_red">Downloading of this file is temporarily disabled</span>'
-
-
- def handleFree(self):
- ukey = re.match(self.__pattern__, self.pyfile.url).group(1)
- json_url = 'http://ifile.it/new_download-request.json'
- post_data = {"ukey": ukey, "ab": "0"}
-
- json_response = json_loads(self.load(json_url, post=post_data))
- self.logDebug(json_response)
- if json_response['status'] == 3:
- self.offline()
-
- if json_response['captcha']:
- captcha_key = re.search(self.RECAPTCHA_PATTERN, self.html).group(1)
-
- recaptcha = ReCaptcha(self)
- post_data['ctype'] = "recaptcha"
-
- for _i in xrange(5):
- post_data['recaptcha_challenge'], post_data['recaptcha_response'] = recaptcha.challenge(captcha_key)
- json_response = json_loads(self.load(json_url, post=post_data))
- self.logDebug(json_response)
-
- if json_response['retry']:
- self.invalidCaptcha()
- else:
- self.correctCaptcha()
- break
- else:
- self.fail(_("Incorrect captcha"))
-
- if not "ticket_url" in json_response:
- self.error(_("No download URL"))
-
- self.download(json_response['ticket_url'])
-
-
-getInfo = create_getInfo(IfileIt)
diff --git a/module/plugins/hoster/IfolderRu.py b/module/plugins/hoster/IfolderRu.py
deleted file mode 100644
index ab3097854..000000000
--- a/module/plugins/hoster/IfolderRu.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class IfolderRu(SimpleHoster):
- __name__ = "IfolderRu"
- __type__ = "hoster"
- __version__ = "0.38"
-
- __pattern__ = r'http://(?:www\.)?(?:ifolder\.ru|rusfolder\.(?:com|net|ru))/(?:files/)?(?P<ID>\d+).*'
-
- __description__ = """Ifolder.ru hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- SIZE_REPLACEMENTS = [(u'Кб', 'KB'), (u'Мб', 'MB'), (u'Гб', 'GB')]
- NAME_PATTERN = ur'(?:<div><span>)?НазваМОе:(?:</span>)? <b>(?P<N>[^<]+)</b><(?:/div|br)>'
- SIZE_PATTERN = ur'(?:<div><span>)?РазЌер:(?:</span>)? <b>(?P<S>[^<]+)</b><(?:/div|br)>'
- OFFLINE_PATTERN = ur'<p>Ѐайл МПЌер <b>[^<]*</b> (Ме МайЎеМ|уЎалеМ) !!!</p>'
-
- SESSION_ID_PATTERN = r'<a href=(http://ints\.(?:rusfolder\.com|ifolder\.ru)/ints/sponsor/\?bi=\d*&session=([^&]+)&u=[^>]+)>'
- INTS_SESSION_PATTERN = r'\(\'ints_session\'\);\s*if\(tag\)\{tag\.value = "([^"]+)";\}'
- HIDDEN_INPUT_PATTERN = r'var v = .*?name=\'(.+?)\' value=\'1\''
- LINK_PATTERN = r'<a id="download_file_href" href="([^"]+)"'
- WRONG_CAPTCHA_PATTERN = ur'<font color=Red>МеверМый кПЎ,<br>ввеЎОте еще раз</font><br>'
-
-
- def setup(self):
- self.resumeDownload = self.multiDL = True if self.account else False
- self.chunkLimit = 1
-
-
- def process(self, pyfile):
- file_id = re.match(self.__pattern__, pyfile.url).group('ID')
- self.html = self.load("http://rusfolder.com/%s" % file_id, cookies=True, decode=True)
- self.getFileInfo()
-
- url = re.search(r"location\.href = '(http://ints\..*?=)'", self.html).group(1)
- self.html = self.load(url, cookies=True, decode=True)
-
- url, session_id = re.search(self.SESSION_ID_PATTERN, self.html).groups()
- self.html = self.load(url, cookies=True, decode=True)
-
- url = "http://ints.rusfolder.com/ints/frame/?session=%s" % session_id
- self.html = self.load(url, cookies=True)
-
- self.wait(31, False)
-
- captcha_url = "http://ints.rusfolder.com/random/images/?session=%s" % session_id
- for _i in xrange(5):
- self.html = self.load(url, cookies=True)
- action, inputs = self.parseHtmlForm('ID="Form1"')
- inputs['ints_session'] = re.search(self.INTS_SESSION_PATTERN, self.html).group(1)
- inputs[re.search(self.HIDDEN_INPUT_PATTERN, self.html).group(1)] = '1'
- inputs['confirmed_number'] = self.decryptCaptcha(captcha_url, cookies=True)
- inputs['action'] = '1'
- self.logDebug(inputs)
-
- self.html = self.load(url, decode=True, cookies=True, post=inputs)
- if self.WRONG_CAPTCHA_PATTERN in self.html:
- self.invalidCaptcha()
- else:
- break
- else:
- self.fail(_("Invalid captcha"))
-
- download_url = re.search(self.LINK_PATTERN, self.html).group(1)
- self.correctCaptcha()
- self.download(download_url)
-
-
-getInfo = create_getInfo(IfolderRu)
diff --git a/module/plugins/hoster/JumbofilesCom.py b/module/plugins/hoster/JumbofilesCom.py
deleted file mode 100644
index 2df639ac1..000000000
--- a/module/plugins/hoster/JumbofilesCom.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class JumbofilesCom(SimpleHoster):
- __name__ = "JumbofilesCom"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?jumbofiles\.com/(\w{12}).*'
-
- __description__ = """JumboFiles.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("godofdream", "soilfiction@gmail.com")]
-
-
- INFO_PATTERN = r'<TR><TD>(?P<N>[^<]+?)\s*<small>\((?P<S>[\d.,]+)\s*(?P<U>[\w^_]+)'
- OFFLINE_PATTERN = r'Not Found or Deleted / Disabled due to inactivity or DMCA'
- LINK_PATTERN = r'<meta http-equiv="refresh" content="10;url=(.+)">'
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
-
-
- def handleFree(self):
- ukey = re.match(self.__pattern__, self.pyfile.url).group(1)
- post_data = {"id": ukey, "op": "download3", "rand": ""}
- html = self.load(self.pyfile.url, post=post_data, decode=True)
- url = re.search(self.LINK_PATTERN, html).group(1)
- self.download(url)
-
-
-getInfo = create_getInfo(JumbofilesCom)
diff --git a/module/plugins/hoster/JunocloudMe.py b/module/plugins/hoster/JunocloudMe.py
deleted file mode 100644
index ffea77315..000000000
--- a/module/plugins/hoster/JunocloudMe.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class JunocloudMe(XFSHoster):
- __name__ = "JunocloudMe"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'http://(?:\w+\.)?junocloud\.me/\w{12}'
-
- __description__ = """Junocloud.me hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("guidobelix", "guidobelix@hotmail.it")]
-
-
- HOSTER_DOMAIN = "junocloud.me"
-
- URL_REPLACEMENTS = [(r'//(www\.)?junocloud', "//dl3.junocloud")]
-
- SIZE_PATTERN = r'<p class="request_filesize">Size: (?P<S>[\d.,]+) (?P<U>[\w^_]+)</p>'
-
- OFFLINE_PATTERN = r'>No such file with this filename<'
- TEMP_OFFLINE_PATTERN = r'The page may have been renamed, removed or be temporarily unavailable.<'
-
-
-getInfo = create_getInfo(JunocloudMe)
diff --git a/module/plugins/hoster/Keep2shareCc.py b/module/plugins/hoster/Keep2shareCc.py
deleted file mode 100644
index cb5e65a29..000000000
--- a/module/plugins/hoster/Keep2shareCc.py
+++ /dev/null
@@ -1,135 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urljoin, urlparse
-
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import _isDirectLink, SimpleHoster, create_getInfo
-
-
-class Keep2shareCc(SimpleHoster):
- __name__ = "Keep2shareCc"
- __type__ = "hoster"
- __version__ = "0.16"
-
- __pattern__ = r'https?://(?:www\.)?(keep2share|k2s|keep2s)\.cc/file/(?P<ID>\w+)'
-
- __description__ = """Keep2share.cc hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- URL_REPLACEMENTS = [(__pattern__ + ".*", "http://k2s.cc/file/\g<ID>")]
-
- CONTENT_DISPOSITION = True
-
- NAME_PATTERN = r'File: <span>(?P<N>.+)</span>'
- SIZE_PATTERN = r'Size: (?P<S>[^<]+)</div>'
-
- OFFLINE_PATTERN = r'File not found or deleted|Sorry, this file is blocked or deleted|Error 404'
- TEMP_OFFLINE_PATTERN = r'Downloading blocked due to'
-
- LINK_FREE_PATTERN = LINK_PREMIUM_PATTERN = r'"([^"]+url.html?file=.+?)"|window\.location\.href = \'(.+?)\';'
-
- CAPTCHA_PATTERN = r'src="(/file/captcha\.html.+?)"'
-
- WAIT_PATTERN = r'Please wait ([\d:]+) to download this file'
- TEMP_ERROR_PATTERN = r'>\s*(Download count files exceed|Traffic limit exceed|Free account does not allow to download more than one file at the same time)'
- ERROR_PATTERN = r'>\s*(Free user can\'t download large files|You no can access to this file|This download available only for premium users|This is private file)'
-
-
- def checkErrors(self):
- m = re.search(self.TEMP_ERROR_PATTERN, self.html)
- if m:
- self.info['error'] = m.group(1)
- self.wantReconnect = True
- self.retry(wait_time=30 * 60, reason=m.group(0))
-
- m = re.search(self.ERROR_PATTERN, self.html)
- if m:
- errmsg = self.info['error'] = m.group(1)
- self.error(errmsg)
-
- m = re.search(self.WAIT_PATTERN, self.html)
- if m:
- self.logDebug("Hoster told us to wait for %s" % m.group(1))
-
- # string to time convert courtesy of https://stackoverflow.com/questions/10663720
- ftr = [3600, 60, 1]
- wait_time = sum([a * b for a, b in zip(ftr, map(int, m.group(1).split(':')))])
-
- self.wantReconnect = True
- self.retry(wait_time=wait_time, reason="Please wait to download this file")
-
- self.info.pop('error', None)
-
-
- def handleFree(self):
- self.fid = re.search(r'<input type="hidden" name="slow_id" value="([^"]+)">', self.html).group(1)
- self.html = self.load(self.pyfile.url, post={'yt0': '', 'slow_id': self.fid})
-
- self.checkErrors()
-
- m = re.search(self.LINK_FREE_PATTERN, self.html)
-
- if m is None:
- self.handleCaptcha()
-
- self.wait(30)
-
- self.html = self.load(self.pyfile.url, post={'uniqueId': self.fid, 'free': 1})
-
- self.checkErrors()
-
- m = re.search(self.LINK_FREE_PATTERN, self.html)
- if m is None:
- self.error(_("LINK_FREE_PATTERN not found"))
-
- self.link = self._getDownloadLink(m.group(1))
-
-
- def handlePremium(self):
- super(Keep2shareCc, self).handlePremium()
- if self.link:
- self.link = self._getDownloadLink(self.link)
-
-
- def handleCaptcha(self):
- recaptcha = ReCaptcha(self)
-
- for _i in xrange(5):
- post_data = {'free' : 1,
- 'freeDownloadRequest': 1,
- 'uniqueId' : self.fid,
- 'yt0' : ''}
-
- m = re.search(self.CAPTCHA_PATTERN, self.html)
- if m:
- captcha_url = urljoin(self.base, m.group(1))
- post_data['CaptchaForm[code]'] = self.decryptCaptcha(captcha_url)
- else:
- challenge, response = recaptcha.challenge()
- post_data.update({'recaptcha_challenge_field': challenge,
- 'recaptcha_response_field' : response})
-
- self.html = self.load(self.pyfile.url, post=post_data)
-
- if 'recaptcha' not in self.html:
- self.correctCaptcha()
- break
- else:
- self.invalidCaptcha()
- else:
- self.fail(_("All captcha attempts failed"))
-
-
- def _getDownloadLink(self, url):
- p = urlparse(self.pyfile.url)
- base = "%s://%s" % (p.scheme, p.netloc)
- link = _isDirectLink(self, url, self.premium)
- return urljoin(base, link) if link else ""
-
-
-getInfo = create_getInfo(Keep2shareCc)
diff --git a/module/plugins/hoster/KickloadCom.py b/module/plugins/hoster/KickloadCom.py
deleted file mode 100644
index 1c39db46c..000000000
--- a/module/plugins/hoster/KickloadCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class KickloadCom(DeadHoster):
- __name__ = "KickloadCom"
- __type__ = "hoster"
- __version__ = "0.21"
-
- __pattern__ = r'http://(?:www\.)?kickload\.com/get/.+'
-
- __description__ = """Kickload.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("mkaay", "mkaay@mkaay.de")]
-
-
-getInfo = create_getInfo(KickloadCom)
diff --git a/module/plugins/hoster/KingfilesNet.py b/module/plugins/hoster/KingfilesNet.py
deleted file mode 100644
index c84e3bb0f..000000000
--- a/module/plugins/hoster/KingfilesNet.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.CaptchaService import SolveMedia
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class KingfilesNet(SimpleHoster):
- __name__ = "KingfilesNet"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'http://(?:www\.)?kingfiles\.net/(?P<ID>\w{12})'
-
- __description__ = """Kingfiles.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zapp-brannigan", "fuerst.reinje@web.de"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- NAME_PATTERN = r'name="fname" value="(?P<N>.+?)">'
- SIZE_PATTERN = r'>Size: .+?">(?P<S>[\d.,]+) (?P<U>[\w^_]+)'
-
- OFFLINE_PATTERN = r'>(File Not Found</b><br><br>|File Not Found</h2>)'
-
- RAND_ID_PATTERN = r'type=\"hidden\" name=\"rand\" value=\"(.+)\">'
-
- LINK_PATTERN = r'var download_url = \'(.+)\';'
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
-
-
- def handleFree(self):
- # Click the free user button
- post_data = {'op': "download1",
- 'usr_login': "",
- 'id': self.info['pattern']['ID'],
- 'fname': self.pyfile.name,
- 'referer': "",
- 'method_free': "+"}
-
- self.html = self.load(self.pyfile.url, post=post_data, cookies=True, decode=True)
-
- solvemedia = SolveMedia(self)
- captcha_challenge, captcha_response = solvemedia.challenge()
-
- # Make the downloadlink appear and load the file
- m = re.search(self.RAND_ID_PATTERN, self.html)
- if m is None:
- self.error(_("Random key not found"))
-
- rand = m.group(1)
- self.logDebug("rand = ", rand)
-
- post_data = {'op': "download2",
- 'id': self.info['pattern']['ID'],
- 'rand': rand,
- 'referer': self.pyfile.url,
- 'method_free': "+",
- 'method_premium': "",
- 'adcopy_response': captcha_response,
- 'adcopy_challenge': captcha_challenge,
- 'down_direct': "1"}
-
- self.html = self.load(self.pyfile.url, post=post_data, cookies=True, decode=True)
-
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("Download url not found"))
-
- self.download(m.group(1), cookies=True, disposition=True)
-
- check = self.checkDownload({'html': re.compile("<html>")})
- if check == "html":
- self.error(_("Downloaded file is an html page"))
-
-
-getInfo = create_getInfo(KingfilesNet)
diff --git a/module/plugins/hoster/LemUploadsCom.py b/module/plugins/hoster/LemUploadsCom.py
deleted file mode 100644
index 22fbd60dd..000000000
--- a/module/plugins/hoster/LemUploadsCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class LemUploadsCom(DeadHoster):
- __name__ = "LemUploadsCom"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'https?://(?:www\.)?lemuploads\.com/\w{12}'
-
- __description__ = """LemUploads.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")]
-
-
-getInfo = create_getInfo(LemUploadsCom)
diff --git a/module/plugins/hoster/LetitbitNet.py b/module/plugins/hoster/LetitbitNet.py
deleted file mode 100644
index ed8d4a39d..000000000
--- a/module/plugins/hoster/LetitbitNet.py
+++ /dev/null
@@ -1,142 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# API Documentation:
-# http://api.letitbit.net/reg/static/api.pdf
-#
-# Test links:
-# http://letitbit.net/download/07874.0b5709a7d3beee2408bb1f2eefce/random.bin.html
-
-import re
-
-from urllib import urlencode, urlopen
-from urlparse import urljoin
-
-from module.common.json_layer import json_loads, json_dumps
-from module.plugins.hoster.UnrestrictLi import secondsToMidnight
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster
-
-
-def api_download_info(url):
- json_data = ["yw7XQy2v9", ["download/info", {"link": url}]]
- post_data = urlencode({'r': json_dumps(json_data)})
- api_rep = urlopen("http://api.letitbit.net/json", data=post_data).read()
- return json_loads(api_rep)
-
-
-def getInfo(urls):
- for url in urls:
- api_rep = api_download_info(url)
- if api_rep['status'] == 'OK':
- info = api_rep['data'][0]
- yield (info['name'], info['size'], 2, url)
- else:
- yield (url, 0, 1, url)
-
-
-class LetitbitNet(SimpleHoster):
- __name__ = "LetitbitNet"
- __type__ = "hoster"
- __version__ = "0.26"
-
- __pattern__ = r'https?://(?:www\.)?(letitbit|shareflare)\.net/download/.*'
-
- __description__ = """Letitbit.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("z00nx", "z00nx0@gmail.com")]
-
-
- URL_REPLACEMENTS = [(r"(?<=http://)([^/]+)", "letitbit.net")]
-
- SECONDS_PATTERN = r'seconds\s*=\s*(\d+);'
- CAPTCHA_CONTROL_FIELD = r'recaptcha_control_field\s=\s\'(?P<value>.+?)\''
-
-
- def setup(self):
- self.resumeDownload = True
-
-
- def getFileInfo(self):
- api_rep = api_download_info(self.pyfile.url)
- if api_rep['status'] == 'OK':
- self.api_data = api_rep['data'][0]
- self.pyfile.name = self.api_data['name']
- self.pyfile.size = self.api_data['size']
- else:
- self.offline()
-
-
- def handleFree(self):
- action, inputs = self.parseHtmlForm('id="ifree_form"')
- if not action:
- self.error(_("ifree_form"))
-
- self.pyfile.size = float(inputs['sssize'])
- self.logDebug(action, inputs)
- inputs['desc'] = ""
-
- self.html = self.load(urljoin("http://letitbit.net/", action), post=inputs, cookies=True)
-
- m = re.search(self.SECONDS_PATTERN, self.html)
- seconds = int(m.group(1)) if m else 60
- self.logDebug("Seconds found", seconds)
- m = re.search(self.CAPTCHA_CONTROL_FIELD, self.html)
- recaptcha_control_field = m.group(1)
- self.logDebug("ReCaptcha control field found", recaptcha_control_field)
- self.wait(seconds)
-
- res = self.load("http://letitbit.net/ajax/download3.php", post=" ", cookies=True)
- if res != '1':
- self.error(_("Unknown response - ajax_check_url"))
- self.logDebug(res)
-
- recaptcha = ReCaptcha(self)
- challenge, response = recaptcha.challenge()
-
- post_data = {"recaptcha_challenge_field": challenge,
- "recaptcha_response_field": response,
- "recaptcha_control_field": recaptcha_control_field}
- self.logDebug("Post data to send", post_data)
- res = self.load("http://letitbit.net/ajax/check_recaptcha.php", post=post_data, cookies=True)
- self.logDebug(res)
- if not res:
- self.invalidCaptcha()
- if res == "error_free_download_blocked":
- self.logWarning(_("Daily limit reached"))
- self.wait(secondsToMidnight(gmt=2), True)
- if res == "error_wrong_captcha":
- self.invalidCaptcha()
- self.retry()
- elif res.startswith('['):
- urls = json_loads(res)
- elif res.startswith('http://'):
- urls = [res]
- else:
- self.error(_("Unknown response - captcha check"))
-
- self.correctCaptcha()
-
- for download_url in urls:
- try:
- self.download(download_url)
- break
- except Exception, e:
- self.logError(e)
- else:
- self.fail(_("Download did not finish correctly"))
-
-
- def handlePremium(self):
- api_key = self.user
- premium_key = self.account.getAccountData(self.user)['password']
-
- json_data = [api_key, ["download/direct_links", {"pass": premium_key, "link": self.pyfile.url}]]
- api_rep = self.load('http://api.letitbit.net/json', post={'r': json_dumps(json_data)})
- self.logDebug("API Data: " + api_rep)
- api_rep = json_loads(api_rep)
-
- if api_rep['status'] == 'FAIL':
- self.fail(api_rep['data'])
-
- self.download(api_rep['data'][0][0], disposition=True)
diff --git a/module/plugins/hoster/LinksnappyCom.py b/module/plugins/hoster/LinksnappyCom.py
deleted file mode 100644
index b8694e141..000000000
--- a/module/plugins/hoster/LinksnappyCom.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urlsplit
-
-from module.common.json_layer import json_loads, json_dumps
-from module.plugins.Hoster import Hoster
-
-
-class LinksnappyCom(Hoster):
- __name__ = "LinksnappyCom"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'https?://(?:[^/]*\.)?linksnappy\.com'
-
- __description__ = """Linksnappy.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- SINGLE_CHUNK_HOSTERS = ('easybytez.com')
-
-
- def setup(self):
- self.chunkLimit = -1
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- if re.match(self.__pattern__, pyfile.url):
- new_url = pyfile.url
- elif not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "Linksnappy.com")
- self.fail(_("No Linksnappy.com account provided"))
- else:
- self.logDebug("Old URL: %s" % pyfile.url)
- host = self._get_host(pyfile.url)
- json_params = json_dumps({'link': pyfile.url,
- 'type': host,
- 'username': self.user,
- 'password': self.account.getAccountData(self.user)['password']})
- r = self.load('http://gen.linksnappy.com/genAPI.php',
- post={'genLinks': json_params})
- self.logDebug("JSON data: " + r)
-
- j = json_loads(r)['links'][0]
-
- if j['error']:
- msg = _("Error converting the link")
- self.logError(msg, j['error'])
- self.fail(msg)
-
- pyfile.name = j['filename']
- new_url = j['generated']
-
- if host in self.SINGLE_CHUNK_HOSTERS:
- self.chunkLimit = 1
- else:
- self.setup()
-
- if new_url != pyfile.url:
- self.logDebug("New URL: " + new_url)
-
- self.download(new_url, disposition=True)
-
- check = self.checkDownload({"html302": "<title>302 Found</title>"})
- if check == "html302":
- self.retry(wait_time=5, reason=_("Linksnappy returns only HTML data"))
-
-
- @staticmethod
- def _get_host(url):
- host = urlsplit(url).netloc
- return re.search(r'[\w-]+\.\w+$', host).group(0)
diff --git a/module/plugins/hoster/LoadTo.py b/module/plugins/hoster/LoadTo.py
deleted file mode 100644
index 4e55496c6..000000000
--- a/module/plugins/hoster/LoadTo.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://www.load.to/JWydcofUY6/random.bin
-# http://www.load.to/oeSmrfkXE/random100.bin
-
-import re
-
-from module.plugins.internal.CaptchaService import SolveMedia
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class LoadTo(SimpleHoster):
- __name__ = "LoadTo"
- __type__ = "hoster"
- __version__ = "0.18"
-
- __pattern__ = r'http://(?:www\.)?load\.to/\w+'
-
- __description__ = """ Load.to hoster plugin """
- __license__ = "GPLv3"
- __authors__ = [("halfman", "Pulpan3@gmail.com"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- NAME_PATTERN = r'<h1>(?P<N>.+)</h1>'
- SIZE_PATTERN = r'Size: (?P<S>[\d.,]+) (?P<U>[\w^_]+)'
- OFFLINE_PATTERN = r'>Can\'t find file'
-
- LINK_PATTERN = r'<form method="post" action="(.+?)"'
- WAIT_PATTERN = r'type="submit" value="Download \((\d+)\)"'
-
- URL_REPLACEMENTS = [(r'(\w)$', r'\1/')]
-
-
- def setup(self):
- self.multiDL = True
- self.chunkLimit = 1
-
-
- def handleFree(self):
- # Search for Download URL
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("LINK_PATTERN not found"))
-
- download_url = m.group(1)
-
- # Set Timer - may be obsolete
- m = re.search(self.WAIT_PATTERN, self.html)
- if m:
- self.wait(int(m.group(1)))
-
- # Load.to is using solvemedia captchas since ~july 2014:
- solvemedia = SolveMedia(self)
- captcha_key = solvemedia.detect_key()
-
- if captcha_key is None:
- self.download(download_url)
- else:
- captcha_challenge, captcha_response = solvemedia.challenge(captcha_key)
- self.download(download_url, post={"adcopy_challenge": captcha_challenge, "adcopy_response": captcha_response})
- check = self.checkDownload({'404': re.compile("\A<h1>404 Not Found</h1>"), 'html': re.compile("html")})
- if check == "404":
- self.invalidCaptcha()
- self.retry()
- elif check == "html":
- self.logWarning(_("Downloaded file is an html page, will retry"))
- self.retry()
-
-
-getInfo = create_getInfo(LoadTo)
diff --git a/module/plugins/hoster/LomafileCom.py b/module/plugins/hoster/LomafileCom.py
deleted file mode 100644
index a7ce39d37..000000000
--- a/module/plugins/hoster/LomafileCom.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class LomafileCom(XFSHoster):
- __name__ = "LomafileCom"
- __type__ = "hoster"
- __version__ = "0.51"
-
- __pattern__ = r'http://lomafile\.com/\w{12}'
-
- __description__ = """Lomafile.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("nath_schwarz", "nathan.notwhite@gmail.com"),
- ("guidobelix", "guidobelix@hotmail.it")]
-
-
- HOSTER_DOMAIN = "lomafile.com"
-
- NAME_PATTERN = r'<a href="http://lomafile\.com/w{12}/(?P<N>.+?)">'
- SIZE_PATTERN = r'Size:</b></td><td>(?P<S>[\d.,]+) (?P<U>[\w^_]+)'
-
- OFFLINE_PATTERN = r'>(No such file|Software error:<)'
- TEMP_OFFLINE_PATTERN = r'The page may have been renamed, removed or be temporarily unavailable.<'
-
- CAPTCHA_PATTERN = r'(http://lomafile\.com/captchas/[^"\']+)'
-
-
-getInfo = create_getInfo(LomafileCom)
diff --git a/module/plugins/hoster/LuckyShareNet.py b/module/plugins/hoster/LuckyShareNet.py
deleted file mode 100644
index 2c33b57e7..000000000
--- a/module/plugins/hoster/LuckyShareNet.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from bottle import json_loads
-
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class LuckyShareNet(SimpleHoster):
- __name__ = "LuckyShareNet"
- __type__ = "hoster"
- __version__ = "0.04"
-
- __pattern__ = r'https?://(?:www\.)?luckyshare\.net/(?P<ID>\d{10,})'
-
- __description__ = """LuckyShare.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- INFO_PATTERN = r'<h1 class=\'file_name\'>(?P<N>\S+)</h1>\s*<span class=\'file_size\'>Filesize: (?P<S>[\d.,]+)(?P<U>[\w^_]+)</span>'
- OFFLINE_PATTERN = r'There is no such file available'
-
-
- def parseJson(self, rep):
- if 'AJAX Error' in rep:
- html = self.load(self.pyfile.url, decode=True)
- m = re.search(r"waitingtime = (\d+);", html)
- if m:
- seconds = int(m.group(1))
- self.logDebug("You have to wait %d seconds between free downloads" % seconds)
- self.retry(wait_time=seconds)
- else:
- self.error(_("Unable to detect wait time between free downloads"))
- elif 'Hash expired' in rep:
- self.retry(reason=_("Hash expired"))
- return json_loads(rep)
-
-
- # TODO: There should be a filesize limit for free downloads
- # TODO: Some files could not be downloaded in free mode
- def handleFree(self):
- rep = self.load(r"http://luckyshare.net/download/request/type/time/file/" + self.info['pattern']['ID'], decode=True)
- self.logDebug("JSON: " + rep)
- json = self.parseJson(rep)
-
- self.wait(int(json['time']))
-
- recaptcha = ReCaptcha(self)
-
- for _i in xrange(5):
- challenge, response = recaptcha.challenge()
- rep = self.load(r"http://luckyshare.net/download/verify/challenge/%s/response/%s/hash/%s" %
- (challenge, response, json['hash']), decode=True)
- self.logDebug("JSON: " + rep)
- if 'link' in rep:
- json.update(self.parseJson(rep))
- self.correctCaptcha()
- break
- elif 'Verification failed' in rep:
- self.invalidCaptcha()
- else:
- self.error(_("Unable to get downlaod link"))
-
- if not json['link']:
- self.fail(_("No Download url retrieved/all captcha attempts failed"))
-
- self.download(json['link'])
-
-
-getInfo = create_getInfo(LuckyShareNet)
diff --git a/module/plugins/hoster/MediafireCom.py b/module/plugins/hoster/MediafireCom.py
deleted file mode 100644
index 3ed1199f6..000000000
--- a/module/plugins/hoster/MediafireCom.py
+++ /dev/null
@@ -1,122 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.CaptchaService import SolveMedia
-from module.plugins.internal.SimpleHoster import SimpleHoster, parseFileInfo
-from module.network.RequestFactory import getURL
-
-
-def replace_eval(js_expr):
- return js_expr.replace(r'eval("', '').replace(r"\'", r"'").replace(r'\"', r'"')
-
-
-def checkHTMLHeader(url):
- try:
- for _i in xrange(3):
- header = getURL(url, just_header=True)
- for line in header.splitlines():
- line = line.lower()
- if 'location' in line:
- url = line.split(':', 1)[1].strip()
- if 'error.php?errno=320' in url:
- return url, 1
- if not url.startswith('http://'):
- url = 'http://www.mediafire.com' + url
- break
- elif 'content-disposition' in line:
- return url, 2
- else:
- break
- except:
- return url, 3
-
- return url, 0
-
-
-def getInfo(urls):
- for url in urls:
- location, status = checkHTMLHeader(url)
-
- if status:
- file_info = (url, 0, status, url)
- else:
- file_info = parseFileInfo(MediafireCom, url, getURL(url, decode=True))
-
- yield file_info
-
-
-class MediafireCom(SimpleHoster):
- __name__ = "MediafireCom"
- __type__ = "hoster"
- __version__ = "0.80"
-
- __pattern__ = r'http://(?:www\.)?mediafire\.com/(file/|(view/?|download\.php)?\?)(\w{11}|\w{15})($|/)'
-
- __description__ = """Mediafire.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- LINK_PATTERN = r'<div class="download_link"[^>]*(?:z-index:(?P<zindex>\d+))?[^>]*>\s*<a href="(?P<href>http://[^"]+)"'
- JS_KEY_PATTERN = r'DoShow\(\'mfpromo1\'\);[^{]*{((\w+)=\'\';.*?)eval\(\2\);'
- JS_ZMODULO_PATTERN = r'\(\'z-index\'\)\) \% (\d+)\)\);'
- PAGE1_ACTION_PATTERN = r'<link rel="canonical" href="([^"]+)"/>'
- PASSWORD_PATTERN = r'<form name="form_password"'
-
- NAME_PATTERN = r'<META NAME="description" CONTENT="(?P<N>[^"]+)"/>'
- INFO_PATTERN = r'oFileSharePopup\.ald\(\'(?P<ID>[^\']*)\',\'(?P<N>[^\']*)\',\'(?P<S>[^\']*)\',\'\',\'(?P<sha256>[^\']*)\'\)'
- OFFLINE_PATTERN = r'class="error_msg_title"> Invalid or Deleted File. </div>'
-
-
- def setup(self):
- self.multiDL = False
-
-
- def process(self, pyfile):
- pyfile.url = re.sub(r'/view/?\?', '/?', pyfile.url)
-
- self.url, result = checkHTMLHeader(pyfile.url)
- self.logDebug("Location (%d): %s" % (result, self.url))
-
- if result == 0:
- self.html = self.load(self.url, decode=True)
- self.checkCaptcha()
- self.multiDL = True
- self.check_data = self.getFileInfo()
-
- if self.account:
- self.handlePremium()
- else:
- self.handleFree()
- elif result == 1:
- self.offline()
- else:
- self.multiDL = True
- self.download(self.url, disposition=True)
-
-
- def handleFree(self):
- passwords = self.getPassword().splitlines()
- while self.PASSWORD_PATTERN in self.html:
- if len(passwords):
- password = passwords.pop(0)
- self.logInfo(_("Password protected link, trying ") + password)
- self.html = self.load(self.url, post={"downloadp": password})
- else:
- self.fail(_("No or incorrect password"))
-
- m = re.search(r'kNO = r"(http://.*?)";', self.html)
- if m is None:
- self.error(_("No download URL"))
-
- download_url = m.group(1)
- self.download(download_url)
-
-
- def checkCaptcha(self):
- solvemedia = SolveMedia(self)
- captcha_challenge, captcha_response = solvemedia.challenge()
- self.html = self.load(self.url, post={"adcopy_challenge": captcha_challenge,
- "adcopy_response": captcha_response}, decode=True)
diff --git a/module/plugins/hoster/MegaCoNz.py b/module/plugins/hoster/MegaCoNz.py
deleted file mode 100644
index 385295d42..000000000
--- a/module/plugins/hoster/MegaCoNz.py
+++ /dev/null
@@ -1,171 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import random
-import re
-
-from array import array
-from base64 import standard_b64decode
-from os import remove
-
-from Crypto.Cipher import AES
-from Crypto.Util import Counter
-from pycurl import SSL_CIPHER_LIST
-
-from module.common.json_layer import json_loads, json_dumps
-from module.plugins.Hoster import Hoster
-
-############################ General errors ###################################
-# EINTERNAL (-1): An internal error has occurred. Please submit a bug report, detailing the exact circumstances in which this error occurred
-# EARGS (-2): You have passed invalid arguments to this command
-# EAGAIN (-3): (always at the request level) A temporary congestion or server malfunction prevented your request from being processed. No data was altered. Retry. Retries must be spaced with exponential backoff
-# ERATELIMIT (-4): You have exceeded your command weight per time quota. Please wait a few seconds, then try again (this should never happen in sane real-life applications)
-#
-############################ Upload errors ####################################
-# EFAILED (-5): The upload failed. Please restart it from scratch
-# ETOOMANY (-6): Too many concurrent IP addresses are accessing this upload target URL
-# ERANGE (-7): The upload file packet is out of range or not starting and ending on a chunk boundary
-# EEXPIRED (-8): The upload target URL you are trying to access has expired. Please request a fresh one
-#
-############################ Stream/System errors #############################
-# ENOENT (-9): Object (typically, node or user) not found
-# ECIRCULAR (-10): Circular linkage attempted
-# EACCESS (-11): Access violation (e.g., trying to write to a read-only share)
-# EEXIST (-12): Trying to create an object that already exists
-# EINCOMPLETE (-13): Trying to access an incomplete resource
-# EKEY (-14): A decryption operation failed (never returned by the API)
-# ESID (-15): Invalid or expired user session, please relogin
-# EBLOCKED (-16): User blocked
-# EOVERQUOTA (-17): Request over quota
-# ETEMPUNAVAIL (-18): Resource temporarily not available, please try again later
-# ETOOMANYCONNECTIONS (-19): Too many connections on this resource
-# EWRITE (-20): Write failed
-# EREAD (-21): Read failed
-# EAPPKEY (-22): Invalid application key; request not processed
-
-
-class MegaCoNz(Hoster):
- __name__ = "MegaCoNz"
- __type__ = "hoster"
- __version__ = "0.16"
-
- __pattern__ = r'https?://(\w+\.)?mega\.co\.nz/#!([\w!-]+)'
-
- __description__ = """Mega.co.nz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "ranan@pyload.org")]
-
- API_URL = "https://g.api.mega.co.nz/cs"
- FILE_SUFFIX = ".crypted"
-
-
- def b64_decode(self, data):
- data = data.replace("-", "+").replace("_", "/")
- return standard_b64decode(data + '=' * (-len(data) % 4))
-
-
- def getCipherKey(self, key):
- """ Construct the cipher key from the given data """
- a = array("I", key)
- key_array = array("I", [a[0] ^ a[4], a[1] ^ a[5], a[2] ^ a[6], a[3] ^ a[7]])
- return key_array
-
-
- def callApi(self, **kwargs):
- """ Dispatch a call to the api, see https://mega.co.nz/#developers """
- # generate a session id, no idea where to obtain elsewhere
- uid = random.randint(10 << 9, 10 ** 10)
-
- res = self.load(self.API_URL, get={'id': uid}, post=json_dumps([kwargs]))
- self.logDebug("Api Response: " + res)
- return json_loads(res)
-
-
- def decryptAttr(self, data, key):
- cbc = AES.new(self.getCipherKey(key), AES.MODE_CBC, "\0" * 16)
- attr = cbc.decrypt(self.b64_decode(data))
- self.logDebug("Decrypted Attr: " + attr)
- if not attr.startswith("MEGA"):
- self.fail(_("Decryption failed"))
-
- # Data is padded, 0-bytes must be stripped
- return json_loads(re.search(r'{.+?}', attr).group(0))
-
-
- def decryptFile(self, key):
- """ Decrypts the file at lastDownload` """
-
- # upper 64 bit of counter start
- n = key[16:24]
-
- # convert counter to long and shift bytes
- ctr = Counter.new(128, initial_value=long(n.encode("hex"), 16) << 64)
- cipher = AES.new(self.getCipherKey(key), AES.MODE_CTR, counter=ctr)
-
- self.pyfile.setStatus("decrypting")
-
- file_crypted = self.lastDownload
- file_decrypted = file_crypted.rsplit(self.FILE_SUFFIX)[0]
-
- try:
- f = open(file_crypted, "rb")
- df = open(file_decrypted, "wb")
- except IOError, e:
- self.fail(str(e))
-
- # TODO: calculate CBC-MAC for checksum
-
- size = 2 ** 15 # buffer size, 32k
- while True:
- buf = f.read(size)
- if not buf:
- break
-
- df.write(cipher.decrypt(buf))
-
- f.close()
- df.close()
- remove(file_crypted)
-
- self.lastDownload = file_decrypted
-
-
- def process(self, pyfile):
- key = None
-
- # match is guaranteed because plugin was chosen to handle url
- node = re.match(self.__pattern__, pyfile.url).group(2)
- if "!" in node:
- node, key = node.split("!")
-
- self.logDebug("File id: %s | Key: %s" % (node, key))
-
- if not key:
- self.fail(_("No file key provided in the URL"))
-
- # g is for requesting a download url
- # this is similar to the calls in the mega js app, documentation is very bad
- dl = self.callApi(a="g", g=1, p=node, ssl=1)[0]
-
- if "e" in dl:
- e = dl['e']
- # ETEMPUNAVAIL (-18): Resource temporarily not available, please try again later
- if e == -18:
- self.retry()
- else:
- self.fail(_("Error code:") + e)
-
- # TODO: map other error codes, e.g
- # EACCESS (-11): Access violation (e.g., trying to write to a read-only share)
-
- key = self.b64_decode(key)
- attr = self.decryptAttr(dl['at'], key)
-
- pyfile.name = attr['n'] + self.FILE_SUFFIX
-
- self.req.http.c.setopt(SSL_CIPHER_LIST, "RC4-MD5:DEFAULT")
-
- self.download(dl['g'])
- self.decryptFile(key)
-
- # Everything is finished and final name can be set
- pyfile.name = attr['n']
diff --git a/module/plugins/hoster/MegaDebridEu.py b/module/plugins/hoster/MegaDebridEu.py
deleted file mode 100644
index 4d1e1bcb7..000000000
--- a/module/plugins/hoster/MegaDebridEu.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urllib import unquote_plus
-
-from module.common.json_layer import json_loads
-from module.plugins.Hoster import Hoster
-
-
-class MegaDebridEu(Hoster):
- __name__ = "MegaDebridEu"
- __type__ = "hoster"
- __version__ = "0.4"
-
- __pattern__ = r'^https?://(?:w{3}\d+\.mega-debrid\.eu|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/download/file/[^/]+/.+$'
-
- __description__ = """mega-debrid.eu hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("D.Ducatel", "dducatel@je-geek.fr")]
-
-
- API_URL = "https://www.mega-debrid.eu/api.php"
-
-
- def getFilename(self, url):
- try:
- return unquote_plus(url.rsplit("/", 1)[1])
- except IndexError:
- return ""
-
-
- def process(self, pyfile):
- if re.match(self.__pattern__, pyfile.url):
- new_url = pyfile.url
- elif not self.account:
- self.exitOnFail("Please enter your %s account or deactivate this plugin" % "Mega-debrid.eu")
- else:
- if not self.connectToApi():
- self.exitOnFail("Unable to connect to Mega-debrid.eu")
-
- self.logDebug("Old URL: %s" % pyfile.url)
- new_url = self.debridLink(pyfile.url)
- self.logDebug("New URL: " + new_url)
-
- filename = self.getFilename(new_url)
- if filename != "":
- pyfile.name = filename
- self.download(new_url, disposition=True)
-
-
- def connectToApi(self):
- """
- Connexion to the mega-debrid API
- Return True if succeed
- """
- user, data = self.account.selectAccount()
- jsonResponse = self.load(self.API_URL,
- get={'action': 'connectUser', 'login': user, 'password': data['password']})
- res = json_loads(jsonResponse)
-
- if res['response_code'] == "ok":
- self.token = res['token']
- return True
- else:
- return False
-
-
- def debridLink(self, linkToDebrid):
- """
- Debrid a link
- Return The debrided link if succeed or original link if fail
- """
- jsonResponse = self.load(self.API_URL, get={'action': 'getLink', 'token': self.token},
- post={"link": linkToDebrid})
- res = json_loads(jsonResponse)
-
- if res['response_code'] == "ok":
- debridedLink = res['debridLink'][1:-1]
- return debridedLink
- else:
- self.exitOnFail("Unable to debrid %s" % linkToDebrid)
-
-
- def exitOnFail(self, msg):
- """
- exit the plugin on fail case
- And display the reason of this failure
- """
- if self.getConfig("unloadFailing"):
- self.logError(_(msg))
- self.resetAccount()
- else:
- self.fail(_(msg))
diff --git a/module/plugins/hoster/MegaFilesSe.py b/module/plugins/hoster/MegaFilesSe.py
deleted file mode 100644
index c3de57914..000000000
--- a/module/plugins/hoster/MegaFilesSe.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class MegaFilesSe(DeadHoster):
- __name__ = "MegaFilesSe"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?megafiles\.se/\w{12}'
-
- __description__ = """MegaFiles.se hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")]
-
-
-getInfo = create_getInfo(MegaFilesSe)
diff --git a/module/plugins/hoster/MegaRapidCz.py b/module/plugins/hoster/MegaRapidCz.py
deleted file mode 100644
index b3100b6d4..000000000
--- a/module/plugins/hoster/MegaRapidCz.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from pycurl import HTTPHEADER
-
-from module.network.RequestFactory import getRequest
-from module.plugins.internal.SimpleHoster import SimpleHoster, parseFileInfo
-
-
-def getInfo(urls):
- h = getRequest()
- h.c.setopt(HTTPHEADER,
- ["Accept: text/html",
- "User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko/20100101 Firefox/25.0"])
-
- for url in urls:
- html = h.load(url, decode=True)
- yield parseFileInfo(MegaRapidCz, url, html)
-
-
-class MegaRapidCz(SimpleHoster):
- __name__ = "MegaRapidCz"
- __type__ = "hoster"
- __version__ = "0.54"
-
- __pattern__ = r'http://(?:www\.)?(share|mega)rapid\.cz/soubor/\d+/.+'
-
- __description__ = """MegaRapid.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("MikyWoW", "mikywow@seznam.cz"),
- ("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- NAME_PATTERN = r'<h1[^>]*><span[^>]*>(?:<a[^>]*>)?(?P<N>[^<]+)'
- SIZE_PATTERN = r'<td class="i">Velikost:</td>\s*<td class="h"><strong>\s*(?P<S>[\d.,]+) (?P<U>[\w^_]+)</strong></td>'
- OFFLINE_PATTERN = ur'Nastala chyba 404|Soubor byl smazán'
-
- FORCE_CHECK_TRAFFIC = True
-
- LINK_PATTERN = r'<a href="([^"]+)" title="Stahnout">([^<]+)</a>'
- ERR_LOGIN_PATTERN = ur'<div class="error_div"><strong>Stahování je přístupné pouze přihlášenÃœm uÅŸivatelům'
- ERR_CREDIT_PATTERN = ur'<div class="error_div"><strong>Stahování zdarma je moÅŸné jen přes náš'
-
-
- def setup(self):
- self.chunkLimit = 1
-
-
- def handlePremium(self):
- try:
- self.html = self.load(self.pyfile.url, decode=True)
- except BadHeader, e:
- self.account.relogin(self.user)
- self.retry(wait_time=60, reason=str(e))
-
- m = re.search(self.LINK_PATTERN, self.html)
- if m:
- link = m.group(1)
- self.logDebug("Premium link: %s" % link)
- self.download(link, disposition=True)
- else:
- if re.search(self.ERR_LOGIN_PATTERN, self.html):
- self.relogin(self.user)
- self.retry(wait_time=60, reason=_("User login failed"))
- elif re.search(self.ERR_CREDIT_PATTERN, self.html):
- self.fail(_("Not enough credit left"))
- else:
- self.fail(_("Download link not found"))
diff --git a/module/plugins/hoster/MegacrypterCom.py b/module/plugins/hoster/MegacrypterCom.py
deleted file mode 100644
index 4034f7d32..000000000
--- a/module/plugins/hoster/MegacrypterCom.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.common.json_layer import json_loads, json_dumps
-
-from module.plugins.hoster.MegaCoNz import MegaCoNz
-
-
-class MegacrypterCom(MegaCoNz):
- __name__ = "MegacrypterCom"
- __type__ = "hoster"
- __version__ = "0.21"
-
- __pattern__ = r'(https?://\w{0,10}\.?megacrypter\.com/[\w!-]+)'
-
- __description__ = """Megacrypter.com decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("GonzaloSR", "gonzalo@gonzalosr.com")]
-
-
- API_URL = "http://megacrypter.com/api"
- FILE_SUFFIX = ".crypted"
-
-
- def callApi(self, **kwargs):
- """ Dispatch a call to the api, see megacrypter.com/api_doc """
- self.logDebug("JSON request: " + json_dumps(kwargs))
- res = self.load(self.API_URL, post=json_dumps(kwargs))
- self.logDebug("API Response: " + res)
- return json_loads(res)
-
-
- def process(self, pyfile):
- # match is guaranteed because plugin was chosen to handle url
- node = re.match(self.__pattern__, pyfile.url).group(1)
-
- # get Mega.co.nz link info
- info = self.callApi(link=node, m="info")
-
- # get crypted file URL
- dl = self.callApi(link=node, m="dl")
-
- # TODO: map error codes, implement password protection
- # if info['pass'] is True:
- # crypted_file_key, md5_file_key = info['key'].split("#")
-
- key = self.b64_decode(info['key'])
-
- pyfile.name = info['name'] + self.FILE_SUFFIX
-
- self.download(dl['url'])
- self.decryptFile(key)
-
- # Everything is finished and final name can be set
- pyfile.name = info['name']
diff --git a/module/plugins/hoster/MegareleaseOrg.py b/module/plugins/hoster/MegareleaseOrg.py
deleted file mode 100644
index 60796c1ee..000000000
--- a/module/plugins/hoster/MegareleaseOrg.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class MegareleaseOrg(DeadHoster):
- __name__ = "MegareleaseOrg"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'https?://(?:www\.)?megarelease\.org/\w{12}'
-
- __description__ = """Megarelease.org hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("derek3x", "derek3x@vmail.me"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(MegareleaseOrg)
diff --git a/module/plugins/hoster/MegasharesCom.py b/module/plugins/hoster/MegasharesCom.py
deleted file mode 100644
index 9d8441c6f..000000000
--- a/module/plugins/hoster/MegasharesCom.py
+++ /dev/null
@@ -1,113 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import time
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class MegasharesCom(SimpleHoster):
- __name__ = "MegasharesCom"
- __type__ = "hoster"
- __version__ = "0.27"
-
- __pattern__ = r'http://(?:www\.)?(d\d{2}\.)?megashares\.com/((index\.php)?\?d\d{2}=|dl/)\w+'
-
- __description__ = """Megashares.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- NAME_PATTERN = r'<h1 class="black xxl"[^>]*title="(?P<N>[^"]+)">'
- SIZE_PATTERN = r'<strong><span class="black">Filesize:</span></strong> (?P<S>[\d.,]+) (?P<U>[\w^_]+)'
- OFFLINE_PATTERN = r'<dd class="red">(Invalid Link Request|Link has been deleted|Invalid link)'
-
- LINK_PATTERN = r'<div id="show_download_button_%d"[^>]*>\s*<a href="([^"]+)">'
-
- PASSPORT_LEFT_PATTERN = r'Your Download Passport is: <[^>]*>(\w+).*?You have.*?<[^>]*>.*?([\d.]+) (\w+)'
- PASSPORT_RENEW_PATTERN = r'(\d+):<strong>(\d+)</strong>:<strong>(\d+)</strong>'
- REACTIVATE_NUM_PATTERN = r'<input[^>]*id="random_num" value="(\d+)" />'
- REACTIVATE_PASSPORT_PATTERN = r'<input[^>]*id="passport_num" value="(\w+)" />'
- REQUEST_URI_PATTERN = r'var request_uri = "([^"]+)";'
- NO_SLOTS_PATTERN = r'<dd class="red">All download slots for this link are currently filled'
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = self.premium
-
-
- def handlePremium(self):
- self.handleDownload(True)
-
-
- def handleFree(self):
- if self.NO_SLOTS_PATTERN in self.html:
- self.retry(wait_time=5 * 60)
-
- m = re.search(self.REACTIVATE_PASSPORT_PATTERN, self.html)
- if m:
- passport_num = m.group(1)
- request_uri = re.search(self.REQUEST_URI_PATTERN, self.html).group(1)
-
- for _i in xrange(5):
- random_num = re.search(self.REACTIVATE_NUM_PATTERN, self.html).group(1)
-
- verifyinput = self.decryptCaptcha("http://d01.megashares.com/index.php",
- get={'secgfx': "gfx", 'random_num': random_num})
-
- self.logInfo(_("Reactivating passport %s: %s %s") % (passport_num, random_num, verifyinput))
-
- res = self.load("http://d01.megashares.com%s" % request_uri,
- get={'rs' : "check_passport_renewal",
- 'rsargs[]': verifyinput,
- 'rsargs[]': random_num,
- 'rsargs[]': passport_num,
- 'rsargs[]': "replace_sec_pprenewal",
- 'rsrnd[]' : str(int(time() * 1000))})
-
- if 'Thank you for reactivating your passport.' in res:
- self.correctCaptcha()
- self.retry()
- else:
- self.invalidCaptcha()
- else:
- self.fail(_("Failed to reactivate passport"))
-
- m = re.search(self.PASSPORT_RENEW_PATTERN, self.html)
- if m:
- time = [int(x) for x in m.groups()]
- renew = time[0] + (time[1] * 60) + (time[2] * 60)
- self.logDebug("Waiting %d seconds for a new passport" % renew)
- self.retry(wait_time=renew, reason=_("Passport renewal"))
-
- # Check traffic left on passport
- m = re.search(self.PASSPORT_LEFT_PATTERN, self.html, re.M | re.S)
- if m is None:
- self.fail(_("Passport not found"))
-
- self.logInfo(_("Download passport: %s") % m.group(1))
- data_left = float(m.group(2)) * 1024 ** {'B': 0, 'KB': 1, 'MB': 2, 'GB': 3}[m.group(3)]
- self.logInfo(_("Data left: %s %s (%d MB needed)") % (m.group(2), m.group(3), self.pyfile.size / 1048576))
-
- if not data_left:
- self.retry(wait_time=600, reason=_("Passport renewal"))
-
- self.handleDownload(False)
-
-
- def handleDownload(self, premium=False):
- # Find download link;
- m = re.search(self.LINK_PATTERN % (1 if premium else 2), self.html)
- msg = _('%s download URL' % ('Premium' if premium else 'Free'))
- if m is None:
- self.error(msg)
-
- download_url = m.group(1)
- self.logDebug("%s: %s" % (msg, download_url))
- self.download(download_url)
-
-
-getInfo = create_getInfo(MegasharesCom)
diff --git a/module/plugins/hoster/MegauploadCom.py b/module/plugins/hoster/MegauploadCom.py
deleted file mode 100644
index 7f51a8a46..000000000
--- a/module/plugins/hoster/MegauploadCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class MegauploadCom(DeadHoster):
- __name__ = "MegauploadCom"
- __type__ = "hoster"
- __version__ = "0.31"
-
- __pattern__ = r'http://(?:www\.)?megaupload\.com/\?.*&?(d|v)=\w+'
-
- __description__ = """Megaupload.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("spoob", "spoob@pyload.org")]
-
-
-getInfo = create_getInfo(MegauploadCom)
diff --git a/module/plugins/hoster/MegavideoCom.py b/module/plugins/hoster/MegavideoCom.py
deleted file mode 100644
index 24905ce62..000000000
--- a/module/plugins/hoster/MegavideoCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class MegavideoCom(DeadHoster):
- __name__ = "MegavideoCom"
- __type__ = "hoster"
- __version__ = "0.21"
-
- __pattern__ = r'http://(?:www\.)?megavideo\.com/\?.*&?(d|v)=\w+'
-
- __description__ = """Megavideo.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de"),
- ("mkaay", "mkaay@mkaay.de")]
-
-
-getInfo = create_getInfo(MegavideoCom)
diff --git a/module/plugins/hoster/MovReelCom.py b/module/plugins/hoster/MovReelCom.py
deleted file mode 100644
index cd1626f6f..000000000
--- a/module/plugins/hoster/MovReelCom.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class MovReelCom(XFSHoster):
- __name__ = "MovReelCom"
- __type__ = "hoster"
- __version__ = "1.24"
-
- __pattern__ = r'http://(?:www\.)?movreel\.com/\w{12}'
-
- __description__ = """MovReel.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("JorisV83", "jorisv83-pyload@yahoo.com")]
-
-
- HOSTER_DOMAIN = "movreel.com"
-
- NAME_PATTERN = r'Filename: <b>(?P<N>.+?)<'
- SIZE_PATTERN = r'Size: (?P<S>[\d.,]+) (?P<U>[\w^_]+)'
-
- LINK_PATTERN = r'<a href="([^"]+)">Download Link'
-
-
-getInfo = create_getInfo(MovReelCom)
diff --git a/module/plugins/hoster/MultishareCz.py b/module/plugins/hoster/MultishareCz.py
deleted file mode 100644
index fc866e2b1..000000000
--- a/module/plugins/hoster/MultishareCz.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from random import random
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class MultishareCz(SimpleHoster):
- __name__ = "MultishareCz"
- __type__ = "hoster"
- __version__ = "0.35"
-
- __pattern__ = r'http://(?:www\.)?multishare\.cz/stahnout/(?P<ID>\d+).*'
-
- __description__ = """MultiShare.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- SIZE_REPLACEMENTS = [('&nbsp;', '')]
-
- MULTI_HOSTER = True
-
- INFO_PATTERN = ur'(?:<li>Název|Soubor): <strong>(?P<N>[^<]+)</strong><(?:/li><li|br)>Velikost: <strong>(?P<S>[^<]+)</strong>'
- OFFLINE_PATTERN = ur'<h1>Stáhnout soubor</h1><p><strong>PoşadovanÜ soubor neexistuje.</strong></p>'
-
-
- def process(self, pyfile):
- msurl = re.match(self.__pattern__, pyfile.url)
- if msurl:
- self.fileID = msurl.group('ID')
- self.html = self.load(pyfile.url, decode=True)
- self.getFileInfo()
-
- if self.premium:
- self.handlePremium()
- else:
- self.handleFree()
- else:
- self.handleOverriden()
-
-
- def handleFree(self):
- self.download("http://www.multishare.cz/html/download_free.php?ID=%s" % self.fileID)
-
-
- def handlePremium(self):
- if not self.checkCredit():
- self.logWarning(_("Not enough credit left to download file"))
- self.resetAccount()
-
- self.download("http://www.multishare.cz/html/download_premium.php?ID=%s" % self.fileID)
-
-
- def handleOverriden(self):
- if not self.premium:
- self.fail(_("Only premium users can download from other hosters"))
-
- self.html = self.load('http://www.multishare.cz/html/mms_ajax.php', post={"link": self.pyfile.url}, decode=True)
- self.getFileInfo()
-
- if not self.checkCredit():
- self.fail(_("Not enough credit left to download file"))
-
- url = "http://dl%d.mms.multishare.cz/html/mms_process.php" % round(random() * 10000 * random())
- params = {"u_ID": self.acc_info['u_ID'], "u_hash": self.acc_info['u_hash'], "link": self.pyfile.url}
- self.logDebug(url, params)
- self.download(url, get=params)
-
-
- def checkCredit(self):
- self.acc_info = self.account.getAccountInfo(self.user, True)
- self.logInfo(_("User %s has %i MB left") % (self.user, self.acc_info['trafficleft'] / 1024))
-
- return self.pyfile.size / 1024 <= self.acc_info['trafficleft']
-
-
-getInfo = create_getInfo(MultishareCz)
diff --git a/module/plugins/hoster/MyfastfileCom.py b/module/plugins/hoster/MyfastfileCom.py
deleted file mode 100644
index a2e582bd0..000000000
--- a/module/plugins/hoster/MyfastfileCom.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.common.json_layer import json_loads
-from module.plugins.Hoster import Hoster
-
-
-class MyfastfileCom(Hoster):
- __name__ = "MyfastfileCom"
- __type__ = "hoster"
- __version__ = "0.04"
-
- __pattern__ = r'http://(?:www\.)?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/dl/'
-
- __description__ = """Myfastfile.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- def setup(self):
- self.chunkLimit = -1
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- if re.match(self.__pattern__, pyfile.url):
- new_url = pyfile.url
- elif not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "Myfastfile.com")
- self.fail(_("No Myfastfile.com account provided"))
- else:
- self.logDebug("Original URL: %s" % pyfile.url)
- page = self.load('http://myfastfile.com/api.php',
- get={'user': self.user, 'pass': self.account.getAccountData(self.user)['password'],
- 'link': pyfile.url})
- self.logDebug("JSON data: " + page)
- page = json_loads(page)
- if page['status'] != 'ok':
- self.fail(_("Unable to unrestrict link"))
- new_url = page['link']
-
- if new_url != pyfile.url:
- self.logDebug("Unrestricted URL: " + new_url)
-
- self.download(new_url, disposition=True)
diff --git a/module/plugins/hoster/MyvideoDe.py b/module/plugins/hoster/MyvideoDe.py
deleted file mode 100644
index d4a85c8c0..000000000
--- a/module/plugins/hoster/MyvideoDe.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Hoster import Hoster
-from module.unescape import unescape
-
-
-class MyvideoDe(Hoster):
- __name__ = "MyvideoDe"
- __type__ = "hoster"
- __version__ = "0.9"
-
- __pattern__ = r'http://(?:www\.)?myvideo\.de/watch/'
-
- __description__ = """Myvideo.de hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("spoob", "spoob@pyload.org")]
-
-
- def process(self, pyfile):
- self.pyfile = pyfile
- self.download_html()
- pyfile.name = self.get_file_name()
- self.download(self.get_file_url())
-
-
- def download_html(self):
- self.html = self.load(self.pyfile.url)
-
-
- def get_file_url(self):
- videoId = re.search(r"addVariable\('_videoid','(.*)'\);p.addParam\('quality'", self.html).group(1)
- videoServer = re.search("rel='image_src' href='(.*)thumbs/.*' />", self.html).group(1)
- file_url = videoServer + videoId + ".flv"
- return file_url
-
-
- def get_file_name(self):
- file_name_pattern = r'<h1 class=\'globalHd\'>(.*)</h1>'
- return unescape(re.search(file_name_pattern, self.html).group(1).replace("/", "") + '.flv')
-
-
- def file_exists(self):
- self.download_html()
- self.load(str(self.pyfile.url), cookies=False, just_header=True)
- if self.req.lastEffectiveURL == "http://www.myvideo.de/":
- return False
- return True
diff --git a/module/plugins/hoster/NahrajCz.py b/module/plugins/hoster/NahrajCz.py
deleted file mode 100644
index 6b5699408..000000000
--- a/module/plugins/hoster/NahrajCz.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class NahrajCz(DeadHoster):
- __name__ = "NahrajCz"
- __type__ = "hoster"
- __version__ = "0.21"
-
- __pattern__ = r'http://(?:www\.)?nahraj\.cz/content/download/.+'
-
- __description__ = """Nahraj.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(NahrajCz)
diff --git a/module/plugins/hoster/NarodRu.py b/module/plugins/hoster/NarodRu.py
deleted file mode 100644
index 21d4e3e3d..000000000
--- a/module/plugins/hoster/NarodRu.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from random import random
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class NarodRu(SimpleHoster):
- __name__ = "NarodRu"
- __type__ = "hoster"
- __version__ = "0.11"
-
- __pattern__ = r'http://(?:www\.)?narod(\.yandex)?\.ru/(disk|start/\d+\.\w+-narod\.yandex\.ru)/(?P<ID>\d+)/.+'
-
- __description__ = """Narod.ru hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<dt class="name">(?:<[^<]*>)*(?P<N>[^<]+)</dt>'
- SIZE_PATTERN = r'<dd class="size">(?P<S>\d[^<]*)</dd>'
- OFFLINE_PATTERN = r'<title>404</title>|Ѐайл уЎалеМ с сервОса|ЗакПМчОлся срПк храМеМОя файла\.'
-
- SIZE_REPLACEMENTS = [(u'КБ', 'KB'), (u'МБ', 'MB'), (u'ГБ', 'GB')]
- URL_REPLACEMENTS = [("narod.yandex.ru/", "narod.ru/"),
- (r"/start/\d+\.\w+-narod\.yandex\.ru/(\d{6,15})/\w+/(\w+)", r"/disk/\1/\2")]
-
- CAPTCHA_PATTERN = r'<number url="(.*?)">(\w+)</number>'
- LINK_PATTERN = r'<a class="h-link" rel="yandex_bar" href="(.+?)">'
-
-
- def handleFree(self):
- for _i in xrange(5):
- self.html = self.load('http://narod.ru/disk/getcapchaxml/?rnd=%d' % int(random() * 777))
- m = re.search(self.CAPTCHA_PATTERN, self.html)
- if m is None:
- self.error(_("Captcha"))
- post_data = {"action": "sendcapcha"}
- captcha_url, post_data['key'] = m.groups()
- post_data['rep'] = self.decryptCaptcha(captcha_url)
-
- self.html = self.load(self.pyfile.url, post=post_data, decode=True)
- m = re.search(self.LINK_PATTERN, self.html)
- if m:
- url = 'http://narod.ru' + m.group(1)
- self.correctCaptcha()
- break
- elif u'<b class="error-msg"><strong>ОшОблОсь?</strong>' in self.html:
- self.invalidCaptcha()
- else:
- self.error(_("Download link"))
- else:
- self.fail(_("No valid captcha code entered"))
-
- self.download(url)
-
-
-getInfo = create_getInfo(NarodRu)
diff --git a/module/plugins/hoster/NetloadIn.py b/module/plugins/hoster/NetloadIn.py
deleted file mode 100644
index f5c5ee802..000000000
--- a/module/plugins/hoster/NetloadIn.py
+++ /dev/null
@@ -1,294 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urljoin
-from time import sleep, time
-
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
-from module.plugins.Plugin import chunks
-from module.plugins.internal.CaptchaService import ReCaptcha
-
-
-def getInfo(urls):
- ## returns list of tupels (name, size (in bytes), status (see FileDatabase), url)
-
- apiurl = "http://api.netload.in/info.php"
- id_regex = re.compile(NetloadIn.__pattern__)
- urls_per_query = 80
-
- for chunk in chunks(urls, urls_per_query):
- ids = ""
- for url in chunk:
- match = id_regex.search(url)
- if match:
- ids = ids + match.group(1) + ";"
-
- api = getURL(apiurl,
- get={'auth' : "Zf9SnQh9WiReEsb18akjvQGqT0I830e8",
- 'bz' : 1,
- 'md5' : 1,
- 'file_id': ids},
- decode=True)
-
- if api is None or len(api) < 10:
- self.logDebug("Prefetch failed")
- return
-
- if api.find("unknown_auth") >= 0:
- self.logDebug("Outdated auth code")
- return
-
- result = []
-
- for i, r in enumerate(api.splitlines()):
- try:
- tmp = r.split(";")
-
- try:
- size = int(tmp[2])
- except Exception:
- size = 0
-
- result.append((tmp[1], size, 2 if tmp[3] == "online" else 1, chunk[i] ))
-
- except Exception:
- self.logDebug("Error while processing response: %s" % r)
-
- yield result
-
-
-class NetloadIn(Hoster):
- __name__ = "NetloadIn"
- __type__ = "hoster"
- __version__ = "0.47"
-
- __pattern__ = r'https?://(?:[^/]*\.)?netload\.in/(?:datei(.*?)(?:\.htm|/)|index\.php?id=10&file_id=)'
-
- __description__ = """Netload.in hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("spoob", "spoob@pyload.org"),
- ("RaNaN", "ranan@pyload.org"),
- ("Gregy", "gregy@gregy.cz")]
-
-
- def setup(self):
- self.multiDL = self.resumeDownload = self.premium
-
-
- def process(self, pyfile):
- self.url = pyfile.url
-
- self.prepare()
-
- pyfile.setStatus("downloading")
-
- self.proceed(self.url)
-
-
- def prepare(self):
- self.download_api_data()
-
- if self.api_data and self.api_data['filename']:
- self.pyfile.name = self.api_data['filename']
-
- if self.premium:
- self.logDebug("Use Premium Account")
-
- settings = self.load("http://www.netload.in/index.php", get={'id': 2, 'lang': "en"})
-
- if '<option value="2" selected="selected">Direkter Download' in settings:
- self.logDebug("Using direct download")
- return True
- else:
- self.logDebug("Direct downloads not enabled. Parsing html for a download URL")
-
- if self.download_html():
- return True
- else:
- self.fail(_("Failed"))
- return False
-
-
- def download_api_data(self, n=0):
- url = self.url
- id_regex = re.compile(self.__pattern__)
- match = id_regex.search(url)
-
- if match:
- #normalize url
- self.url = 'http://www.netload.in/datei%s.htm' % match.group(1)
- self.logDebug("URL: %s" % self.url)
- else:
- self.api_data = False
- return
-
- apiurl = "http://api.netload.in/info.php"
- html = self.load(apiurl, cookies=False,
- get={"file_id": match.group(1), "auth": "Zf9SnQh9WiReEsb18akjvQGqT0I830e8", "bz": "1",
- "md5": "1"}, decode=True).strip()
- if not html and n <= 3:
- sleep(0.2)
- self.download_api_data(n + 1)
- return
-
- self.logDebug("APIDATA: " + html)
-
- self.api_data = {}
-
- if html and ";" in html and html not in ("unknown file_data", "unknown_server_data", "No input file specified."):
- lines = html.split(";")
- self.api_data['exists'] = True
- self.api_data['fileid'] = lines[0]
- self.api_data['filename'] = lines[1]
- self.api_data['size'] = lines[2]
- self.api_data['status'] = lines[3]
-
- if self.api_data['status'] == "online":
- self.api_data['checksum'] = lines[4].strip()
- else:
- self.api_data = False # check manually since api data is useless sometimes
-
- if lines[0] == lines[1] and lines[2] == "0": # useless api data
- self.api_data = False
- else:
- self.api_data = False
-
-
- def final_wait(self, page):
- wait_time = self.get_wait_time(page)
-
- self.setWait(wait_time)
-
- self.logDebug("Final wait %d seconds" % wait_time)
-
- self.wait()
-
- self.url = self.get_file_url(page)
-
-
- def check_free_wait(self,page):
- if ">An access request has been made from IP address <" in page:
- self.wantReconnect = True
- self.setWait(self.get_wait_time(page) or 30)
- self.wait()
- return True
- else:
- return False
-
-
- def download_html(self):
- page = self.load(self.url, decode=True)
-
- if "/share/templates/download_hddcrash.tpl" in page:
- self.logError(_("Netload HDD Crash"))
- self.fail(_("File temporarily not available"))
-
- if not self.api_data:
- self.logDebug("API Data may be useless, get details from html page")
-
- if "* The file was deleted" in page:
- self.offline()
-
- name = re.search(r'class="dl_first_filename">([^<]+)', page, re.M)
- # the found filename is not truncated
- if name:
- name = name.group(1).strip()
- if not name.endswith(".."):
- self.pyfile.name = name
-
- captchawaited = False
-
- for i in xrange(5):
- if not page:
- page = self.load(self.url)
- t = time() + 30
-
- if "/share/templates/download_hddcrash.tpl" in page:
- self.logError(_("Netload HDD Crash"))
- self.fail(_("File temporarily not available"))
-
- self.logDebug("Try number %d " % i)
-
- if ">Your download is being prepared.<" in page:
- self.logDebug("We will prepare your download")
- self.final_wait(page)
- return True
-
- self.logDebug("Trying to find captcha")
-
- try:
- url_captcha_html = re.search(r'(index.php\?id=10&amp;.*&amp;captcha=1)', page).group(1).replace("amp;", "")
-
- except Exception, e:
- self.logDebug("Exception during Captcha regex: %s" % e.message)
- page = None
-
- else:
- url_captcha_html = urljoin("http://netload.in/", url_captcha_html)
- break
-
- self.html = self.load(url_captcha_html)
-
- recaptcha = ReCaptcha(self)
-
- for _i in xrange(5):
- challenge, response = recaptcha.challenge()
-
- response_page = self.load("http://www.netload.in/index.php?id=10",
- post={'captcha_check' : '1',
- 'recaptcha_challenge_field': challenge,
- 'recaptcha_response_field' : response,
- 'file_id' : self.api_data['fileid'],
- 'Download_Next' : ''})
- if "Orange_Link" in response_page:
- break
-
- if self.check_free_wait(response_page):
- self.logDebug("Had to wait for next free slot, trying again")
- return self.download_html()
-
- else:
- download_url = self.get_file_url(response_page)
- self.logDebug("Download URL after get_file: " + download_url)
- if not download_url.startswith("http://"):
- self.error("download url: %s" % download_url)
- self.wait()
-
- self.url = download_url
- return True
-
-
- def get_file_url(self, page):
- try:
- file_url_pattern = r'<a class="Orange_Link" href="(http://.+)".?>Or click here'
- attempt = re.search(file_url_pattern, page)
- if attempt is not None:
- return attempt.group(1)
- else:
- self.logDebug("Backup try for final link")
- file_url_pattern = r'<a href="(.+)" class="Orange_Link">Click here'
- attempt = re.search(file_url_pattern, page)
- return "http://netload.in/" + attempt.group(1)
-
- except Exception, e:
- self.logDebug("Getting final link failed", e.message)
- return None
-
-
- def get_wait_time(self, page):
- return int(re.search(r"countdown\((.+),'change\(\)'\)", page).group(1)) / 100
-
-
- def proceed(self, url):
- self.download(url, disposition=True)
-
- check = self.checkDownload({'empty' : re.compile(r'^$'),
- 'offline': re.compile("The file was deleted")})
- if check == "empty":
- self.logInfo(_("Downloaded File was empty"))
- self.retry()
-
- elif check == "offline":
- self.offline()
diff --git a/module/plugins/hoster/NosuploadCom.py b/module/plugins/hoster/NosuploadCom.py
deleted file mode 100644
index b2255ca54..000000000
--- a/module/plugins/hoster/NosuploadCom.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class NosuploadCom(XFSHoster):
- __name__ = "NosuploadCom"
- __type__ = "hoster"
- __version__ = "0.31"
-
- __pattern__ = r'http://(?:www\.)?nosupload\.com/\?d=\w{12}'
-
- __description__ = """Nosupload.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("igel", "igelkun@myopera.com")]
-
-
- HOSTER_DOMAIN = "nosupload.com"
-
- SIZE_PATTERN = r'<p><strong>Size:</strong> (?P<S>[\d.,]+) (?P<U>[\w^_]+)</p>'
- LINK_PATTERN = r'<a class="select" href="(http://.+?)">Download</a>'
- WAIT_PATTERN = r'Please wait.*?>(\d+)</span>'
-
-
- def getDownloadLink(self):
- # stage1: press the "Free Download" button
- data = self.getPostParameters()
- self.html = self.load(self.pyfile.url, post=data, ref=True, decode=True)
-
- # stage2: wait some time and press the "Download File" button
- data = self.getPostParameters()
- wait_time = re.search(self.WAIT_PATTERN, self.html, re.M | re.S).group(1)
- self.logDebug("Hoster told us to wait %s seconds" % wait_time)
- self.wait(wait_time)
- self.html = self.load(self.pyfile.url, post=data, ref=True, decode=True)
-
- # stage3: get the download link
- return re.search(self.LINK_PATTERN, self.html, re.S).group(1)
-
-
-getInfo = create_getInfo(NosuploadCom)
diff --git a/module/plugins/hoster/NovafileCom.py b/module/plugins/hoster/NovafileCom.py
deleted file mode 100644
index 5c4dfddca..000000000
--- a/module/plugins/hoster/NovafileCom.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://novafile.com/vfun4z6o2cit
-# http://novafile.com/s6zrr5wemuz4
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class NovafileCom(XFSHoster):
- __name__ = "NovafileCom"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'http://(?:www\.)?novafile\.com/\w{12}'
-
- __description__ = """Novafile.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- HOSTER_DOMAIN = "novafile.com"
-
- SIZE_PATTERN = r'<div class="size">(?P<S>.+?)</div>'
- ERROR_PATTERN = r'class="alert[^"]*alert-separate"[^>]*>\s*(?:<p>)?(.*?)\s*</'
- LINK_PATTERN = r'<a href="(http://s\d+\.novafile\.com/.*?)" class="btn btn-green">Download File</a>'
- WAIT_PATTERN = r'<p>Please wait <span id="count"[^>]*>(\d+)</span> seconds</p>'
-
-
-getInfo = create_getInfo(NovafileCom)
diff --git a/module/plugins/hoster/OboomCom.py b/module/plugins/hoster/OboomCom.py
deleted file mode 100644
index a2a0840c3..000000000
--- a/module/plugins/hoster/OboomCom.py
+++ /dev/null
@@ -1,145 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# https://www.oboom.com/B7CYZIEB/10Mio.dat
-
-import re
-
-from module.common.json_layer import json_loads
-from module.plugins.Hoster import Hoster
-from module.plugins.internal.CaptchaService import ReCaptcha
-
-
-class OboomCom(Hoster):
- __name__ = "OboomCom"
- __type__ = "hoster"
- __version__ = "0.3"
-
- __pattern__ = r'https?://(?:www\.)?oboom\.com/(#(id=|/)?)?(?P<ID>\w{8})'
-
- __description__ = """oboom.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stanley", "stanley.foerster@gmail.com")]
-
-
- RECAPTCHA_KEY = "6LdqpO0SAAAAAJGHXo63HyalP7H4qlRs_vff0kJX"
-
-
- def setup(self):
- self.chunkLimit = 1
- self.multiDL = self.resumeDownload = self.premium
-
-
- def process(self, pyfile):
- self.pyfile.url.replace(".com/#id=", ".com/#")
- self.pyfile.url.replace(".com/#/", ".com/#")
- self.getFileId(self.pyfile.url)
- self.getSessionToken()
- self.getFileInfo(self.sessionToken, self.fileId)
- self.pyfile.name = self.fileName
- self.pyfile.size = self.fileSize
- if not self.premium:
- self.solveCaptcha()
- self.getDownloadTicket()
- self.download("https://%s/1.0/dlh" % self.downloadDomain, get={"ticket": self.downloadTicket, "http_errors": 0})
-
-
- def loadUrl(self, url, get=None):
- if get is None:
- get = dict()
- return json_loads(self.load(url, get, decode=True))
-
-
- def getFileId(self, url):
- self.fileId = re.match(OboomCom.__pattern__, url).group('ID')
-
-
- def getSessionToken(self):
- if self.premium:
- accountInfo = self.account.getAccountInfo(self.user, True)
- if "session" in accountInfo:
- self.sessionToken = accountInfo['session']
- else:
- self.fail(_("Could not retrieve premium session"))
- else:
- apiUrl = "https://www.oboom.com/1.0/guestsession"
- result = self.loadUrl(apiUrl)
- if result[0] == 200:
- self.sessionToken = result[1]
- else:
- self.fail(_("Could not retrieve token for guest session. Error code: %s") % result[0])
-
-
- def solveCaptcha(self):
- recaptcha = ReCaptcha(self)
-
- for _i in xrange(5):
- challenge, response = recaptcha.challenge(self.RECAPTCHA_KEY)
- apiUrl = "https://www.oboom.com/1.0/download/ticket"
- params = {"recaptcha_challenge_field": challenge,
- "recaptcha_response_field": response,
- "download_id": self.fileId,
- "token": self.sessionToken}
- result = self.loadUrl(apiUrl, params)
-
- if result[0] == 200:
- self.downloadToken = result[1]
- self.downloadAuth = result[2]
- self.correctCaptcha()
- self.setWait(30)
- self.wait()
- break
-
- elif result[0] == 400:
- if result[1] == "incorrect-captcha-sol":
- self.invalidCaptcha()
- elif result[1] == "captcha-timeout":
- self.invalidCaptcha()
- elif result[1] == "forbidden":
- self.retry(5, 15 * 60, _("Service unavailable"))
-
- elif result[0] == 403:
- if result[1] == -1: # another download is running
- self.setWait(15 * 60)
- else:
- self.setWait(result[1], True)
- self.wait()
- self.retry(5)
- else:
- self.invalidCaptcha()
- self.fail(_("Received invalid captcha 5 times"))
-
-
- def getFileInfo(self, token, fileId):
- apiUrl = "https://api.oboom.com/1.0/info"
- params = {"token": token, "items": fileId, "http_errors": 0}
-
- result = self.loadUrl(apiUrl, params)
- if result[0] == 200:
- item = result[1][0]
- if item['state'] == "online":
- self.fileSize = item['size']
- self.fileName = item['name']
- else:
- self.offline()
- else:
- self.fail(_("Could not retrieve file info. Error code %s: %s") % (result[0], result[1]))
-
-
- def getDownloadTicket(self):
- apiUrl = "https://api.oboom.com/1/dl"
- params = {"item": self.fileId, "http_errors": 0}
- if self.premium:
- params['token'] = self.sessionToken
- else:
- params['token'] = self.downloadToken
- params['auth'] = self.downloadAuth
-
- result = self.loadUrl(apiUrl, params)
- if result[0] == 200:
- self.downloadDomain = result[1]
- self.downloadTicket = result[2]
- elif result[0] == 421:
- self.retry(wait_time=result[2] + 60, reason=_("Connection limit exceeded"))
- else:
- self.fail(_("Could not retrieve download ticket. Error code: %s") % result[0])
diff --git a/module/plugins/hoster/OneFichierCom.py b/module/plugins/hoster/OneFichierCom.py
deleted file mode 100644
index 346317271..000000000
--- a/module/plugins/hoster/OneFichierCom.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class OneFichierCom(SimpleHoster):
- __name__ = "OneFichierCom"
- __type__ = "hoster"
- __version__ = "0.74"
-
- __pattern__ = r'https?://(?:www\.)?(?:(?P<ID1>\w+)\.)?(?P<HOST>1fichier\.com|alterupload\.com|cjoint\.net|d(es)?fichiers\.com|dl4free\.com|megadl\.fr|mesfichiers\.org|piecejointe\.net|pjointe\.com|tenvoi\.com)(?:/\?(?P<ID2>\w+))?'
-
- __description__ = """1fichier.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("fragonib", "fragonib[AT]yahoo[DOT]es"),
- ("the-razer", "daniel_ AT gmx DOT net"),
- ("zoidberg", "zoidberg@mujmail.cz"),
- ("imclem", None),
- ("stickell", "l.stickell@yahoo.it"),
- ("Elrick69", "elrick69[AT]rocketmail[DOT]com"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- NAME_PATTERN = r'>FileName :</td>\s*<td.*>(?P<N>.+?)<'
- SIZE_PATTERN = r'>Size :</td>\s*<td.*>(?P<S>[\d.,]+) (?P<U>[\w^_]+)'
-
- OFFLINE_PATTERN = r'File not found !\s*<'
-
- COOKIES = [("1fichier.com", "LG", "en")]
-
- WAIT_PATTERN = r'>You must wait (\d+)'
-
-
- def setup(self):
- self.multiDL = self.premium
- self.resumeDownload = True
-
-
- def handle(self, reconnect):
- m = re.search(self.WAIT_PATTERN, self.html)
- if m:
- wait_time = int(m.group(1)) * 60
-
- self.wait(wait_time, reconnect)
- self.retry(reason="You have to wait been each free download")
-
- id = self.info['pattern']['ID1'] or self.info['pattern']['ID2']
- url, inputs = self.parseHtmlForm('action="https://1fichier.com/\?%s' % id)
-
- if not url:
- self.fail(_("Download link not found"))
-
- if "pass" in inputs:
- inputs['pass'] = self.getPassword()
-
- inputs['submit'] = "Download"
-
- self.download(url, post=inputs)
-
-
- def handleFree(self):
- return self.handle(True)
-
-
- def handlePremium(self):
- return self.handle(False)
-
-
-getInfo = create_getInfo(OneFichierCom)
diff --git a/module/plugins/hoster/OronCom.py b/module/plugins/hoster/OronCom.py
deleted file mode 100644
index 7e8423ec9..000000000
--- a/module/plugins/hoster/OronCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class OronCom(DeadHoster):
- __name__ = "OronCom"
- __type__ = "hoster"
- __version__ = "0.14"
-
- __pattern__ = r'https?://(?:www\.)?oron\.com/\w{12}'
-
- __description__ = """Oron.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("chrox", "chrox@pyload.org"),
- ("DHMH", "DHMH@pyload.org")]
-
-
-getInfo = create_getInfo(OronCom)
diff --git a/module/plugins/hoster/OverLoadMe.py b/module/plugins/hoster/OverLoadMe.py
deleted file mode 100644
index bb8d84742..000000000
--- a/module/plugins/hoster/OverLoadMe.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from random import randrange
-from urllib import unquote
-
-from module.common.json_layer import json_loads
-from module.plugins.Hoster import Hoster
-from module.utils import parseFileSize
-
-
-class OverLoadMe(Hoster):
- __name__ = "OverLoadMe"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'https?://.*overload\.me.*'
-
- __description__ = """Over-Load.me hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("marley", "marley@over-load.me")]
-
-
- def getFilename(self, url):
- try:
- name = unquote(url.rsplit("/", 1)[1])
- except IndexError:
- name = "Unknown_Filename..."
- if name.endswith("..."): #: incomplete filename, append random stuff
- name += "%s.tmp" % randrange(100, 999)
- return name
-
-
- def setup(self):
- self.chunkLimit = 5
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- if re.match(self.__pattern__, pyfile.url):
- new_url = pyfile.url
- elif not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "Over-Load")
- self.fail(_("No Over-Load account provided"))
- else:
- self.logDebug("Old URL: %s" % pyfile.url)
- data = self.account.getAccountData(self.user)
-
- page = self.load("https://api.over-load.me/getdownload.php",
- get={"auth": data['password'], "link": pyfile.url})
- data = json_loads(page)
-
- self.logDebug("Returned Data: %s" % data)
-
- if data['error'] == 1:
- self.logWarning(data['msg'])
- self.tempOffline()
- else:
- if pyfile.name is not None and pyfile.name.endswith('.tmp') and data['filename']:
- pyfile.name = data['filename']
- pyfile.size = parseFileSize(data['filesize'])
- new_url = data['downloadlink']
-
- if self.getConfig("https"):
- new_url = new_url.replace("http://", "https://")
- else:
- new_url = new_url.replace("https://", "http://")
-
- if new_url != pyfile.url:
- self.logDebug("New URL: %s" % new_url)
-
- if pyfile.name.startswith("http") or pyfile.name.startswith("Unknown") or pyfile.name.endswith('..'):
- # only use when name wasn't already set
- pyfile.name = self.getFilename(new_url)
-
- self.download(new_url, disposition=True)
-
- check = self.checkDownload(
- {"error": "<title>An error occured while processing your request</title>"})
-
- if check == "error":
- # usual this download can safely be retried
- self.retry(wait_time=60, reason=_("An error occured while generating link."))
diff --git a/module/plugins/hoster/PandaplaNet.py b/module/plugins/hoster/PandaplaNet.py
deleted file mode 100644
index 78a1ed177..000000000
--- a/module/plugins/hoster/PandaplaNet.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class PandaplaNet(DeadHoster):
- __name__ = "PandaplaNet"
- __type__ = "hoster"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?pandapla\.net/\w{12}'
-
- __description__ = """Pandapla.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")]
-
-
-getInfo = create_getInfo(PandaplaNet)
diff --git a/module/plugins/hoster/PornhostCom.py b/module/plugins/hoster/PornhostCom.py
deleted file mode 100644
index a6f415043..000000000
--- a/module/plugins/hoster/PornhostCom.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Hoster import Hoster
-
-
-class PornhostCom(Hoster):
- __name__ = "PornhostCom"
- __type__ = "hoster"
- __version__ = "0.2"
-
- __pattern__ = r'http://(?:www\.)?pornhost\.com/(\d+/\d+\.html|\d+)'
-
- __description__ = """Pornhost.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de")]
-
-
- def process(self, pyfile):
- self.download_html()
- if not self.file_exists():
- self.offline()
-
- pyfile.name = self.get_file_name()
- self.download(self.get_file_url())
-
-
- # Old interface
- def download_html(self):
- url = self.pyfile.url
- self.html = self.load(url)
-
-
- def get_file_url(self):
- """ returns the absolute downloadable filepath
- """
- if not self.html:
- self.download_html()
-
- url = re.search(r'download this file</label>.*?<a href="(.*?)"', self.html)
- if url is None:
- url = re.search(r'"(http://dl\d+\.pornhost\.com/files/.*?/.*?/.*?/.*?/.*?/.*?\..*?)"', self.html)
- if url is None:
- url = re.search(r'width: 894px; height: 675px">.*?<img src="(.*?)"', self.html)
- if url is None:
- url = re.search(r'"http://file\d+\.pornhost\.com/\d+/.*?"',
- self.html) # TODO: fix this one since it doesn't match
-
- return url.group(1).strip()
-
-
- def get_file_name(self):
- if not self.html:
- self.download_html()
-
- name = re.search(r'<title>pornhost\.com - free file hosting with a twist - gallery(.*?)</title>', self.html)
- if name is None:
- name = re.search(r'id="url" value="http://www\.pornhost\.com/(.*?)/"', self.html)
- if name is None:
- name = re.search(r'<title>pornhost\.com - free file hosting with a twist -(.*?)</title>', self.html)
- if name is None:
- name = re.search(r'"http://file\d+\.pornhost\.com/.*?/(.*?)"', self.html)
-
- name = name.group(1).strip() + ".flv"
-
- return name
-
-
- def file_exists(self):
- """ returns True or False
- """
- if not self.html:
- self.download_html()
-
- if (re.search(r'gallery not found', self.html) is not None or
- re.search(r'You will be redirected to', self.html) is not None):
- return False
- else:
- return True
diff --git a/module/plugins/hoster/PornhubCom.py b/module/plugins/hoster/PornhubCom.py
deleted file mode 100644
index d1b84771f..000000000
--- a/module/plugins/hoster/PornhubCom.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Hoster import Hoster
-
-
-class PornhubCom(Hoster):
- __name__ = "PornhubCom"
- __type__ = "hoster"
- __version__ = "0.5"
-
- __pattern__ = r'http://(?:www\.)?pornhub\.com/view_video\.php\?viewkey=\w+'
-
- __description__ = """Pornhub.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de")]
-
-
- def process(self, pyfile):
- self.download_html()
- if not self.file_exists():
- self.offline()
-
- pyfile.name = self.get_file_name()
- self.download(self.get_file_url())
-
-
- def download_html(self):
- url = self.pyfile.url
- self.html = self.load(url)
-
-
- def get_file_url(self):
- """ returns the absolute downloadable filepath
- """
- if not self.html:
- self.download_html()
-
- url = "http://www.pornhub.com//gateway.php"
- video_id = self.pyfile.url.split('=')[-1]
- # thanks to jD team for this one v
- post_data = "\x00\x03\x00\x00\x00\x01\x00\x0c\x70\x6c\x61\x79\x65\x72\x43\x6f\x6e\x66\x69\x67\x00\x02\x2f\x31\x00\x00\x00\x44\x0a\x00\x00\x00\x03\x02\x00"
- post_data += chr(len(video_id))
- post_data += video_id
- post_data += "\x02\x00\x02\x2d\x31\x02\x00\x20"
- post_data += "add299463d4410c6d1b1c418868225f7"
-
- content = self.load(url, post=str(post_data))
-
- new_content = ""
- for x in content:
- if ord(x) < 32 or ord(x) > 176:
- new_content += '#'
- else:
- new_content += x
-
- content = new_content
-
- return re.search(r'flv_url.*(http.*?)##post_roll', content).group(1)
-
-
- def get_file_name(self):
- if not self.html:
- self.download_html()
-
- m = re.search(r'<title[^>]+>([^<]+) - ', self.html)
- if m:
- name = m.group(1)
- else:
- matches = re.findall('<h1>(.*?)</h1>', self.html)
- if len(matches) > 1:
- name = matches[1]
- else:
- name = matches[0]
-
- return name + '.flv'
-
-
- def file_exists(self):
- """ returns True or False
- """
- if not self.html:
- self.download_html()
-
- if re.search(r'This video is no longer in our database or is in conversion', self.html) is not None:
- return False
- else:
- return True
diff --git a/module/plugins/hoster/PotloadCom.py b/module/plugins/hoster/PotloadCom.py
deleted file mode 100644
index d6261af3a..000000000
--- a/module/plugins/hoster/PotloadCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class PotloadCom(DeadHoster):
- __name__ = "PotloadCom"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?potload\.com/\w{12}'
-
- __description__ = """Potload.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(PotloadCom)
diff --git a/module/plugins/hoster/PremiumTo.py b/module/plugins/hoster/PremiumTo.py
deleted file mode 100644
index 9f8037d41..000000000
--- a/module/plugins/hoster/PremiumTo.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-
-from os import remove
-from os.path import exists
-from urllib import quote
-
-from module.plugins.Hoster import Hoster
-from module.utils import fs_encode
-
-
-class PremiumTo(Hoster):
- __name__ = "PremiumTo"
- __type__ = "hoster"
- __version__ = "0.11"
-
- __pattern__ = r'https?://(?:www\.)?premium\.to/.*'
-
- __description__ = """Premium.to hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- def setup(self):
- self.resumeDownload = True
- self.chunkLimit = 1
-
-
- def process(self, pyfile):
- if not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "premium.to")
- self.fail(_("No premium.to account provided"))
-
- self.logDebug("Old URL: %s" % pyfile.url)
-
- tra = self.getTraffic()
-
- #raise timeout to 2min
- self.req.setOption("timeout", 120)
-
- self.download("http://premium.to/api/getfile.php",
- get={'username': self.account.username,
- 'password': self.account.password,
- 'link' : quote(pyfile.url, "")},
- disposition=True)
-
- check = self.checkDownload({"nopremium": "No premium account available"})
-
- if check == "nopremium":
- self.retry(60, 5 * 60, "No premium account available")
-
- err = ''
- if self.req.http.code == '420':
- # Custom error code send - fail
- lastDownload = fs_encode(self.lastDownload)
-
- if exists(lastDownload):
- with open(lastDownload, "rb") as f:
- err = f.read(256).strip()
- remove(lastDownload)
- else:
- err = _('File does not exist')
-
- trb = self.getTraffic()
- self.logInfo(_("Filesize: %d, Traffic used %d, traffic left %d") % (pyfile.size, tra - trb, trb))
-
- if err:
- self.fail(err)
-
-
- def getTraffic(self):
- try:
- api_r = self.load("http://premium.to/api/straffic.php",
- get={'username': self.account.username, 'password': self.account.password})
- traffic = sum(map(int, api_r.split(';')))
- except:
- traffic = 0
- return traffic
diff --git a/module/plugins/hoster/PremiumizeMe.py b/module/plugins/hoster/PremiumizeMe.py
deleted file mode 100644
index bf00325d9..000000000
--- a/module/plugins/hoster/PremiumizeMe.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.common.json_layer import json_loads
-from module.plugins.Hoster import Hoster
-
-
-class PremiumizeMe(Hoster):
- __name__ = "PremiumizeMe"
- __type__ = "hoster"
- __version__ = "0.12"
-
- __pattern__ = r'^unmatchable$' #: Since we want to allow the user to specify the list of hoster to use we let MultiHoster.coreReady
-
- __description__ = """Premiumize.me hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Florian Franzen", "FlorianFranzen@gmail.com")]
-
-
- def process(self, pyfile):
- # Check account
- if not self.account or not self.account.canUse():
- self.logError(_("Please enter your %s account or deactivate this plugin") % "premiumize.me")
- self.fail(_("No valid premiumize.me account provided"))
-
- # In some cases hostsers do not supply us with a filename at download, so we
- # are going to set a fall back filename (e.g. for freakshare or xfileshare)
- pyfile.name = pyfile.name.split('/').pop() # Remove everthing before last slash
-
- # Correction for automatic assigned filename: Removing html at end if needed
- suffix_to_remove = ["html", "htm", "php", "php3", "asp", "shtm", "shtml", "cfml", "cfm"]
- temp = pyfile.name.split('.')
- if temp.pop() in suffix_to_remove:
- pyfile.name = ".".join(temp)
-
- # Get account data
- (user, data) = self.account.selectAccount()
-
- # Get rewritten link using the premiumize.me api v1 (see https://secure.premiumize.me/?show=api)
- data = json_loads(self.load("https://api.premiumize.me/pm-api/v1.php",
- get={'method' : "directdownloadlink",
- 'params[login]': user,
- 'params[pass]' : data['password'],
- 'params[link]' : pyfile.url}))
-
- # Check status and decide what to do
- status = data['status']
- if status == 200:
- self.download(data['result']['location'], disposition=True)
- elif status == 400:
- self.fail(_("Invalid link"))
- elif status == 404:
- self.offline()
- elif status >= 500:
- self.tempOffline()
- else:
- self.fail(data['statusmessage'])
diff --git a/module/plugins/hoster/PromptfileCom.py b/module/plugins/hoster/PromptfileCom.py
deleted file mode 100644
index af38c4e15..000000000
--- a/module/plugins/hoster/PromptfileCom.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class PromptfileCom(SimpleHoster):
- __name__ = "PromptfileCom"
- __type__ = "hoster"
- __version__ = "0.12"
-
- __pattern__ = r'https?://(?:www\.)?promptfile\.com/'
-
- __description__ = """Promptfile.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("igel", "igelkun@myopera.com")]
-
-
- INFO_PATTERN = r'<span style="[^"]*" title="[^"]*">(?P<N>.*?) \((?P<S>[\d.,]+) (?P<U>[\w^_]+)\)</span>'
- OFFLINE_PATTERN = r'<span style="[^"]*" title="File Not Found">File Not Found</span>'
-
- CHASH_PATTERN = r'<input type="hidden" name="chash" value="([^"]*)" />'
- LINK_PATTERN = r'<a href=\"(.+)\" target=\"_blank\" class=\"view_dl_link\">Download File</a>'
-
-
- def handleFree(self):
- # STAGE 1: get link to continue
- m = re.search(self.CHASH_PATTERN, self.html)
- if m is None:
- self.error(_("CHASH_PATTERN not found"))
- chash = m.group(1)
- self.logDebug("Read chash %s" % chash)
- # continue to stage2
- self.html = self.load(self.pyfile.url, decode=True, post={'chash': chash})
-
- # STAGE 2: get the direct link
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("LINK_PATTERN not found"))
-
- self.download(m.group(1), disposition=True)
-
-
-getInfo = create_getInfo(PromptfileCom)
diff --git a/module/plugins/hoster/PrzeklejPl.py b/module/plugins/hoster/PrzeklejPl.py
deleted file mode 100644
index 3a59a2c9e..000000000
--- a/module/plugins/hoster/PrzeklejPl.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class PrzeklejPl(DeadHoster):
- __name__ = "PrzeklejPl"
- __type__ = "hoster"
- __version__ = "0.11"
-
- __pattern__ = r'http://(?:www\.)?przeklej\.pl/plik/.+'
-
- __description__ = """Przeklej.pl hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(PrzeklejPl)
diff --git a/module/plugins/hoster/QuickshareCz.py b/module/plugins/hoster/QuickshareCz.py
deleted file mode 100644
index 85a4dee39..000000000
--- a/module/plugins/hoster/QuickshareCz.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from pycurl import FOLLOWLOCATION
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class QuickshareCz(SimpleHoster):
- __name__ = "QuickshareCz"
- __type__ = "hoster"
- __version__ = "0.55"
-
- __pattern__ = r'http://(?:[^/]*\.)?quickshare\.cz/stahnout-soubor/.*'
-
- __description__ = """Quickshare.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<th width="145px">Název:</th>\s*<td style="word-wrap:break-word;">(?P<N>[^<]+)</td>'
- SIZE_PATTERN = r'<th>Velikost:</th>\s*<td>(?P<S>[\d.,]+) (?P<U>[\w^_]+)</td>'
- OFFLINE_PATTERN = r'<script type="text/javascript">location\.href=\'/chyba\';</script>'
-
-
- def process(self, pyfile):
- self.html = self.load(pyfile.url, decode=True)
- self.getFileInfo()
-
- # parse js variables
- self.jsvars = dict((x, y.strip("'")) for x, y in re.findall(r"var (\w+) = ([\d.]+|'[^']*')", self.html))
- self.logDebug(self.jsvars)
- pyfile.name = self.jsvars['ID3']
-
- # determine download type - free or premium
- if self.premium:
- if 'UU_prihlasen' in self.jsvars:
- if self.jsvars['UU_prihlasen'] == '0':
- self.logWarning(_("User not logged in"))
- self.relogin(self.user)
- self.retry()
- elif float(self.jsvars['UU_kredit']) < float(self.jsvars['kredit_odecet']):
- self.logWarning(_("Not enough credit left"))
- self.premium = False
-
- if self.premium:
- self.handlePremium()
- else:
- self.handleFree()
-
- check = self.checkDownload({"err": re.compile(r"\AChyba!")}, max_size=100)
- if check == "err":
- self.fail(_("File not m or plugin defect"))
-
-
- def handleFree(self):
- # get download url
- download_url = '%s/download.php' % self.jsvars['server']
- data = dict((x, self.jsvars[x]) for x in self.jsvars if x in ("ID1", "ID2", "ID3", "ID4"))
- self.logDebug("FREE URL1:" + download_url, data)
-
- self.req.http.c.setopt(FOLLOWLOCATION, 0)
- self.load(download_url, post=data)
- self.header = self.req.http.header
- self.req.http.c.setopt(FOLLOWLOCATION, 1)
-
- m = re.search(r'Location\s*:\s*(.+)', self.header, re.I)
- if m is None:
- self.fail(_("File not found"))
- download_url = m.group(1).rstrip() #@TODO: Remove .rstrip() in 0.4.10
- self.logDebug("FREE URL2:" + download_url)
-
- # check errors
- m = re.search(r'/chyba/(\d+)', download_url)
- if m:
- if m.group(1) == '1':
- self.retry(60, 2 * 60, "This IP is already downloading")
- elif m.group(1) == '2':
- self.retry(60, 60, "No free slots available")
- else:
- self.fail(_("Error %d") % m.group(1))
-
- # download file
- self.download(download_url)
-
-
- def handlePremium(self):
- download_url = '%s/download_premium.php' % self.jsvars['server']
- data = dict((x, self.jsvars[x]) for x in self.jsvars if x in ("ID1", "ID2", "ID4", "ID5"))
- self.download(download_url, get=data)
-
-
-getInfo = create_getInfo(QuickshareCz)
diff --git a/module/plugins/hoster/RPNetBiz.py b/module/plugins/hoster/RPNetBiz.py
deleted file mode 100644
index c1e536d39..000000000
--- a/module/plugins/hoster/RPNetBiz.py
+++ /dev/null
@@ -1,85 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Hoster import Hoster
-from module.common.json_layer import json_loads
-
-
-class RPNetBiz(Hoster):
- __name__ = "RPNetBiz"
- __type__ = "hoster"
- __version__ = "0.1"
-
- __description__ = """RPNet.biz hoster plugin"""
- __license__ = "GPLv3"
-
- __pattern__ = r'https?://.*rpnet\.biz'
- __authors__ = [("Dman", "dmanugm@gmail.com")]
-
-
- def setup(self):
- self.chunkLimit = -1
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- if re.match(self.__pattern__, pyfile.url):
- link_status = {'generated': pyfile.url}
- elif not self.account:
- # Check account
- self.logError(_("Please enter your %s account or deactivate this plugin") % "rpnet")
- self.fail(_("No rpnet account provided"))
- else:
- (user, data) = self.account.selectAccount()
-
- self.logDebug("Original URL: %s" % pyfile.url)
- # Get the download link
- res = self.load("https://premium.rpnet.biz/client_api.php",
- get={"username": user,
- "password": data['password'],
- "action": "generate",
- "links": pyfile.url})
-
- self.logDebug("JSON data: %s" % res)
- link_status = json_loads(res)['links'][0] # get the first link... since we only queried one
-
- # Check if we only have an id as a HDD link
- if 'id' in link_status:
- self.logDebug("Need to wait at least 30 seconds before requery")
- self.setWait(30) # wait for 30 seconds
- self.wait()
- # Lets query the server again asking for the status on the link,
- # we need to keep doing this until we reach 100
- max_tries = 30
- my_try = 0
- while (my_try <= max_tries):
- self.logDebug("Try: %d ; Max Tries: %d" % (my_try, max_tries))
- res = self.load("https://premium.rpnet.biz/client_api.php",
- get={"username": user,
- "password": data['password'],
- "action": "downloadInformation",
- "id": link_status['id']})
- self.logDebug("JSON data hdd query: %s" % res)
- download_status = json_loads(res)['download']
-
- if download_status['status'] == '100':
- link_status['generated'] = download_status['rpnet_link']
- self.logDebug("Successfully downloaded to rpnet HDD: %s" % link_status['generated'])
- break
- else:
- self.logDebug("At %s%% for the file download" % download_status['status'])
-
- self.setWait(30)
- self.wait()
- my_try += 1
-
- if my_try > max_tries: # We went over the limit!
- self.fail(_("Waited for about 15 minutes for download to finish but failed"))
-
- if 'generated' in link_status:
- self.download(link_status['generated'], disposition=True)
- elif 'error' in link_status:
- self.fail(link_status['error'])
- else:
- self.fail(_("Something went wrong, not supposed to enter here"))
diff --git a/module/plugins/hoster/RapidfileshareNet.py b/module/plugins/hoster/RapidfileshareNet.py
deleted file mode 100644
index ae53411c3..000000000
--- a/module/plugins/hoster/RapidfileshareNet.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class RapidfileshareNet(XFSHoster):
- __name__ = "RapidfileshareNet"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?rapidfileshare\.net/\w{12}'
-
- __description__ = """Rapidfileshare.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("guidobelix", "guidobelix@hotmail.it")]
-
-
- HOSTER_DOMAIN = "rapidfileshare.net"
-
- NAME_PATTERN = r'<input type="hidden" name="fname" value="(?P<N>.+?)">'
- SIZE_PATTERN = r'>http://www.rapidfileshare.net/\w+?</font> \((?P<S>[\d.,]+) (?P<U>[\w^_]+)\)</font>'
-
- OFFLINE_PATTERN = r'>No such file with this filename'
- TEMP_OFFLINE_PATTERN = r'The page may have been renamed, removed or be temporarily unavailable.<'
-
-
- def handlePremium(self):
- self.fail(_("Premium download not implemented"))
-
-
-getInfo = create_getInfo(RapidfileshareNet)
diff --git a/module/plugins/hoster/RapidgatorNet.py b/module/plugins/hoster/RapidgatorNet.py
deleted file mode 100644
index 7788616a8..000000000
--- a/module/plugins/hoster/RapidgatorNet.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from pycurl import HTTPHEADER
-
-from module.common.json_layer import json_loads
-from module.network.HTTPRequest import BadHeader
-from module.plugins.hoster.UnrestrictLi import secondsToMidnight
-from module.plugins.internal.CaptchaService import AdsCaptcha, ReCaptcha, SolveMedia
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class RapidgatorNet(SimpleHoster):
- __name__ = "RapidgatorNet"
- __type__ = "hoster"
- __version__ = "0.26"
-
- __pattern__ = r'http://(?:www\.)?(rapidgator\.net|rg\.to)/file/\w+'
-
- __description__ = """Rapidgator.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("chrox", None),
- ("stickell", "l.stickell@yahoo.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- API_URL = "http://rapidgator.net/api/file"
-
- COOKIES = [("rapidgator.net", "lang", "en")]
-
- NAME_PATTERN = r'<title>Download file (?P<N>.*)</title>'
- SIZE_PATTERN = r'File size:\s*<strong>(?P<S>[\d.,]+) (?P<U>[\w^_]+)</strong>'
- OFFLINE_PATTERN = r'>(File not found|Error 404)'
-
- JSVARS_PATTERN = r'\s+var\s*(startTimerUrl|getDownloadUrl|captchaUrl|fid|secs)\s*=\s*\'?(.*?)\'?;'
- PREMIUM_ONLY_ERROR_PATTERN = r'You can download files up to|This file can be downloaded by premium only<'
- DOWNLOAD_LIMIT_ERROR_PATTERN = r'You have reached your (daily|hourly) downloads limit'
- WAIT_PATTERN = r'(?:Delay between downloads must be not less than|Try again in)\s*(\d+)\s*(hour|min)'
- LINK_PATTERN = r'return \'(http://\w+.rapidgator.net/.*)\';'
-
- RECAPTCHA_PATTERN = r'"http://api\.recaptcha\.net/challenge\?k=(.*?)"'
- ADSCAPTCHA_PATTERN = r'(http://api\.adscaptcha\.com/Get\.aspx[^"\']*)'
- SOLVEMEDIA_PATTERN = r'http://api\.solvemedia\.com/papi/challenge\.script\?k=(.*?)"'
-
-
- def setup(self):
- if self.account:
- self.sid = self.account.getAccountData(self.user).get('SID', None)
- else:
- self.sid = None
-
- if self.sid:
- self.premium = True
-
- self.resumeDownload = self.multiDL = self.premium
- self.chunkLimit = 1
-
-
- def api_response(self, cmd):
- try:
- json = self.load('%s/%s' % (self.API_URL, cmd),
- get={'sid': self.sid,
- 'url': self.pyfile.url}, decode=True)
- self.logDebug("API:%s" % cmd, json, "SID: %s" % self.sid)
- json = json_loads(json)
- status = json['response_status']
- msg = json['response_details']
-
- except BadHeader, e:
- self.logError("API: %s" % cmd, e, "SID: %s" % self.sid)
- status = e.code
- msg = e
-
- if status == 200:
- return json['response']
-
- elif status == 423:
- self.account.empty(self.user)
- self.retry()
-
- else:
- self.account.relogin(self.user)
- self.retry(wait_time=60)
-
-
- def handlePremium(self):
- #self.logDebug("ACCOUNT_DATA", self.account.getAccountData(self.user))
- self.api_data = self.api_response('info')
- self.api_data['md5'] = self.api_data['hash']
- self.pyfile.name = self.api_data['filename']
- self.pyfile.size = self.api_data['size']
- url = self.api_response('download')['url']
- self.download(url)
-
-
- def handleFree(self):
- self.checkFree()
-
- jsvars = dict(re.findall(self.JSVARS_PATTERN, self.html))
- self.logDebug(jsvars)
-
- self.req.http.lastURL = self.pyfile.url
- self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With: XMLHttpRequest"])
-
- url = "http://rapidgator.net%s?fid=%s" % (
- jsvars.get('startTimerUrl', '/download/AjaxStartTimer'), jsvars['fid'])
- jsvars.update(self.getJsonResponse(url))
-
- self.wait(int(jsvars.get('secs', 45)), False)
-
- url = "http://rapidgator.net%s?sid=%s" % (
- jsvars.get('getDownloadUrl', '/download/AjaxGetDownload'), jsvars['sid'])
- jsvars.update(self.getJsonResponse(url))
-
- self.req.http.lastURL = self.pyfile.url
- self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With:"])
-
- url = "http://rapidgator.net%s" % jsvars.get('captchaUrl', '/download/captcha')
- self.html = self.load(url)
-
- for _i in xrange(5):
- m = re.search(self.LINK_PATTERN, self.html)
- if m:
- link = m.group(1)
- self.logDebug(link)
- self.download(link, disposition=True)
- break
- else:
- captcha, captcha_key = self.getCaptcha()
- captcha_challenge, captcha_response = captcha.challenge(captcha_key)
-
- self.html = self.load(url, post={
- "DownloadCaptchaForm[captcha]": "",
- "adcopy_challenge": captcha_challenge,
- "adcopy_response": captcha_response
- })
-
- if "The verification code is incorrect" in self.html:
- self.invalidCaptcha()
- else:
- self.correctCaptcha()
- else:
- self.error(_("Download link"))
-
-
- def getCaptcha(self):
- m = re.search(self.ADSCAPTCHA_PATTERN, self.html)
- if m:
- captcha_key = m.group(1)
- captcha = AdsCaptcha(self)
- else:
- m = re.search(self.RECAPTCHA_PATTERN, self.html)
- if m:
- captcha_key = m.group(1)
- captcha = ReCaptcha(self)
- else:
- m = re.search(self.SOLVEMEDIA_PATTERN, self.html)
- if m:
- captcha_key = m.group(1)
- captcha = SolveMedia(self)
- else:
- self.error(_("Captcha"))
-
- return captcha, captcha_key
-
-
- def checkFree(self):
- m = re.search(self.PREMIUM_ONLY_ERROR_PATTERN, self.html)
- if m:
- self.fail(_("Premium account needed for download"))
- else:
- m = re.search(self.WAIT_PATTERN, self.html)
-
- if m:
- wait_time = int(m.group(1)) * {"hour": 60, "min": 1}[m.group(2)]
- else:
- m = re.search(self.DOWNLOAD_LIMIT_ERROR_PATTERN, self.html)
- if m is None:
- return
- elif m.group(1) == "daily":
- self.logWarning(_("You have reached your daily downloads limit for today"))
- wait_time = secondsToMidnight(gmt=2)
- else:
- wait_time = 1 * 60 * 60
-
- self.logDebug("Waiting %d minutes" % wait_time / 60)
- self.wait(wait_time, True)
- self.retry()
-
-
- def getJsonResponse(self, url):
- res = self.load(url, decode=True)
- if not res.startswith('{'):
- self.retry()
- self.logDebug(url, res)
- return json_loads(res)
-
-
-getInfo = create_getInfo(RapidgatorNet)
diff --git a/module/plugins/hoster/RarefileNet.py b/module/plugins/hoster/RarefileNet.py
deleted file mode 100644
index 987028c8f..000000000
--- a/module/plugins/hoster/RarefileNet.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class RarefileNet(XFSHoster):
- __name__ = "RarefileNet"
- __type__ = "hoster"
- __version__ = "0.08"
-
- __pattern__ = r'http://(?:www\.)?rarefile\.net/\w{12}'
-
- __description__ = """Rarefile.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- HOSTER_DOMAIN = "rarefile.net"
-
- NAME_PATTERN = r'<font color="red">(?P<N>.+?)<'
- SIZE_PATTERN = r'>Size : (?P<S>[\d.,]+) (?P<U>[\w^_]+)'
-
- LINK_PATTERN = r'<a href="(?P<link>[^"]+)">(?P=link)</a>'
-
-
-getInfo = create_getInfo(RarefileNet)
diff --git a/module/plugins/hoster/RealdebridCom.py b/module/plugins/hoster/RealdebridCom.py
deleted file mode 100644
index cc6dd49c3..000000000
--- a/module/plugins/hoster/RealdebridCom.py
+++ /dev/null
@@ -1,94 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from random import randrange
-from urllib import quote, unquote
-from time import time
-
-from module.common.json_layer import json_loads
-from module.plugins.Hoster import Hoster
-from module.utils import parseFileSize
-
-
-class RealdebridCom(Hoster):
- __name__ = "RealdebridCom"
- __type__ = "hoster"
- __version__ = "0.53"
-
- __pattern__ = r'https?://(?:[^/]*\.)?real-debrid\..*'
-
- __description__ = """Real-Debrid.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Devirex Hazzard", "naibaf_11@yahoo.de")]
-
-
- def getFilename(self, url):
- try:
- name = unquote(url.rsplit("/", 1)[1])
- except IndexError:
- name = "Unknown_Filename..."
- if not name or name.endswith(".."): #: incomplete filename, append random stuff
- name += "%s.tmp" % randrange(100, 999)
- return name
-
-
- def setup(self):
- self.chunkLimit = 3
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- if re.match(self.__pattern__, pyfile.url):
- new_url = pyfile.url
- elif not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "Real-debrid")
- self.fail(_("No Real-debrid account provided"))
- else:
- self.logDebug("Old URL: %s" % pyfile.url)
- password = self.getPassword().splitlines()
- if not password:
- password = ""
- else:
- password = password[0]
-
- data = json_loads(self.load("https://real-debrid.com/ajax/unrestrict.php",
- get={'lang' : "en",
- 'link' : quote(pyfile.url, ""),
- 'password': password,
- 'time' : int(time() * 1000)}))
-
- self.logDebug("Returned Data: %s" % data)
-
- if data['error'] != 0:
- if data['message'] == "Your file is unavailable on the hoster.":
- self.offline()
- else:
- self.logWarning(data['message'])
- self.tempOffline()
- else:
- if pyfile.name is not None and pyfile.name.endswith('.tmp') and data['file_name']:
- pyfile.name = data['file_name']
- pyfile.size = parseFileSize(data['file_size'])
- new_url = data['generated_links'][0][-1]
-
- if self.getConfig("https"):
- new_url = new_url.replace("http://", "https://")
- else:
- new_url = new_url.replace("https://", "http://")
-
- if new_url != pyfile.url:
- self.logDebug("New URL: %s" % new_url)
-
- if pyfile.name.startswith("http") or pyfile.name.startswith("Unknown") or pyfile.name.endswith('..'):
- #only use when name wasnt already set
- pyfile.name = self.getFilename(new_url)
-
- self.download(new_url, disposition=True)
-
- check = self.checkDownload(
- {"error": "<title>An error occured while processing your request</title>"})
-
- if check == "error":
- #usual this download can safely be retried
- self.retry(wait_time=60, reason=_("An error occured while generating link"))
diff --git a/module/plugins/hoster/RedtubeCom.py b/module/plugins/hoster/RedtubeCom.py
deleted file mode 100644
index 5bf624273..000000000
--- a/module/plugins/hoster/RedtubeCom.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Hoster import Hoster
-from module.unescape import unescape
-
-
-class RedtubeCom(Hoster):
- __name__ = "RedtubeCom"
- __type__ = "hoster"
- __version__ = "0.2"
-
- __pattern__ = r'http://(?:www\.)?redtube\.com/\d+'
-
- __description__ = """Redtube.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de")]
-
-
- def process(self, pyfile):
- self.download_html()
- if not self.file_exists():
- self.offline()
-
- pyfile.name = self.get_file_name()
- self.download(self.get_file_url())
-
-
- def download_html(self):
- url = self.pyfile.url
- self.html = self.load(url)
-
-
- def get_file_url(self):
- """ returns the absolute downloadable filepath
- """
- if not self.html:
- self.download_html()
-
- file_url = unescape(re.search(r'hashlink=(http.*?)"', self.html).group(1))
-
- return file_url
-
-
- def get_file_name(self):
- if not self.html:
- self.download_html()
-
- return re.search('<title>(.*?)- RedTube - Free Porn Videos</title>', self.html).group(1).strip() + ".flv"
-
-
- def file_exists(self):
- """ returns True or False
- """
- if not self.html:
- self.download_html()
-
- if re.search(r'This video has been removed.', self.html) is not None:
- return False
- else:
- return True
diff --git a/module/plugins/hoster/RehostTo.py b/module/plugins/hoster/RehostTo.py
deleted file mode 100644
index 7cde01025..000000000
--- a/module/plugins/hoster/RehostTo.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from urllib import quote, unquote
-
-from module.plugins.Hoster import Hoster
-
-
-class RehostTo(Hoster):
- __name__ = "RehostTo"
- __type__ = "hoster"
- __version__ = "0.13"
-
- __pattern__ = r'https?://.*rehost\.to\..*'
-
- __description__ = """Rehost.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org")]
-
-
- def getFilename(self, url):
- return unquote(url.rsplit("/", 1)[1])
-
-
- def setup(self):
- self.chunkLimit = 1
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- if not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "rehost.to")
- self.fail(_("No rehost.to account provided"))
-
- data = self.account.getAccountInfo(self.user)
- long_ses = data['long_ses']
-
- self.logDebug("Rehost.to: Old URL: %s" % pyfile.url)
-
- #raise timeout to 2min
- self.req.setOption("timeout", 120)
-
- self.download("http://rehost.to/process_download.php",
- get={'user': "cookie", 'pass': long_ses, 'dl': quote(pyfile.url, "")},
- disposition=True)
diff --git a/module/plugins/hoster/RemixshareCom.py b/module/plugins/hoster/RemixshareCom.py
deleted file mode 100644
index ed171895e..000000000
--- a/module/plugins/hoster/RemixshareCom.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://remixshare.com/download/p946u
-#
-# Note:
-# The remixshare.com website is very very slow, so
-# if your download not starts because of pycurl timeouts:
-# Adjust timeouts in /usr/share/pyload/module/network/HTTPRequest.py
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class RemixshareCom(SimpleHoster):
- __name__ = "RemixshareCom"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'https?://remixshare\.com/(download|dl)/\w+'
-
- __description__ = """Remixshare.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zapp-brannigan", "fuerst.reinje@web.de"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- INFO_PATTERN = r'title=\'.+?\'>(?P<N>.+?)</span><span class=\'light2\'>&nbsp;\((?P<S>\d+)&nbsp;(?P<U>[\w^_]+)\)<'
- OFFLINE_PATTERN = r'<h1>Ooops!<'
-
- LINK_PATTERN = r'(http://remixshare\.com/downloadfinal/.+?)"'
- TOKEN_PATTERN = r'var acc = (\d+)'
- WAIT_PATTERN = r'var XYZ = r"(\d+)"'
-
-
- def setup(self):
- self.multiDL = True
- self.chunkLimit = 1
-
-
- def handleFree(self):
- b = re.search(self.LINK_PATTERN, self.html)
- if not b:
- self.error(_("Cannot parse download url"))
- c = re.search(self.TOKEN_PATTERN, self.html)
- if not c:
- self.error(_("Cannot parse file token"))
- dl_url = b.group(1) + c.group(1)
-
- #Check if we have to wait
- seconds = re.search(self.WAIT_PATTERN, self.html)
- if seconds:
- self.logDebug("Wait " + seconds.group(1))
- self.wait(int(seconds.group(1)))
-
- # Finally start downloading...
- self.download(dl_url, disposition=True)
-
-
-getInfo = create_getInfo(RemixshareCom)
diff --git a/module/plugins/hoster/RgHostNet.py b/module/plugins/hoster/RgHostNet.py
deleted file mode 100644
index aa4830563..000000000
--- a/module/plugins/hoster/RgHostNet.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class RgHostNet(SimpleHoster):
- __name__ = "RgHostNet"
- __type__ = "hoster"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?rghost\.net/\d+(?:r=\d+)?'
-
- __description__ = """RgHost.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("z00nx", "z00nx0@gmail.com")]
-
-
- INFO_PATTERN = r'<h1>\s+(<a[^>]+>)?(?P<N>[^<]+)(</a>)?\s+<small[^>]+>\s+\((?P<S>[^)]+)\)\s+</small>\s+</h1>'
- OFFLINE_PATTERN = r'File is deleted|this page is not found'
-
- LINK_FREE_PATTERN = r'<a\s+href="([^"]+)"\s+class="btn\s+large\s+download"[^>]+>Download</a>'
-
-
-getInfo = create_getInfo(RgHostNet)
diff --git a/module/plugins/hoster/RyushareCom.py b/module/plugins/hoster/RyushareCom.py
deleted file mode 100644
index d2e4489a6..000000000
--- a/module/plugins/hoster/RyushareCom.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://ryushare.com/cl0jy8ric2js/random.bin
-
-import re
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-from module.plugins.internal.CaptchaService import SolveMedia
-
-
-class RyushareCom(XFSHoster):
- __name__ = "RyushareCom"
- __type__ = "hoster"
- __version__ = "0.20"
-
- __pattern__ = r'http://(?:www\.)?ryushare\.com/\w+'
-
- __description__ = """Ryushare.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it"),
- ("quareevo", "quareevo@arcor.de")]
-
-
- HOSTER_DOMAIN = "ryushare.com"
-
- SIZE_PATTERN = r'You have requested <font color="red">[^<]+</font> \((?P<S>[\d.,]+) (?P<U>[\w^_]+)'
-
- WAIT_PATTERN = r'You have to wait ((?P<hour>\d+) hour[s]?, )?((?P<min>\d+) minute[s], )?(?P<sec>\d+) second[s]'
- LINK_PATTERN = r'<a href="([^"]+)">Click here to download<'
-
-
- def getDownloadLink(self):
- retry = False
- self.html = self.load(self.pyfile.url)
- action, inputs = self.parseHtmlForm(input_names={"op": re.compile("^download")})
- if "method_premium" in inputs:
- del inputs['method_premium']
-
- self.html = self.load(self.pyfile.url, post=inputs)
- action, inputs = self.parseHtmlForm('F1')
-
- self.setWait(65)
- # Wait 1 hour
- if "You have reached the download-limit" in self.html:
- self.setWait(1 * 60 * 60, True)
- retry = True
-
- m = re.search(self.WAIT_PATTERN, self.html)
- if m:
- wait = m.groupdict(0)
- waittime = int(wait['hour']) * 60 * 60 + int(wait['min']) * 60 + int(wait['sec'])
- self.setWait(waittime, True)
- retry = True
-
- self.wait()
- if retry:
- self.retry()
-
- for _i in xrange(5):
- solvemedia = SolveMedia(self)
- challenge, response = solvemedia.challenge()
-
- inputs['adcopy_challenge'] = challenge
- inputs['adcopy_response'] = response
-
- self.html = self.load(self.pyfile.url, post=inputs)
- if "WRONG CAPTCHA" in self.html:
- self.invalidCaptcha()
- else:
- self.correctCaptcha()
- break
- else:
- self.fail(_("You have entered 5 invalid captcha codes"))
-
- if "Click here to download" in self.html:
- return re.search(r'<a href="([^"]+)">Click here to download</a>', self.html).group(1)
-
-
-getInfo = create_getInfo(RyushareCom)
diff --git a/module/plugins/hoster/SecureUploadEu.py b/module/plugins/hoster/SecureUploadEu.py
deleted file mode 100644
index 64e6456a9..000000000
--- a/module/plugins/hoster/SecureUploadEu.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class SecureUploadEu(XFSHoster):
- __name__ = "SecureUploadEu"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'https?://(?:www\.)?secureupload\.eu/\w{12}'
-
- __description__ = """SecureUpload.eu hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("z00nx", "z00nx0@gmail.com")]
-
-
- HOSTER_DOMAIN = "secureupload.eu"
-
- INFO_PATTERN = r'<h3>Downloading (?P<N>[^<]+) \((?P<S>[^<]+)\)</h3>'
-
-
-getInfo = create_getInfo(SecureUploadEu)
diff --git a/module/plugins/hoster/SendmywayCom.py b/module/plugins/hoster/SendmywayCom.py
deleted file mode 100644
index 4254922fc..000000000
--- a/module/plugins/hoster/SendmywayCom.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class SendmywayCom(XFSHoster):
- __name__ = "SendmywayCom"
- __type__ = "hoster"
- __version__ = "0.04"
-
- __pattern__ = r'http://(?:www\.)?sendmyway\.com/\w{12}'
-
- __description__ = """SendMyWay hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- HOSTER_DOMAIN = "sendmyway.com"
-
- NAME_PATTERN = r'<p class="file-name" ><.*?>\s*(?P<N>.+)'
- SIZE_PATTERN = r'<small>\((?P<S>\d+) bytes\)</small>'
-
-
-getInfo = create_getInfo(SendmywayCom)
diff --git a/module/plugins/hoster/SendspaceCom.py b/module/plugins/hoster/SendspaceCom.py
deleted file mode 100644
index b589bb021..000000000
--- a/module/plugins/hoster/SendspaceCom.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class SendspaceCom(SimpleHoster):
- __name__ = "SendspaceCom"
- __type__ = "hoster"
- __version__ = "0.14"
-
- __pattern__ = r'http://(?:www\.)?sendspace\.com/file/.*'
-
- __description__ = """Sendspace.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<h2 class="bgray">\s*<(?:b|strong)>(?P<N>[^<]+)</'
- SIZE_PATTERN = r'<div class="file_description reverse margin_center">\s*<b>File Size:</b>\s*(?P<S>[\d.,]+)(?P<U>[\w^_]+)\s*</div>'
- OFFLINE_PATTERN = r'<div class="msg error" style="cursor: default">Sorry, the file you requested is not available.</div>'
-
- LINK_PATTERN = r'<a id="download_button" href="([^"]+)"'
- CAPTCHA_PATTERN = r'<td><img src="(/captchas/captcha\.php?captcha=([^"]+))"></td>'
- USER_CAPTCHA_PATTERN = r'<td><img src="/captchas/captcha\.php?user=([^"]+))"></td>'
-
-
- def handleFree(self):
- params = {}
- for _i in xrange(3):
- m = re.search(self.LINK_PATTERN, self.html)
- if m:
- if 'captcha_hash' in params:
- self.correctCaptcha()
- download_url = m.group(1)
- break
-
- m = re.search(self.CAPTCHA_PATTERN, self.html)
- if m:
- if 'captcha_hash' in params:
- self.invalidCaptcha()
- captcha_url1 = "http://www.sendspace.com/" + m.group(1)
- m = re.search(self.USER_CAPTCHA_PATTERN, self.html)
- captcha_url2 = "http://www.sendspace.com/" + m.group(1)
- params = {'captcha_hash': m.group(2),
- 'captcha_submit': 'Verify',
- 'captcha_answer': self.decryptCaptcha(captcha_url1) + " " + self.decryptCaptcha(captcha_url2)}
- else:
- params = {'download': "Regular Download"}
-
- self.logDebug(params)
- self.html = self.load(self.pyfile.url, post=params)
- else:
- self.fail(_("Download link not found"))
-
- self.download(download_url)
-
-
-getInfo = create_getInfo(SendspaceCom)
diff --git a/module/plugins/hoster/Share4webCom.py b/module/plugins/hoster/Share4webCom.py
deleted file mode 100644
index 4634917ad..000000000
--- a/module/plugins/hoster/Share4webCom.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.hoster.UnibytesCom import UnibytesCom
-from module.plugins.internal.SimpleHoster import create_getInfo
-
-
-class Share4webCom(UnibytesCom):
- __name__ = "Share4webCom"
- __type__ = "hoster"
- __version__ = "0.11"
-
- __pattern__ = r'https?://(?:www\.)?share4web\.com/get/\w+'
-
- __description__ = """Share4web.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- HOSTER_DOMAIN = "share4web.com"
-
-
-getInfo = create_getInfo(UnibytesCom)
diff --git a/module/plugins/hoster/Share76Com.py b/module/plugins/hoster/Share76Com.py
deleted file mode 100644
index 1ac8a64e7..000000000
--- a/module/plugins/hoster/Share76Com.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class Share76Com(DeadHoster):
- __name__ = "Share76Com"
- __type__ = "hoster"
- __version__ = "0.04"
-
- __pattern__ = r'http://(?:www\.)?share76\.com/\w{12}'
-
- __description__ = """Share76.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = []
-
-
-getInfo = create_getInfo(Share76Com)
diff --git a/module/plugins/hoster/ShareFilesCo.py b/module/plugins/hoster/ShareFilesCo.py
deleted file mode 100644
index 4996014d8..000000000
--- a/module/plugins/hoster/ShareFilesCo.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class ShareFilesCo(DeadHoster):
- __name__ = "ShareFilesCo"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?sharefiles\.co/\w{12}'
-
- __description__ = """Sharefiles.co hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(ShareFilesCo)
diff --git a/module/plugins/hoster/SharebeesCom.py b/module/plugins/hoster/SharebeesCom.py
deleted file mode 100644
index c0fd22c91..000000000
--- a/module/plugins/hoster/SharebeesCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class SharebeesCom(DeadHoster):
- __name__ = "SharebeesCom"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?sharebees\.com/\w{12}'
-
- __description__ = """ShareBees hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(SharebeesCom)
diff --git a/module/plugins/hoster/ShareonlineBiz.py b/module/plugins/hoster/ShareonlineBiz.py
deleted file mode 100644
index 1cb651b12..000000000
--- a/module/plugins/hoster/ShareonlineBiz.py
+++ /dev/null
@@ -1,191 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import time
-from urllib import unquote
-from urlparse import urlparse
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class ShareonlineBiz(SimpleHoster):
- __name__ = "ShareonlineBiz"
- __type__ = "hoster"
- __version__ = "0.44"
-
- __pattern__ = r'https?://(?:www\.)?(share-online\.biz|egoshare\.com)/(download\.php\?id=|dl/)(?P<ID>\w+)'
-
- __description__ = """Shareonline.biz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("spoob", "spoob@pyload.org"),
- ("mkaay", "mkaay@mkaay.de"),
- ("zoidberg", "zoidberg@mujmail.cz"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- URL_REPLACEMENTS = [(__pattern__ + ".*", "http://www.share-online.biz/dl/\g<ID>")]
-
- RECAPTCHA_KEY = "6LdatrsSAAAAAHZrB70txiV5p-8Iv8BtVxlTtjKX"
-
- ERROR_INFO_PATTERN = r'<p class="b">Information:</p>\s*<div>\s*<strong>(.*?)</strong>'
-
-
- @classmethod
- def getInfo(cls, url="", html=""):
- info = {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 3 if url else 1, 'url': url}
-
- if url:
- info['pattern'] = re.match(cls.__pattern__, url).groupdict()
-
- field = getURL("http://api.share-online.biz/linkcheck.php",
- get={'md5': "1"},
- post={'links': info['pattern']['ID']},
- decode=True).split(";")
-
- if field[1] == "OK":
- info['fileid'] = field[0]
- info['status'] = 2
- info['name'] = field[2]
- info['size'] = field[3] #: in bytes
- info['md5'] = field[4].strip().lower().replace("\n\n", "") #: md5
-
- elif field[1] in ("DELETED", "NOT FOUND"):
- info['status'] = 1
-
- return info
-
-
- def setup(self):
- self.resumeDownload = self.premium
- self.multiDL = False
-
-
- def handleCaptcha(self):
- recaptcha = ReCaptcha(self)
-
- for _i in xrange(5):
- challenge, response = recaptcha.challenge(self.RECAPTCHA_KEY)
-
- m = re.search(r'var wait=(\d+);', self.html)
- self.setWait(int(m.group(1)) if m else 30)
-
- res = self.load("%s/free/captcha/%d" % (self.pyfile.url, int(time() * 1000)),
- post={'dl_free' : "1",
- 'recaptcha_challenge_field': challenge,
- 'recaptcha_response_field' : response})
- if not res == '0':
- self.correctCaptcha()
- return res
- else:
- self.invalidCaptcha()
- else:
- self.invalidCaptcha()
- self.fail(_("No valid captcha solution received"))
-
-
- def handleFree(self):
- self.html = self.load(self.pyfile.url, cookies=True) #: refer, stuff
-
- self.wait(3)
-
- self.html = self.load("%s/free/" % self.pyfile.url, post={"dl_free": "1", "choice": "free"}, decode=True)
-
- self.checkErrors()
-
- res = self.handleCaptcha()
-
- download_url = res.decode("base64")
-
- if not download_url.startswith("http://"):
- self.error(_("Wrong download url"))
-
- self.wait()
-
- self.download(download_url)
-
-
- def checkFile(self):
- # check download
- check = self.checkDownload({
- 'empty' : re.compile(r"^$"),
- 'cookie': re.compile(r'<div id="dl_failure"'),
- 'fail' : re.compile(r"<title>Share-Online")
- })
-
- if check == "empty":
- self.fail(_("Empty file"))
-
- elif check == "cookie":
- self.invalidCaptcha()
- self.retry(5, 60, _("Cookie failure"))
-
- elif check == "fail":
- self.invalidCaptcha()
- self.retry(5, 5 * 60, _("Download failed"))
-
-
- def handlePremium(self): #: should be working better loading (account) api internally
- self.account.getAccountInfo(self.user, True)
-
- html = self.load("http://api.share-online.biz/account.php",
- {"username": self.user, "password": self.account.accounts[self.user]['password'],
- "act": "download", "lid": self.info['fileid']})
-
- self.api_data = dlinfo = {}
-
- for line in html.splitlines():
- key, value = line.split(": ")
- dlinfo[key.lower()] = value
-
- self.logDebug(dlinfo)
-
- if not dlinfo['status'] == "online":
- self.offline()
- else:
- self.pyfile.name = dlinfo['name']
- self.pyfile.size = int(dlinfo['size'])
-
- dlLink = dlinfo['url']
-
- if dlLink == "server_under_maintenance":
- self.tempOffline()
- else:
- self.multiDL = True
- self.download(dlLink)
-
-
- def checkErrors(self):
- m = re.search(r"/failure/(.*?)/1", self.req.lastEffectiveURL)
- if m is None:
- self.info.pop('error', None)
- return
-
- errmsg = m.group(1).lower()
-
- try:
- self.logError(errmsg, re.search(self.ERROR_INFO_PATTERN, self.html).group(1))
- except:
- self.logError("Unknown error occurred", errmsg)
-
- if errmsg is "invalid":
- self.fail(_("File not available"))
-
- elif errmsg in ("freelimit", "size", "proxy"):
- self.fail(_("Premium account needed"))
-
- elif errmsg in ("expired", "server"):
- self.retry(wait_time=600, reason=errmsg)
-
- elif 'slot' in errmsg:
- self.wantReconnect = True
- self.retry(24, 3600, errmsg)
-
- else:
- self.wantReconnect = True
- self.retry(wait_time=60, reason=errmsg)
-
-
-getInfo = create_getInfo(ShareonlineBiz)
diff --git a/module/plugins/hoster/ShareplaceCom.py b/module/plugins/hoster/ShareplaceCom.py
deleted file mode 100644
index ccf4bcda5..000000000
--- a/module/plugins/hoster/ShareplaceCom.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urllib import unquote
-
-from module.plugins.Hoster import Hoster
-
-
-class ShareplaceCom(Hoster):
- __name__ = "ShareplaceCom"
- __type__ = "hoster"
- __version__ = "0.11"
-
- __pattern__ = r'(http://)?(?:www\.)?shareplace\.(com|org)/\?\w+'
-
- __description__ = """Shareplace.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("ACCakut", None)]
-
-
- def process(self, pyfile):
- self.pyfile = pyfile
- self.prepare()
- self.download(self.get_file_url())
-
-
- def prepare(self):
- if not self.file_exists():
- self.offline()
-
- self.pyfile.name = self.get_file_name()
-
- wait_time = self.get_waiting_time()
- self.setWait(wait_time)
- self.wait()
-
-
- def get_waiting_time(self):
- if not self.html:
- self.download_html()
-
- #var zzipitime = 15;
- m = re.search(r'var zzipitime = (\d+);', self.html)
- if m:
- sec = int(m.group(1))
- else:
- sec = 0
-
- return sec
-
-
- def download_html(self):
- url = re.sub("shareplace.com\/\?", "shareplace.com//index1.php/?a=", self.pyfile.url)
- self.html = self.load(url, decode=True)
-
-
- def get_file_url(self):
- """ returns the absolute downloadable filepath
- """
- url = re.search(r"var beer = '(.*?)';", self.html)
- if url:
- url = url.group(1)
- url = unquote(
- url.replace("http://http:/", "").replace("vvvvvvvvv", "").replace("lllllllll", "").replace(
- "teletubbies", ""))
- self.logDebug("URL: %s" % url)
- return url
- else:
- self.error(_("Absolute filepath not found"))
-
-
- def get_file_name(self):
- if not self.html:
- self.download_html()
-
- return re.search("<title>\s*(.*?)\s*</title>", self.html).group(1)
-
-
- def file_exists(self):
- """ returns True or False
- """
- if not self.html:
- self.download_html()
-
- if re.search(r"HTTP Status 404", self.html) is not None:
- return False
- else:
- return True
diff --git a/module/plugins/hoster/SharingmatrixCom.py b/module/plugins/hoster/SharingmatrixCom.py
deleted file mode 100644
index fa08a4a8f..000000000
--- a/module/plugins/hoster/SharingmatrixCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class SharingmatrixCom(DeadHoster):
- __name__ = "SharingmatrixCom"
- __type__ = "hoster"
- __version__ = "0.01"
-
- __pattern__ = r'http://(?:www\.)?sharingmatrix\.com/file/\w+'
-
- __description__ = """Sharingmatrix.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de"),
- ("paulking", None)]
-
-
-getInfo = create_getInfo(SharingmatrixCom)
diff --git a/module/plugins/hoster/ShragleCom.py b/module/plugins/hoster/ShragleCom.py
deleted file mode 100644
index b31f0e450..000000000
--- a/module/plugins/hoster/ShragleCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class ShragleCom(DeadHoster):
- __name__ = "ShragleCom"
- __type__ = "hoster"
- __version__ = "0.22"
-
- __pattern__ = r'http://(?:www\.)?(cloudnator|shragle)\.com/files/(?P<ID>.*?)/'
-
- __description__ = """Cloudnator.com (Shragle.com) hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(ShragleCom)
diff --git a/module/plugins/hoster/SimplyPremiumCom.py b/module/plugins/hoster/SimplyPremiumCom.py
deleted file mode 100644
index bc37f45c8..000000000
--- a/module/plugins/hoster/SimplyPremiumCom.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from datetime import datetime, timedelta
-
-from module.plugins.Hoster import Hoster
-from module.plugins.hoster.UnrestrictLi import secondsToMidnight
-
-
-class SimplyPremiumCom(Hoster):
- __name__ = "SimplyPremiumCom"
- __type__ = "hoster"
- __version__ = "0.03"
-
- __pattern__ = r'https?://.*(simply-premium)\.com'
-
- __description__ = """Simply-Premium.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("EvolutionClip", "evolutionclip@live.de")]
-
-
- def setup(self):
- self.chunkLimit = 16
- self.resumeDownload = False
-
-
- def process(self, pyfile):
- if re.match(self.__pattern__, pyfile.url):
- new_url = pyfile.url
- elif not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "Simply-Premium.com")
- self.fail(_("No Simply-Premium.com account provided"))
- else:
- self.logDebug("Old URL: %s" % pyfile.url)
- for i in xrange(5):
- page = self.load("http://www.simply-premium.com/premium.php", get={'info': "", 'link': pyfile.url})
- self.logDebug("JSON data: " + page)
- if page != '':
- break
- else:
- self.logInfo(_("Unable to get API data, waiting 1 minute and retry"))
- self.retry(5, 60, "Unable to get API data")
-
- if '<valid>0</valid>' in page or (
- "You are not allowed to download from this host" in page and self.premium):
- self.account.relogin(self.user)
- self.retry()
- elif "NOTFOUND" in page:
- self.offline()
- elif "downloadlimit" in page:
- self.logWarning(_("Reached maximum connctions"))
- self.retry(5, 60, "Reached maximum connctions")
- elif "trafficlimit" in page:
- self.logWarning(_("Reached daily limit for this host"))
- self.retry(wait_time=secondsToMidnight(gmt=2), "Daily limit for this host reached")
- elif "hostererror" in page:
- self.logWarning(_("Hoster temporarily unavailable, waiting 1 minute and retry"))
- self.retry(5, 60, "Hoster is temporarily unavailable")
- #page = json_loads(page)
- #new_url = page.keys()[0]
- #self.api_data = page[new_url]
-
- try:
- self.pyfile.name = re.search(r'<name>([^<]+)</name>', page).group(1)
- except AttributeError:
- self.pyfile.name = ""
-
- try:
- self.pyfile.size = re.search(r'<size>(\d+)</size>', page).group(1)
- except AttributeError:
- self.pyfile.size = 0
-
- try:
- new_url = re.search(r'<download>([^<]+)</download>', page).group(1)
- except AttributeError:
- new_url = 'http://www.simply-premium.com/premium.php?link=' + pyfile.url
-
- if new_url != pyfile.url:
- self.logDebug("New URL: " + new_url)
-
- self.download(new_url, disposition=True)
diff --git a/module/plugins/hoster/SimplydebridCom.py b/module/plugins/hoster/SimplydebridCom.py
deleted file mode 100644
index 3d4d3be93..000000000
--- a/module/plugins/hoster/SimplydebridCom.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Hoster import Hoster
-
-
-class SimplydebridCom(Hoster):
- __name__ = "SimplydebridCom"
- __type__ = "hoster"
- __version__ = "0.1"
-
- __pattern__ = r'http://(?:www\.)?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/sd\.php/*'
-
- __description__ = """Simply-debrid.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Kagenoshin", "kagenoshin@gmx.ch")]
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
- self.chunkLimit = 1
-
-
- def process(self, pyfile):
- if not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "simply-debrid.com")
- self.fail(_("No simply-debrid.com account provided"))
-
- self.logDebug("Old URL: %s" % pyfile.url)
-
- #fix the links for simply-debrid.com!
- new_url = pyfile.url
- new_url = new_url.replace("clz.to", "cloudzer.net/file")
- new_url = new_url.replace("http://share-online", "http://www.share-online")
- new_url = new_url.replace("ul.to", "uploaded.net/file")
- new_url = new_url.replace("uploaded.com", "uploaded.net")
- new_url = new_url.replace("filerio.com", "filerio.in")
- new_url = new_url.replace("lumfile.com", "lumfile.se")
- if('fileparadox' in new_url):
- new_url = new_url.replace("http://", "https://")
-
- if re.match(self.__pattern__, new_url):
- new_url = new_url
-
- self.logDebug("New URL: %s" % new_url)
-
- if not re.match(self.__pattern__, new_url):
- page = self.load("http://simply-debrid.com/api.php", get={'dl': new_url}) # +'&u='+self.user+'&p='+self.account.getAccountData(self.user)['password'])
- if 'tiger Link' in page or 'Invalid Link' in page or ('API' in page and 'ERROR' in page):
- self.fail(_("Unable to unrestrict link"))
- new_url = page
-
- self.setWait(5)
- self.wait()
- self.logDebug("Unrestricted URL: " + new_url)
-
- self.download(new_url, disposition=True)
-
- check = self.checkDownload({"bad1": "No address associated with hostname", "bad2": "<html"})
-
- if check == "bad1" or check == "bad2":
- self.retry(24, 3 * 60, "Bad file downloaded")
diff --git a/module/plugins/hoster/SockshareCom.py b/module/plugins/hoster/SockshareCom.py
deleted file mode 100644
index aabb8dcd1..000000000
--- a/module/plugins/hoster/SockshareCom.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class SockshareCom(DeadHoster):
- __name__ = "SockshareCom"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'http://(?:www\.)?sockshare\.com/(mobile/)?(file|embed)/(?P<ID>\w+)'
-
- __description__ = """Sockshare.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de"),
- ("stickell", "l.stickell@yahoo.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
-getInfo = create_getInfo(SockshareCom)
diff --git a/module/plugins/hoster/SoundcloudCom.py b/module/plugins/hoster/SoundcloudCom.py
deleted file mode 100644
index 71cfb6b27..000000000
--- a/module/plugins/hoster/SoundcloudCom.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import pycurl
-import re
-
-from module.plugins.Hoster import Hoster
-
-
-class SoundcloudCom(Hoster):
- __name__ = "SoundcloudCom"
- __type__ = "hoster"
- __version__ = "0.1"
-
- __pattern__ = r'https?://(?:www\.)?soundcloud\.com/(?P<UID>.*?)/(?P<SID>.*)'
-
- __description__ = """SoundCloud.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Peekayy", "peekayy.dev@gmail.com")]
-
-
- def process(self, pyfile):
- # default UserAgent of HTTPRequest fails for this hoster so we use this one
- self.req.http.c.setopt(pycurl.USERAGENT, 'Mozilla/5.0')
- page = self.load(pyfile.url)
- m = re.search(r'<div class="haudio.*?large.*?" data-sc-track="(?P<ID>\d*)"', page)
- songId = clientId = ""
- if m:
- songId = m.group("ID")
- if len(songId) <= 0:
- self.logError(_("Could not find song id"))
- self.offline()
- else:
- m = re.search(r'"clientID":"(?P<CID>.*?)"', page)
- if m:
- clientId = m.group("CID")
-
- if len(clientId) <= 0:
- clientId = "b45b1aa10f1ac2941910a7f0d10f8e28"
-
- m = re.search(r'<em itemprop="name">\s(?P<TITLE>.*?)\s</em>', page)
- if m:
- pyfile.name = m.group("TITLE") + ".mp3"
- else:
- pyfile.name = re.match(self.__pattern__, pyfile.url).group("SID") + ".mp3"
-
- # url to retrieve the actual song url
- page = self.load("https://api.sndcdn.com/i1/tracks/%s/streams" % songId, get={"client_id": clientId})
- # getting streams
- # for now we choose the first stream found in all cases
- # it could be improved if relevant for this hoster
- streams = [
- (result.group("QUALITY"), result.group("URL"))
- for result in re.finditer(r'"(?P<QUALITY>.*?)":"(?P<URL>.*?)"', page)
- ]
- self.logDebug("Found Streams", streams)
- self.logDebug("Downloading", streams[0][0], streams[0][1])
- self.download(streams[0][1])
diff --git a/module/plugins/hoster/SpeedLoadOrg.py b/module/plugins/hoster/SpeedLoadOrg.py
deleted file mode 100644
index a13220eab..000000000
--- a/module/plugins/hoster/SpeedLoadOrg.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class SpeedLoadOrg(DeadHoster):
- __name__ = "SpeedLoadOrg"
- __type__ = "hoster"
- __version__ = "1.02"
-
- __pattern__ = r'http://(?:www\.)?speedload\.org/(?P<ID>\w+)'
-
- __description__ = """Speedload.org hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
-getInfo = create_getInfo(SpeedLoadOrg)
diff --git a/module/plugins/hoster/SpeedfileCz.py b/module/plugins/hoster/SpeedfileCz.py
deleted file mode 100644
index c04d8b281..000000000
--- a/module/plugins/hoster/SpeedfileCz.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class SpeedfileCz(DeadHoster):
- __name__ = "SpeedFileCz"
- __type__ = "hoster"
- __version__ = "0.32"
-
- __pattern__ = r'http://(?:www\.)?speedfile\.cz/.*'
-
- __description__ = """Speedfile.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(SpeedfileCz)
diff --git a/module/plugins/hoster/SpeedyshareCom.py b/module/plugins/hoster/SpeedyshareCom.py
deleted file mode 100644
index fa54d6060..000000000
--- a/module/plugins/hoster/SpeedyshareCom.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://speedy.sh/ep2qY/Zapp-Brannigan.jpg
-
-import re
-
-from urlparse import urljoin
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class SpeedyshareCom(SimpleHoster):
- __name__ = "SpeedyshareCom"
- __type__ = "hoster"
- __version__ = "0.03"
-
- __pattern__ = r'https?://(?:www\.)?(speedyshare\.com|speedy\.sh)/\w+'
-
- __description__ = """Speedyshare.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zapp-brannigan", "fuerst.reinje@web.de")]
-
-
- NAME_PATTERN = r'class=downloadfilename>(?P<N>.*)</span></td>'
- SIZE_PATTERN = r'class=sizetagtext>(?P<S>.*) (?P<U>[kKmM]?[iI]?[bB]?)</div>'
-
- OFFLINE_PATTERN = r'class=downloadfilenamenotfound>.*</span>'
-
- LINK_PATTERN = r'<a href=\'(.*)\'><img src=/gf/slowdownload\.png alt=\'Slow Download\' border=0'
-
-
- def setup(self):
- self.multiDL = False
- self.chunkLimit = 1
-
-
- def handleFree(self):
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("Download link not found"))
-
- dl_link = urljoin("http://www.speedyshare.com", m.group(1))
- self.download(dl_link, disposition=True)
-
- check = self.checkDownload({'html': re.compile("html")})
- if check == "html":
- self.error(_("Downloaded file is an html page"))
-
-
-getInfo = create_getInfo(SpeedyshareCom)
diff --git a/module/plugins/hoster/StorageTo.py b/module/plugins/hoster/StorageTo.py
deleted file mode 100644
index bedc2934f..000000000
--- a/module/plugins/hoster/StorageTo.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class StorageTo(DeadHoster):
- __name__ = "StorageTo"
- __type__ = "hoster"
- __version__ = "0.01"
-
- __pattern__ = r'http://(?:www\.)?storage\.to/get/.+'
-
- __description__ = """Storage.to hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("mkaay", "mkaay@mkaay.de")]
-
-
-getInfo = create_getInfo(StorageTo)
diff --git a/module/plugins/hoster/StreamCz.py b/module/plugins/hoster/StreamCz.py
deleted file mode 100644
index 95d1e12ba..000000000
--- a/module/plugins/hoster/StreamCz.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
-
-
-def getInfo(urls):
- result = []
-
- for url in urls:
-
- html = getURL(url)
- if re.search(StreamCz.OFFLINE_PATTERN, html):
- # File offline
- result.append((url, 0, 1, url))
- else:
- result.append((url, 0, 2, url))
- yield result
-
-
-class StreamCz(Hoster):
- __name__ = "StreamCz"
- __type__ = "hoster"
- __version__ = "0.2"
-
- __pattern__ = r'https?://(?:www\.)?stream\.cz/[^/]+/\d+.*'
-
- __description__ = """Stream.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<link rel="video_src" href="http://www\.stream\.cz/\w+/(\d+)-([^"]+)" />'
- OFFLINE_PATTERN = r'<h1 class="commonTitle">Str.nku nebylo mo.n. nal.zt \(404\)</h1>'
-
- CDN_PATTERN = r'<param name="flashvars" value="[^"]*&id=(?P<ID>\d+)(?:&cdnLQ=(?P<cdnLQ>\d*))?(?:&cdnHQ=(?P<cdnHQ>\d*))?(?:&cdnHD=(?P<cdnHD>\d*))?&'
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
-
-
- def process(self, pyfile):
- self.html = self.load(pyfile.url, decode=True)
-
- if re.search(self.OFFLINE_PATTERN, self.html):
- self.offline()
-
- m = re.search(self.CDN_PATTERN, self.html)
- if m is None:
- self.error(_("CDN_PATTERN not found"))
- cdn = m.groupdict()
- self.logDebug(cdn)
- for cdnkey in ("cdnHD", "cdnHQ", "cdnLQ"):
- if cdnkey in cdn and cdn[cdnkey] > '':
- cdnid = cdn[cdnkey]
- break
- else:
- self.fail(_("Stream URL not found"))
-
- m = re.search(self.NAME_PATTERN, self.html)
- if m is None:
- self.error(_("NAME_PATTERN not found"))
- pyfile.name = "%s-%s.%s.mp4" % (m.group(2), m.group(1), cdnkey[-2:])
-
- download_url = "http://cdn-dispatcher.stream.cz/?id=" + cdnid
- self.logInfo(_("STREAM: %s") % cdnkey[-2:], download_url)
- self.download(download_url)
diff --git a/module/plugins/hoster/StreamcloudEu.py b/module/plugins/hoster/StreamcloudEu.py
deleted file mode 100644
index 4f854a99d..000000000
--- a/module/plugins/hoster/StreamcloudEu.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class StreamcloudEu(XFSHoster):
- __name__ = "StreamcloudEu"
- __type__ = "hoster"
- __version__ = "0.09"
-
- __pattern__ = r'http://(?:www\.)?streamcloud\.eu/\w{12}'
-
- __description__ = """Streamcloud.eu hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("seoester", "seoester@googlemail.com")]
-
-
- HOSTER_DOMAIN = "streamcloud.eu"
-
- LINK_PATTERN = r'file: "(http://(stor|cdn)\d+\.streamcloud\.eu:?\d*/.*/video\.(mp4|flv))",'
-
-
- def setup(self):
- self.multiDL = True
- self.chunkLimit = 1
- self.resumeDownload = self.premium
-
-
-getInfo = create_getInfo(StreamcloudEu)
diff --git a/module/plugins/hoster/TurbobitNet.py b/module/plugins/hoster/TurbobitNet.py
deleted file mode 100644
index 9d7dcc67b..000000000
--- a/module/plugins/hoster/TurbobitNet.py
+++ /dev/null
@@ -1,173 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import random
-import re
-import time
-
-from Crypto.Cipher import ARC4
-from binascii import hexlify, unhexlify
-from pycurl import HTTPHEADER
-from urllib import quote
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, timestamp
-
-
-class TurbobitNet(SimpleHoster):
- __name__ = "TurbobitNet"
- __type__ = "hoster"
- __version__ = "0.16"
-
- __pattern__ = r'http://(?:www\.)?turbobit\.net/(?:download/free/)?(?P<ID>\w+)'
-
- __description__ = """ Turbobit.net hoster plugin """
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("prOq", None)]
-
-
- URL_REPLACEMENTS = [(__pattern__ + ".*", "http://turbobit.net/\g<ID>.html")]
-
- COOKIES = [("turbobit.net", "user_lang", "en")]
-
- NAME_PATTERN = r'id="file-title">(?P<N>.+?)<'
- SIZE_PATTERN = r'class="file-size">(?P<S>[\d.,]+) (?P<U>[\w^_]+)'
- OFFLINE_PATTERN = r'<h2>File Not Found</h2>|html\(\'File (?:was )?not found'
-
- LINK_PATTERN = r'(?P<url>/download/redirect/[^"\']+)'
- LIMIT_WAIT_PATTERN = r'<div id=\'timeout\'>(\d+)<'
-
- CAPTCHA_PATTERN = r'<img alt="Captcha" src="(.+?)"'
-
-
- def handleFree(self):
- self.url = "http://turbobit.net/download/free/%s" % self.info['pattern']['ID']
- self.html = self.load(self.url, ref=True, decode=True)
-
- rtUpdate = self.getRtUpdate()
-
- self.solveCaptcha()
- self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With: XMLHttpRequest"])
- self.url = self.getDownloadUrl(rtUpdate)
-
- self.wait()
- self.html = self.load(self.url)
- self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With:"])
- self.downloadFile()
-
-
- def solveCaptcha(self):
- for _i in xrange(5):
- m = re.search(self.LIMIT_WAIT_PATTERN, self.html)
- if m:
- wait_time = int(m.group(1))
- self.wait(wait_time, wait_time > 60)
- self.retry()
-
- action, inputs = self.parseHtmlForm("action='#'")
- if not inputs:
- self.error(_("Captcha form not found"))
- self.logDebug(inputs)
-
- if inputs['captcha_type'] == 'recaptcha':
- recaptcha = ReCaptcha(self)
- inputs['recaptcha_challenge_field'], inputs['recaptcha_response_field'] = recaptcha.challenge()
- else:
- m = re.search(self.CAPTCHA_PATTERN, self.html)
- if m is None:
- self.error(_("captcha"))
- captcha_url = m.group(1)
- inputs['captcha_response'] = self.decryptCaptcha(captcha_url)
-
- self.logDebug(inputs)
- self.html = self.load(self.url, post=inputs)
-
- if '<div class="captcha-error">Incorrect, try again!<' in self.html:
- self.invalidCaptcha()
- else:
- self.correctCaptcha()
- break
- else:
- self.fail(_("Invalid captcha"))
-
-
- def getRtUpdate(self):
- rtUpdate = self.getStorage("rtUpdate")
- if not rtUpdate:
- if self.getStorage("version") != self.__version__ \
- or int(self.getStorage("timestamp", 0)) + 86400000 < timestamp():
- # that's right, we are even using jdownloader updates
- rtUpdate = getURL("http://update0.jdownloader.org/pluginstuff/tbupdate.js")
- rtUpdate = self.decrypt(rtUpdate.splitlines()[1])
- # but we still need to fix the syntax to work with other engines than rhino
- rtUpdate = re.sub(r'for each\(var (\w+) in(\[[^\]]+\])\)\{',
- r'zza=\2;for(var zzi=0;zzi<zza.length;zzi++){\1=zza[zzi];', rtUpdate)
- rtUpdate = re.sub(r"for\((\w+)=", r"for(var \1=", rtUpdate)
-
- self.setStorage("rtUpdate", rtUpdate)
- self.setStorage("timestamp", timestamp())
- self.setStorage("version", self.__version__)
- else:
- self.logError(_("Unable to download, wait for update..."))
- self.tempOffline()
-
- return rtUpdate
-
-
- def getDownloadUrl(self, rtUpdate):
- self.req.http.lastURL = self.url
-
- m = re.search("(/\w+/timeout\.js\?\w+=)([^\"\'<>]+)", self.html)
- if m:
- url = "http://turbobit.net%s%s" % m.groups()
- else:
- url = "http://turbobit.net/files/timeout.js?ver=%s" % "".join(random.choice('0123456789ABCDEF') for _i in xrange(32))
-
- fun = self.load(url)
-
- self.setWait(65, False)
-
- for b in [1, 3]:
- self.jscode = "var id = \'%s\';var b = %d;var inn = \'%s\';%sout" % (
- self.info['pattern']['ID'], b, quote(fun), rtUpdate)
-
- try:
- out = self.js.eval(self.jscode)
- self.logDebug("URL", self.js.engine, out)
- if out.startswith('/download/'):
- return "http://turbobit.net%s" % out.strip()
- except Exception, e:
- self.logError(e)
- else:
- if self.retries >= 2:
- # retry with updated js
- self.delStorage("rtUpdate")
- self.retry()
-
-
- def decrypt(self, data):
- cipher = ARC4.new(hexlify('E\x15\xa1\x9e\xa3M\xa0\xc6\xa0\x84\xb6H\x83\xa8o\xa0'))
- return unhexlify(cipher.encrypt(unhexlify(data)))
-
-
- def getLocalTimeString(self):
- lt = time.localtime()
- tz = time.altzone if lt.tm_isdst else time.timezone
- return "%s GMT%+03d%02d" % (time.strftime("%a %b %d %Y %H:%M:%S", lt), -tz // 3600, tz % 3600)
-
-
- def handlePremium(self):
- self.logDebug("Premium download as user %s" % self.user)
- self.downloadFile()
-
-
- def downloadFile(self):
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("Download link not found"))
- self.url = "http://turbobit.net" + m.group('url')
- self.download(self.url)
-
-
-getInfo = create_getInfo(TurbobitNet)
diff --git a/module/plugins/hoster/TurbouploadCom.py b/module/plugins/hoster/TurbouploadCom.py
deleted file mode 100644
index 7dd7b7dce..000000000
--- a/module/plugins/hoster/TurbouploadCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class TurbouploadCom(DeadHoster):
- __name__ = "TurbouploadCom"
- __type__ = "hoster"
- __version__ = "0.03"
-
- __pattern__ = r'http://(?:www\.)?turboupload\.com/(\w+).*'
-
- __description__ = """Turboupload.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(TurbouploadCom)
diff --git a/module/plugins/hoster/TusfilesNet.py b/module/plugins/hoster/TusfilesNet.py
deleted file mode 100644
index 8c80455b4..000000000
--- a/module/plugins/hoster/TusfilesNet.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class TusfilesNet(XFSHoster):
- __name__ = "TusfilesNet"
- __type__ = "hoster"
- __version__ = "0.07"
-
- __pattern__ = r'https?://(?:www\.)?tusfiles\.net/\w{12}'
-
- __description__ = """Tusfiles.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com"),
- ("guidobelix", "guidobelix@hotmail.it")]
-
-
- HOSTER_DOMAIN = "tusfiles.net"
-
- INFO_PATTERN = r'\](?P<N>.+) - (?P<S>[\d.,]+) (?P<U>[\w^_]+)\['
- OFFLINE_PATTERN = r'>File Not Found|<Title>TusFiles - Fast Sharing Files!'
-
-
- def setup(self):
- self.multiDL = False
- self.chunkLimit = -1
- self.resumeDownload = True
-
-
- def handlePremium(self):
- return self.handleFree()
-
-
-getInfo = create_getInfo(TusfilesNet)
diff --git a/module/plugins/hoster/TwoSharedCom.py b/module/plugins/hoster/TwoSharedCom.py
deleted file mode 100644
index 59a8ce6e1..000000000
--- a/module/plugins/hoster/TwoSharedCom.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class TwoSharedCom(SimpleHoster):
- __name__ = "TwoSharedCom"
- __type__ = "hoster"
- __version__ = "0.12"
-
- __pattern__ = r'http://(?:www\.)?2shared\.com/(account/)?(download|get|file|document|photo|video|audio)/.*'
-
- __description__ = """2Shared.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<h1>(?P<N>.*)</h1>'
- SIZE_PATTERN = r'<span class="dtitle">File size:</span>\s*(?P<S>[\d.,]+) (?P<U>[\w^_]+)'
- OFFLINE_PATTERN = r'The file link that you requested is not valid\.|This file was deleted\.'
-
- LINK_PATTERN = r'window.location =\'(.+?)\';'
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
-
-
- def handleFree(self):
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("Download link"))
-
- link = m.group(1)
- self.download(link)
-
-
-getInfo = create_getInfo(TwoSharedCom)
diff --git a/module/plugins/hoster/UlozTo.py b/module/plugins/hoster/UlozTo.py
deleted file mode 100644
index 262b37c21..000000000
--- a/module/plugins/hoster/UlozTo.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-import time
-
-from module.common.json_layer import json_loads
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-def convertDecimalPrefix(m):
- # decimal prefixes used in filesize and traffic
- return ("%%.%df" % {'k': 3, 'M': 6, 'G': 9}[m.group(2)] % float(m.group(1))).replace('.', '')
-
-
-class UlozTo(SimpleHoster):
- __name__ = "UlozTo"
- __type__ = "hoster"
- __version__ = "1.00"
-
- __pattern__ = r'http://(?:www\.)?(uloz\.to|ulozto\.(cz|sk|net)|bagruj\.cz|zachowajto\.pl)/(?:live/)?(?P<id>\w+/[^/?]*)'
-
- __description__ = """Uloz.to hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- INFO_PATTERN = r'<p>File <strong>(?P<N>[^<]+)</strong> is password protected</p>'
- NAME_PATTERN = r'<title>(?P<N>[^<]+) \| Uloz\.to</title>'
- SIZE_PATTERN = r'<span id="fileSize">.*?(?P<S>[\d.,]+\s[kMG]?B)</span>'
- OFFLINE_PATTERN = r'<title>404 - Page not found</title>|<h1 class="h1">File (has been deleted|was banned)</h1>'
-
- SIZE_REPLACEMENTS = [('([\d.]+)\s([kMG])B', convertDecimalPrefix)]
- URL_REPLACEMENTS = [(r"(?<=http://)([^/]+)", "www.ulozto.net")]
-
- ADULT_PATTERN = r'<form action="(?P<link>[^\"]*)" method="post" id="frm-askAgeForm">'
- PASSWD_PATTERN = r'<div class="passwordProtectedFile">'
- VIPLINK_PATTERN = r'<a href="[^"]*\?disclaimer=1" class="linkVip">'
- FREE_URL_PATTERN = r'<div class="freeDownloadForm"><form action="([^"]+)"'
- PREMIUM_URL_PATTERN = r'<div class="downloadForm"><form action="([^"]+)"'
- TOKEN_PATTERN = r'<input type="hidden" name="_token_" id="[^\"]*" value="(?P<token>.+?)"'
-
-
- def setup(self):
- self.multiDL = self.premium
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- pyfile.url = re.sub(r"(?<=http://)([^/]+)", "www.ulozto.net", pyfile.url)
- self.html = self.load(pyfile.url, decode=True, cookies=True)
-
- if re.search(self.ADULT_PATTERN, self.html):
- self.logInfo(_("Adult content confirmation needed"))
-
- m = re.search(self.TOKEN_PATTERN, self.html)
- if m is None:
- self.error(_("TOKEN_PATTERN not found"))
- token = m.group(1)
-
- self.html = self.load(pyfile.url, get={"do": "askAgeForm-submit"},
- post={"agree": "Confirm", "_token_": token}, cookies=True)
-
- passwords = self.getPassword().splitlines()
- while self.PASSWD_PATTERN in self.html:
- if passwords:
- password = passwords.pop(0)
- self.logInfo(_("Password protected link, trying ") + password)
- self.html = self.load(pyfile.url, get={"do": "passwordProtectedForm-submit"},
- post={"password": password, "password_send": 'Send'}, cookies=True)
- else:
- self.fail(_("No or incorrect password"))
-
- if re.search(self.VIPLINK_PATTERN, self.html):
- self.html = self.load(pyfile.url, get={"disclaimer": "1"})
-
- self.getFileInfo()
-
- if self.premium and self.checkTrafficLeft():
- self.handlePremium()
- else:
- self.handleFree()
-
- self.doCheckDownload()
-
-
- def handleFree(self):
- action, inputs = self.parseHtmlForm('id="frm-downloadDialog-freeDownloadForm"')
- if not action or not inputs:
- self.error(_("Free download form not found"))
-
- self.logDebug("inputs.keys = " + str(inputs.keys()))
- # get and decrypt captcha
- if all(key in inputs for key in ("captcha_value", "captcha_id", "captcha_key")):
- # Old version - last seen 9.12.2013
- self.logDebug('Using "old" version')
-
- captcha_value = self.decryptCaptcha("http://img.uloz.to/captcha/%s.png" % inputs['captcha_id'])
- self.logDebug("CAPTCHA ID: " + inputs['captcha_id'] + ", CAPTCHA VALUE: " + captcha_value)
-
- inputs.update({'captcha_id': inputs['captcha_id'], 'captcha_key': inputs['captcha_key'], 'captcha_value': captcha_value})
-
- elif all(key in inputs for key in ("captcha_value", "timestamp", "salt", "hash")):
- # New version - better to get new parameters (like captcha reload) because of image url - since 6.12.2013
- self.logDebug('Using "new" version')
-
- xapca = self.load("http://www.ulozto.net/reloadXapca.php", get={"rnd": str(int(time.time()))})
- self.logDebug("xapca = " + str(xapca))
-
- data = json_loads(xapca)
- captcha_value = self.decryptCaptcha(str(data['image']))
- self.logDebug("CAPTCHA HASH: " + data['hash'], "CAPTCHA SALT: " + str(data['salt']), "CAPTCHA VALUE: " + captcha_value)
-
- inputs.update({'timestamp': data['timestamp'], 'salt': data['salt'], 'hash': data['hash'], 'captcha_value': captcha_value})
- else:
- self.error(_("CAPTCHA form changed"))
-
- self.multiDL = True
- self.download("http://www.ulozto.net" + action, post=inputs, cookies=True, disposition=True)
-
-
- def handlePremium(self):
- self.download(self.pyfile.url + "?do=directDownload", disposition=True)
- #parsed_url = self.findDownloadURL(premium=True)
- #self.download(parsed_url, post={"download": "Download"})
-
-
- def findDownloadURL(self, premium=False):
- msg = _("%s link" % ("Premium" if premium else "Free"))
- m = re.search(self.PREMIUM_URL_PATTERN if premium else self.FREE_URL_PATTERN, self.html)
- if m is None:
- self.error(msg)
- parsed_url = "http://www.ulozto.net" + m.group(1)
- self.logDebug("%s: %s" % (msg, parsed_url))
- return parsed_url
-
-
- def doCheckDownload(self):
- check = self.checkDownload({
- "wrong_captcha": re.compile(r'<ul class="error">\s*<li>Error rewriting the text.</li>'),
- "offline": re.compile(self.OFFLINE_PATTERN),
- "passwd": self.PASSWD_PATTERN,
- "server_error": 'src="http://img.ulozto.cz/error403/vykricnik.jpg"', # paralell dl, server overload etc.
- "not_found": "<title>UloÅŸ.to</title>"
- })
-
- if check == "wrong_captcha":
- #self.delStorage("captcha_id")
- #self.delStorage("captcha_text")
- self.invalidCaptcha()
- self.retry(reason=_("Wrong captcha code"))
- elif check == "offline":
- self.offline()
- elif check == "passwd":
- self.fail(_("Wrong password"))
- elif check == "server_error":
- self.logError(_("Server error, try downloading later"))
- self.multiDL = False
- self.wait(1 * 60 * 60, True)
- self.retry()
- elif check == "not_found":
- self.fail(_("Server error - file not downloadable"))
-
-
-getInfo = create_getInfo(UlozTo)
diff --git a/module/plugins/hoster/UloziskoSk.py b/module/plugins/hoster/UloziskoSk.py
deleted file mode 100644
index b48964771..000000000
--- a/module/plugins/hoster/UloziskoSk.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class UloziskoSk(SimpleHoster):
- __name__ = "UloziskoSk"
- __type__ = "hoster"
- __version__ = "0.24"
-
- __pattern__ = r'http://(?:www\.)?ulozisko\.sk/.*'
-
- __description__ = """Ulozisko.sk hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<div class="down1">(?P<N>[^<]+)</div>'
- SIZE_PATTERN = ur'Veğkosť súboru: <strong>(?P<S>[\d.,]+) (?P<U>[\w^_]+)</strong><br />'
- OFFLINE_PATTERN = ur'<span class = "red">ZadanÜ súbor neexistuje z jedného z nasledujúcich dÎvodov:</span>'
-
- LINK_PATTERN = r'<form name = "formular" action = "([^"]+)" method = "post">'
- ID_PATTERN = r'<input type = "hidden" name = "id" value = "([^"]+)" />'
- CAPTCHA_PATTERN = r'<img src="(/obrazky/obrazky\.php\?fid=[^"]+)" alt="" />'
- IMG_PATTERN = ur'<strong>PRE ZVÄČŠENIE KLIKNITE NA OBRÁZOK</strong><br /><a href = "([^"]+)">'
-
-
- def process(self, pyfile):
- self.html = self.load(pyfile.url, decode=True)
- self.getFileInfo()
-
- m = re.search(self.IMG_PATTERN, self.html)
- if m:
- url = "http://ulozisko.sk" + m.group(1)
- self.download(url)
- else:
- self.handleFree()
-
-
- def handleFree(self):
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("LINK_PATTERN not found"))
- parsed_url = 'http://www.ulozisko.sk' + m.group(1)
-
- m = re.search(self.ID_PATTERN, self.html)
- if m is None:
- self.error(_("ID_PATTERN not found"))
- id = m.group(1)
-
- self.logDebug("URL:" + parsed_url + ' ID:' + id)
-
- m = re.search(self.CAPTCHA_PATTERN, self.html)
- if m is None:
- self.error(_("CAPTCHA_PATTERN not found"))
- captcha_url = 'http://www.ulozisko.sk' + m.group(1)
-
- captcha = self.decryptCaptcha(captcha_url, cookies=True)
-
- self.logDebug("CAPTCHA_URL:" + captcha_url + ' CAPTCHA:' + captcha)
-
- self.download(parsed_url, post={
- "antispam": captcha,
- "id": id,
- "name": self.pyfile.name,
- "but": "++++STIAHNI+S%DABOR++++"
- })
-
-
-getInfo = create_getInfo(UloziskoSk)
diff --git a/module/plugins/hoster/UnibytesCom.py b/module/plugins/hoster/UnibytesCom.py
deleted file mode 100644
index 8c39cce82..000000000
--- a/module/plugins/hoster/UnibytesCom.py
+++ /dev/null
@@ -1,74 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urljoin
-
-from pycurl import FOLLOWLOCATION
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class UnibytesCom(SimpleHoster):
- __name__ = "UnibytesCom"
- __type__ = "hoster"
- __version__ = "0.11"
-
- __pattern__ = r'https?://(?:www\.)?unibytes\.com/[\w .-]{11}B'
-
- __description__ = """UniBytes.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- HOSTER_DOMAIN = "unibytes.com"
-
- INFO_PATTERN = r'<span[^>]*?id="fileName"[^>]*>(?P<N>[^>]+)</span>\s*\((?P<S>\d.*?)\)'
-
- WAIT_PATTERN = r'Wait for <span id="slowRest">(\d+)</span> sec'
- LINK_PATTERN = r'<a href="([^"]+)">Download</a>'
-
-
- def handleFree(self):
- domain = "http://www.%s/" % self.HOSTER_DOMAIN
- action, post_data = self.parseHtmlForm('id="startForm"')
- self.req.http.c.setopt(FOLLOWLOCATION, 0)
-
- for _i in xrange(8):
- self.logDebug(action, post_data)
- self.html = self.load(urljoin(domain, action), post=post_data)
-
- m = re.search(r'location:\s*(\S+)', self.req.http.header, re.I)
- if m:
- url = m.group(1)
- break
-
- if '>Somebody else is already downloading using your IP-address<' in self.html:
- self.wait(10 * 60, True)
- self.retry()
-
- if post_data['step'] == 'last':
- m = re.search(self.LINK_PATTERN, self.html)
- if m:
- url = m.group(1)
- self.correctCaptcha()
- break
- else:
- self.invalidCaptcha()
-
- last_step = post_data['step']
- action, post_data = self.parseHtmlForm('id="stepForm"')
-
- if last_step == 'timer':
- m = re.search(self.WAIT_PATTERN, self.html)
- self.wait(int(m.group(1)) if m else 60, False)
- elif last_step in ("captcha", "last"):
- post_data['captcha'] = self.decryptCaptcha(urljoin(domain, "/captcha.jpg"))
- else:
- self.fail(_("No valid captcha code entered"))
-
- self.req.http.c.setopt(FOLLOWLOCATION, 1)
- self.download(url)
-
-
-getInfo = create_getInfo(UnibytesCom)
diff --git a/module/plugins/hoster/UnrestrictLi.py b/module/plugins/hoster/UnrestrictLi.py
deleted file mode 100644
index 94ce1b845..000000000
--- a/module/plugins/hoster/UnrestrictLi.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from datetime import datetime, timedelta
-
-from module.common.json_layer import json_loads
-from module.plugins.Hoster import Hoster
-
-
-def secondsToMidnight(gmt=0):
- now = datetime.utcnow() + timedelta(hours=gmt)
- if now.hour is 0 and now.minute < 10:
- midnight = now
- else:
- midnight = now + timedelta(days=1)
- midnight = midnight.replace(hour=0, minute=10, second=0, microsecond=0)
- return int((midnight - now).total_seconds())
-
-
-class UnrestrictLi(Hoster):
- __name__ = "UnrestrictLi"
- __type__ = "hoster"
- __version__ = "0.12"
-
- __pattern__ = r'https?://(?:[^/]*\.)?(unrestrict|unr)\.li'
-
- __description__ = """Unrestrict.li hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- def setup(self):
- self.chunkLimit = 16
- self.resumeDownload = True
-
-
- def process(self, pyfile):
- if re.match(self.__pattern__, pyfile.url):
- new_url = pyfile.url
- elif not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "Unrestrict.li")
- self.fail(_("No Unrestrict.li account provided"))
- else:
- self.logDebug("Old URL: %s" % pyfile.url)
- for _i in xrange(5):
- page = self.load('https://unrestrict.li/unrestrict.php',
- post={'link': pyfile.url, 'domain': 'long'})
- self.logDebug("JSON data: " + page)
- if page != '':
- break
- else:
- self.logInfo(_("Unable to get API data, waiting 1 minute and retry"))
- self.retry(5, 60, "Unable to get API data")
-
- if 'Expired session' in page or ("You are not allowed to "
- "download from this host" in page and self.premium):
- self.account.relogin(self.user)
- self.retry()
- elif "File offline" in page:
- self.offline()
- elif "You are not allowed to download from this host" in page:
- self.fail(_("You are not allowed to download from this host"))
- elif "You have reached your daily limit for this host" in page:
- self.logWarning(_("Reached daily limit for this host"))
- self.retry(5, secondsToMidnight(gmt=2), "Daily limit for this host reached")
- elif "ERROR_HOSTER_TEMPORARILY_UNAVAILABLE" in page:
- self.logInfo(_("Hoster temporarily unavailable, waiting 1 minute and retry"))
- self.retry(5, 60, "Hoster is temporarily unavailable")
- page = json_loads(page)
- new_url = page.keys()[0]
- self.api_data = page[new_url]
-
- if new_url != pyfile.url:
- self.logDebug("New URL: " + new_url)
-
- if hasattr(self, 'api_data'):
- self.setNameSize()
-
- self.download(new_url, disposition=True)
-
- if self.getConfig("history"):
- self.load("https://unrestrict.li/history/", get={'delete': "all"})
- self.logInfo(_("Download history deleted"))
-
-
- def setNameSize(self):
- if 'name' in self.api_data:
- self.pyfile.name = self.api_data['name']
- if 'size' in self.api_data:
- self.pyfile.size = self.api_data['size']
diff --git a/module/plugins/hoster/UpleaCom.py b/module/plugins/hoster/UpleaCom.py
deleted file mode 100644
index c9b32c196..000000000
--- a/module/plugins/hoster/UpleaCom.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urljoin
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class UpleaCom(XFSHoster):
- __name__ = "UpleaCom"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'https?://(?:www\.)?uplea\.com/dl/\w{15}'
-
- __description__ = """Uplea.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Redleon", None)]
-
-
- HOSTER_DOMAIN = "uplea.com"
-
- NAME_PATTERN = r'class="agmd size18">(?P<N>.+?)<'
- SIZE_PATTERN = r'size14">(?P<S>[\d.,]+) (?P<U>[\w^_])</span>'
-
- OFFLINE_PATTERN = r'>You followed an invalid or expired link'
-
- LINK_PATTERN = r'"(http?://\w+\.uplea\.com/anonym/.*?)"'
- WAIT_PATTERN = r'timeText:([\d.]+),'
- STEP_PATTERN = r'<a href="(/step/.+)">'
-
-
- def setup(self):
- self.multiDL = False
- self.chunkLimit = 1
- self.resumeDownload = True
-
-
- def handleFree(self):
- m = re.search(self.STEP_PATTERN, self.html)
- if m is None:
- self.error("STEP_PATTERN not found")
-
- self.html = self.load(urljoin("http://uplea.com/", m.group(1)))
-
- m = re.search(self.WAIT_PATTERN, self.html)
- if m:
- self.wait(int(m.group(1)), True)
- self.retry()
-
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error("LINK_PATTERN not found")
-
- self.wait(15)
- self.download(m.group(1), disposition=True)
-
-
-getInfo = create_getInfo(UpleaCom)
diff --git a/module/plugins/hoster/UploadStationCom.py b/module/plugins/hoster/UploadStationCom.py
deleted file mode 100644
index fa6e3f693..000000000
--- a/module/plugins/hoster/UploadStationCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class UploadStationCom(DeadHoster):
- __name__ = "UploadStationCom"
- __type__ = "hoster"
- __version__ = "0.52"
-
- __pattern__ = r'http://(?:www\.)?uploadstation\.com/file/(?P<id>\w+)'
-
- __description__ = """UploadStation.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("fragonib", "fragonib[AT]yahoo[DOT]es"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(UploadStationCom)
diff --git a/module/plugins/hoster/UploadboxCom.py b/module/plugins/hoster/UploadboxCom.py
deleted file mode 100644
index 031c5f761..000000000
--- a/module/plugins/hoster/UploadboxCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class UploadboxCom(DeadHoster):
- __name__ = "Uploadbox"
- __type__ = "hoster"
- __version__ = "0.05"
-
- __pattern__ = r'http://(?:www\.)?uploadbox\.com/files/.+'
-
- __description__ = """UploadBox.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(UploadboxCom)
diff --git a/module/plugins/hoster/UploadedTo.py b/module/plugins/hoster/UploadedTo.py
deleted file mode 100644
index 7af6501eb..000000000
--- a/module/plugins/hoster/UploadedTo.py
+++ /dev/null
@@ -1,245 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://ul.to/044yug9o
-# http://ul.to/gzfhd0xs
-
-import re
-
-from time import sleep
-
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
-from module.plugins.Plugin import chunks
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.utils import html_unescape, parseFileSize
-
-
-key = "bGhGMkllZXByd2VEZnU5Y2NXbHhYVlZ5cEE1bkEzRUw=".decode('base64')
-
-
-def getID(url):
- """ returns id from file url"""
- m = re.match(UploadedTo.__pattern__, url)
- return m.group('ID')
-
-
-def getAPIData(urls):
- post = {"apikey": key}
-
- idMap = {}
-
- for i, url in enumerate(urls):
- id = getID(url)
- post['id_%s' % i] = id
- idMap[id] = url
-
- for _i in xrange(5):
- api = unicode(getURL("http://uploaded.net/api/filemultiple", post=post, decode=False), 'iso-8859-1')
- if api != "can't find request":
- break
- else:
- sleep(3)
-
- result = {}
-
- if api:
- for line in api.splitlines():
- data = line.split(",", 4)
- if data[1] in idMap:
- result[data[1]] = (data[0], data[2], data[4], data[3], idMap[data[1]])
-
- return result
-
-
-def parseFileInfo(self, url='', html=''):
- if not html and hasattr(self, "html"):
- html = self.html
-
- name = url
- size = 0
- fileid = None
-
- if re.search(self.OFFLINE_PATTERN, html):
- # File offline
- status = 1
- else:
- m = re.search(self.INFO_PATTERN, html)
- if m:
- name, fileid = html_unescape(m.group('N')), m.group('ID')
- size = parseFileSize(m.group('S'))
- status = 2
- else:
- status = 3
-
- return name, size, status, fileid
-
-
-def getInfo(urls):
- for chunk in chunks(urls, 80):
- result = []
-
- api = getAPIData(chunk)
-
- for data in api.itervalues():
- if data[0] == "online":
- result.append((html_unescape(data[2]), data[1], 2, data[4]))
-
- elif data[0] == "offline":
- result.append((data[4], 0, 1, data[4]))
-
- yield result
-
-
-class UploadedTo(Hoster):
- __name__ = "UploadedTo"
- __type__ = "hoster"
- __version__ = "0.75"
-
- __pattern__ = r'https?://(?:www\.)?(uploaded\.(to|net)|ul\.to)(/file/|/?\?id=|.*?&id=|/)(?P<ID>\w+)'
-
- __description__ = """Uploaded.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("spoob", "spoob@pyload.org"),
- ("mkaay", "mkaay@mkaay.de"),
- ("zoidberg", "zoidberg@mujmail.cz"),
- ("netpok", "netpok@gmail.com"),
- ("stickell", "l.stickell@yahoo.it")]
-
-
- INFO_PATTERN = r'<a href="file/(?P<ID>\w+)" id="filename">(?P<N>[^<]+)</a> &nbsp;\s*<small[^>]*>(?P<S>[^<]+)</small>'
- OFFLINE_PATTERN = r'<small class="cL">Error: 404</small>'
- DL_LIMIT_PATTERN = r'You have reached the max. number of possible free downloads for this hour'
-
-
- def setup(self):
- self.multiDL = self.resumeDownload = self.premium
- self.chunkLimit = 1 # critical problems with more chunks
-
- self.fileID = getID(self.pyfile.url)
- self.pyfile.url = "http://uploaded.net/file/%s" % self.fileID
-
-
- def process(self, pyfile):
- self.load("http://uploaded.net/language/en", just_header=True)
-
- api = getAPIData([pyfile.url])
-
- # TODO: fallback to parse from site, because api sometimes delivers wrong status codes
-
- if not api:
- self.logWarning(_("No response for API call"))
-
- self.html = unicode(self.load(pyfile.url, decode=False), 'iso-8859-1')
- name, size, status, self.fileID = parseFileInfo(self)
- self.logDebug(name, size, status, self.fileID)
- if status == 1:
- self.offline()
- elif status == 2:
- pyfile.name, pyfile.size = name, size
- else:
- self.error(_("file info"))
-
- elif api == 'Access denied':
- self.fail(_("API key invalid"))
-
- else:
- if self.fileID not in api:
- self.offline()
-
- self.data = api[self.fileID]
- if self.data[0] != "online":
- self.offline()
-
- pyfile.name = html_unescape(self.data[2])
-
- # pyfile.name = self.get_file_name()
-
- if self.premium:
- self.handlePremium()
- else:
- self.handleFree()
-
-
- def handlePremium(self):
- info = self.account.getAccountInfo(self.user, True)
- self.logDebug("%(name)s: Use Premium Account (%(left)sGB left)" % {"name": self.__name__,
- "left": info['trafficleft'] / 1024 / 1024})
- if int(self.data[1]) / 1024 > info['trafficleft']:
- self.logInfo(_("Not enough traffic left"))
- self.account.empty(self.user)
- self.resetAccount()
- self.fail(_("Traffic exceeded"))
-
- header = self.load("http://uploaded.net/file/%s" % self.fileID, just_header=True)
- if 'location' in header:
- #Direct download
- self.logDebug("Direct download link detected")
- self.download(header['location'])
- else:
- #Indirect download
- self.html = self.load("http://uploaded.net/file/%s" % self.fileID)
- m = re.search(r'<div class="tfree".*\s*<form method="post" action="(.*?)"', self.html)
- if m is None:
- self.fail(_("Download URL not m. Try to enable direct downloads"))
- url = m.group(1)
- self.download(url, post={})
-
-
- def handleFree(self):
- self.html = self.load(self.pyfile.url, decode=True)
-
- if 'var free_enabled = false;' in self.html:
- self.logError(_("Free-download capacities exhausted"))
- self.retry(24, 5 * 60)
-
- m = re.search(r"Current waiting period: <span>(\d+)</span> seconds", self.html)
- if m is None:
- self.fail(_("File not downloadable for free users"))
- self.setWait(int(m.group(1)))
-
- self.html = self.load("http://uploaded.net/js/download.js", decode=True)
-
- url = "http://uploaded.net/io/ticket/captcha/%s" % self.fileID
- downloadURL = ""
-
- recaptcha = ReCaptcha(self)
-
- for _i in xrange(5):
- challenge, result = recaptcha.challenge()
- options = {"recaptcha_challenge_field": challenge, "recaptcha_response_field": result}
- self.wait()
-
- result = self.load(url, post=options)
- self.logDebug("Result: %s" % result)
-
- if "limit-size" in result:
- self.fail(_("File too big for free download"))
- elif "limit-slot" in result: # Temporary restriction so just wait a bit
- self.setWait(30 * 60, True)
- self.wait()
- self.retry()
- elif "limit-parallel" in result:
- self.fail(_("Cannot download in parallel"))
- elif "limit-dl" in result or self.DL_LIMIT_PATTERN in result: # limit-dl
- self.setWait(3 * 60 * 60, True)
- self.wait()
- self.retry()
- elif '"err":"captcha"' in result:
- self.invalidCaptcha()
- elif "type:'download'" in result:
- self.correctCaptcha()
- downloadURL = re.search("url:'([^']+)", result).group(1)
- break
- else:
- self.error(_("Unknown error: %s") % result)
-
- if not downloadURL:
- self.fail(_("No Download url retrieved/all captcha attempts failed"))
-
- self.download(downloadURL, disposition=True)
- check = self.checkDownload({"limit-dl": self.DL_LIMIT_PATTERN})
- if check == "limit-dl":
- self.setWait(3 * 60 * 60, True)
- self.wait()
- self.retry()
diff --git a/module/plugins/hoster/UploadhereCom.py b/module/plugins/hoster/UploadhereCom.py
deleted file mode 100644
index 8da30be46..000000000
--- a/module/plugins/hoster/UploadhereCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class UploadhereCom(DeadHoster):
- __name__ = "UploadhereCom"
- __type__ = "hoster"
- __version__ = "0.12"
-
- __pattern__ = r'http://(?:www\.)?uploadhere\.com/\w{10}'
-
- __description__ = """Uploadhere.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(UploadhereCom)
diff --git a/module/plugins/hoster/UploadheroCom.py b/module/plugins/hoster/UploadheroCom.py
deleted file mode 100644
index 189079017..000000000
--- a/module/plugins/hoster/UploadheroCom.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# http://uploadhero.co/dl/wQBRAVSM
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class UploadheroCom(SimpleHoster):
- __name__ = "UploadheroCom"
- __type__ = "hoster"
- __version__ = "0.16"
-
- __pattern__ = r'http://(?:www\.)?uploadhero\.com?/dl/\w+'
-
- __description__ = """UploadHero.co plugin"""
- __license__ = "GPLv3"
- __authors__ = [("mcmyst", "mcmyst@hotmail.fr"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'<div class="nom_de_fichier">(?P<N>.*?)</div>'
- SIZE_PATTERN = r'Taille du fichier : </span><strong>(?P<S>.*?)</strong>'
- OFFLINE_PATTERN = r'<p class="titre_dl_2">|<div class="raison"><strong>Le lien du fichier ci-dessus n\'existe plus.'
-
- COOKIES = [("uploadhero.co", "lang", "en")]
-
- IP_BLOCKED_PATTERN = r'href="(/lightbox_block_download\.php\?min=.*?)"'
- IP_WAIT_PATTERN = r'<span id="minutes">(\d+)</span>.*\s*<span id="seconds">(\d+)</span>'
-
- CAPTCHA_PATTERN = r'"(/captchadl\.php\?\w+)"'
- FREE_URL_PATTERN = r'var magicomfg = \'<a href="(http://[^<>"]*?)"|"(http://storage\d+\.uploadhero\.co/\?d=\w+/[^<>"/]+)"'
- PREMIUM_URL_PATTERN = r'<a href="([^"]+)" id="downloadnow"'
-
-
- def handleFree(self):
- self.checkErrors()
-
- m = re.search(self.CAPTCHA_PATTERN, self.html)
- if m is None:
- self.error(_("CAPTCHA_PATTERN not found"))
- captcha_url = "http://uploadhero.co" + m.group(1)
-
- for _i in xrange(5):
- captcha = self.decryptCaptcha(captcha_url)
- self.html = self.load(self.pyfile.url, get={"code": captcha})
- m = re.search(self.FREE_URL_PATTERN, self.html)
- if m:
- self.correctCaptcha()
- download_url = m.group(1) or m.group(2)
- break
- else:
- self.invalidCaptcha()
- else:
- self.fail(_("No valid captcha code entered"))
-
- self.download(download_url)
-
-
- def handlePremium(self):
- self.logDebug("%s: Use Premium Account" % self.__name__)
- link = re.search(self.PREMIUM_URL_PATTERN, self.html).group(1)
- self.download(link)
-
-
- def checkErrors(self):
- m = re.search(self.IP_BLOCKED_PATTERN, self.html)
- if m:
- self.html = self.load("http://uploadhero.co%s" % m.group(1))
-
- m = re.search(self.IP_WAIT_PATTERN, self.html)
- wait_time = (int(m.group(1)) * 60 + int(m.group(2))) if m else 5 * 60
- self.wait(wait_time, True)
- self.retry()
-
- self.info.pop('error', None)
-
-
-getInfo = create_getInfo(UploadheroCom)
diff --git a/module/plugins/hoster/UploadingCom.py b/module/plugins/hoster/UploadingCom.py
deleted file mode 100644
index b163f2252..000000000
--- a/module/plugins/hoster/UploadingCom.py
+++ /dev/null
@@ -1,104 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from pycurl import HTTPHEADER
-
-from module.common.json_layer import json_loads
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, timestamp
-
-
-class UploadingCom(SimpleHoster):
- __name__ = "UploadingCom"
- __type__ = "hoster"
- __version__ = "0.39"
-
- __pattern__ = r'http://(?:www\.)?uploading\.com/files/(?:get/)?(?P<ID>\w+)'
-
- __description__ = """Uploading.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de"),
- ("mkaay", "mkaay@mkaay.de"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'id="file_title">(?P<N>.+)</'
- SIZE_PATTERN = r'size tip_container">(?P<S>[\d.,]+) (?P<U>[\w^_]+)<'
- OFFLINE_PATTERN = r'(Page|file) not found'
-
- COOKIES = [("uploading.com", "lang", "1"),
- (".uploading.com", "language", "1"),
- (".uploading.com", "setlang", "en"),
- (".uploading.com", "_lang", "en")]
-
-
- def process(self, pyfile):
- if not "/get/" in pyfile.url:
- pyfile.url = pyfile.url.replace("/files", "/files/get")
-
- self.html = self.load(pyfile.url, decode=True)
- self.getFileInfo()
-
- if self.premium:
- self.handlePremium()
- else:
- self.handleFree()
-
-
- def handlePremium(self):
- postData = {'action': 'get_link',
- 'code': self.info['pattern']['ID'],
- 'pass': 'undefined'}
-
- self.html = self.load('http://uploading.com/files/get/?JsHttpRequest=%d-xml' % timestamp(), post=postData)
- url = re.search(r'"link"\s*:\s*"(.*?)"', self.html)
- if url:
- url = url.group(1).replace("\\/", "/")
- self.download(url)
-
- raise Exception("Plugin defect")
-
-
- def handleFree(self):
- m = re.search('<h2>((Daily )?Download Limit)</h2>', self.html)
- if m:
- self.pyfile.error = m.group(1)
- self.logWarning(self.pyfile.error)
- self.retry(6, (6 * 60 if m.group(2) else 15) * 60, self.pyfile.error)
-
- ajax_url = "http://uploading.com/files/get/?ajax"
- self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With: XMLHttpRequest"])
- self.req.http.lastURL = self.pyfile.url
-
- res = json_loads(self.load(ajax_url, post={'action': 'second_page', 'code': self.info['pattern']['ID']}))
-
- if 'answer' in res and 'wait_time' in res['answer']:
- wait_time = int(res['answer']['wait_time'])
- self.logInfo(_("Waiting %d seconds") % wait_time)
- self.wait(wait_time)
- else:
- self.error(_("No AJAX/WAIT"))
-
- res = json_loads(self.load(ajax_url, post={'action': 'get_link', 'code': self.info['pattern']['ID'], 'pass': 'false'}))
-
- if 'answer' in res and 'link' in res['answer']:
- url = res['answer']['link']
- else:
- self.error(_("No AJAX/URL"))
-
- self.html = self.load(url)
- m = re.search(r'<form id="file_form" action="(.*?)"', self.html)
- if m:
- url = m.group(1)
- else:
- self.error(_("No URL"))
-
- self.download(url)
-
- check = self.checkDownload({"html": re.compile("\A<!DOCTYPE html PUBLIC")})
- if check == "html":
- self.logWarning(_("Redirected to a HTML page, wait 10 minutes and retry"))
- self.wait(10 * 60, True)
-
-
-getInfo = create_getInfo(UploadingCom)
diff --git a/module/plugins/hoster/UploadkingCom.py b/module/plugins/hoster/UploadkingCom.py
deleted file mode 100644
index 743e700eb..000000000
--- a/module/plugins/hoster/UploadkingCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class UploadkingCom(DeadHoster):
- __name__ = "UploadkingCom"
- __type__ = "hoster"
- __version__ = "0.14"
-
- __pattern__ = r'http://(?:www\.)?uploadking\.com/\w{10}'
-
- __description__ = """UploadKing.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
-getInfo = create_getInfo(UploadkingCom)
diff --git a/module/plugins/hoster/UpstoreNet.py b/module/plugins/hoster/UpstoreNet.py
deleted file mode 100644
index 239cc92f5..000000000
--- a/module/plugins/hoster/UpstoreNet.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.CaptchaService import ReCaptcha
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class UpstoreNet(SimpleHoster):
- __name__ = "UpstoreNet"
- __type__ = "hoster"
- __version__ = "0.03"
-
- __pattern__ = r'https?://(?:www\.)?upstore\.net/'
-
- __description__ = """Upstore.Net File Download Hoster"""
- __license__ = "GPLv3"
- __authors__ = [("igel", "igelkun@myopera.com")]
-
-
- INFO_PATTERN = r'<div class="comment">.*?</div>\s*\n<h2 style="margin:0">(?P<N>.*?)</h2>\s*\n<div class="comment">\s*\n\s*(?P<S>[\d.,]+) (?P<U>[\w^_]+)'
- OFFLINE_PATTERN = r'<span class="error">File not found</span>'
-
- WAIT_PATTERN = r'var sec = (\d+)'
- CHASH_PATTERN = r'<input type="hidden" name="hash" value="([^"]*)">'
- LINK_PATTERN = r'<a href="(https?://.*?)" target="_blank"><b>'
-
-
- def handleFree(self):
- # STAGE 1: get link to continue
- m = re.search(self.CHASH_PATTERN, self.html)
- if m is None:
- self.error(_("CHASH_PATTERN not found"))
- chash = m.group(1)
- self.logDebug("Read hash " + chash)
- # continue to stage2
- post_data = {'hash': chash, 'free': 'Slow download'}
- self.html = self.load(self.pyfile.url, post=post_data, decode=True)
-
- # STAGE 2: solv captcha and wait
- # first get the infos we need: recaptcha key and wait time
- recaptcha = ReCaptcha(self)
-
- # try the captcha 5 times
- for i in xrange(5):
- m = re.search(self.WAIT_PATTERN, self.html)
- if m is None:
- self.error(_("Wait pattern not found"))
- wait_time = int(m.group(1))
-
- # then, do the waiting
- self.wait(wait_time)
-
- # then, handle the captcha
- challenge, code = recaptcha.challenge()
- post_data['recaptcha_challenge_field'] = challenge
- post_data['recaptcha_response_field'] = code
-
- self.html = self.load(self.pyfile.url, post=post_data, decode=True)
-
- # STAGE 3: get direct link
- m = re.search(self.LINK_PATTERN, self.html, re.S)
- if m:
- break
-
- if m is None:
- self.error(_("Download link not found"))
-
- direct = m.group(1)
- self.download(direct, disposition=True)
-
-
-getInfo = create_getInfo(UpstoreNet)
diff --git a/module/plugins/hoster/UptoboxCom.py b/module/plugins/hoster/UptoboxCom.py
deleted file mode 100644
index 3c3fa45b0..000000000
--- a/module/plugins/hoster/UptoboxCom.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class UptoboxCom(XFSHoster):
- __name__ = "UptoboxCom"
- __type__ = "hoster"
- __version__ = "0.16"
-
- __pattern__ = r'https?://(?:www\.)?uptobox\.com/\w{12}'
-
- __description__ = """Uptobox.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = "uptobox.com"
-
- INFO_PATTERN = r'"para_title">(?P<N>.+) \((?P<S>[\d.,]+) (?P<U>[\w^_]+)\)'
- OFFLINE_PATTERN = r'>(File not found|Access Denied|404 Not Found)'
-
- LINK_PATTERN = r'"(https?://\w+\.uptobox\.com/d/.*?)"'
-
- ERROR_PATTERN = r'>(You have to wait.+till next download.)<' #@TODO: Check XFSHoster ERROR_PATTERN
-
-
- def setup(self):
- self.multiDL = True
- self.chunkLimit = 1
- self.resumeDownload = True
-
-
-getInfo = create_getInfo(UptoboxCom)
diff --git a/module/plugins/hoster/VeehdCom.py b/module/plugins/hoster/VeehdCom.py
deleted file mode 100644
index d894dab24..000000000
--- a/module/plugins/hoster/VeehdCom.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Hoster import Hoster
-
-
-class VeehdCom(Hoster):
- __name__ = "VeehdCom"
- __type__ = "hoster"
- __version__ = "0.23"
-
- __pattern__ = r'http://veehd\.com/video/\d+_\S+'
- __config__ = [("filename_spaces", "bool", "Allow spaces in filename", False),
- ("replacement_char", "str", "Filename replacement character", "_")]
-
- __description__ = """Veehd.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("cat", "cat@pyload")]
-
-
- def setup(self):
- self.multiDL = True
- self.req.canContinue = True
-
-
- def process(self, pyfile):
- self.download_html()
- if not self.file_exists():
- self.offline()
-
- pyfile.name = self.get_file_name()
- self.download(self.get_file_url())
-
-
- def download_html(self):
- url = self.pyfile.url
- self.logDebug("Requesting page: %s" % url)
- self.html = self.load(url)
-
-
- def file_exists(self):
- if not self.html:
- self.download_html()
-
- if '<title>Veehd</title>' in self.html:
- return False
- return True
-
-
- def get_file_name(self):
- if not self.html:
- self.download_html()
-
- m = re.search(r'<title[^>]*>([^<]+) on Veehd</title>', self.html)
- if m is None:
- self.error(_("Video title not found"))
-
- name = m.group(1)
-
- # replace unwanted characters in filename
- if self.getConfig('filename_spaces'):
- pattern = '[^\w ]+'
- else:
- pattern = '[^\w.]+'
-
- return re.sub(pattern, self.getConfig('replacement_char'), name) + '.avi'
-
-
- def get_file_url(self):
- """ returns the absolute downloadable filepath
- """
- if not self.html:
- self.download_html()
-
- m = re.search(r'<embed type="video/divx" src="(http://([^/]*\.)?veehd\.com/dl/[^"]+)"',
- self.html)
- if m is None:
- self.error(_("Embedded video url not found"))
-
- return m.group(1)
diff --git a/module/plugins/hoster/VeohCom.py b/module/plugins/hoster/VeohCom.py
deleted file mode 100644
index 6dbac397b..000000000
--- a/module/plugins/hoster/VeohCom.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class VeohCom(SimpleHoster):
- __name__ = "VeohCom"
- __type__ = "hoster"
- __version__ = "0.21"
-
- __pattern__ = r'http://(?:www\.)?veoh\.com/(tv/)?(watch|videos)/(?P<ID>v\w+)'
- __config__ = [("quality", "Low;High;Auto", "Quality", "Auto")]
-
- __description__ = """Veoh.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- NAME_PATTERN = r'<meta name="title" content="(?P<N>.*?)"'
- OFFLINE_PATTERN = r'>Sorry, we couldn\'t find the video you were looking for'
-
- URL_REPLACEMENTS = [(__pattern__ + ".*", r'http://www.veoh.com/watch/\g<ID>')]
-
- COOKIES = [("veoh.com", "lassieLocale", "en")]
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
- self.chunkLimit = -1
-
-
- def handleFree(self):
- quality = self.getConfig("quality")
- if quality == "Auto":
- quality = ("High", "Low")
- for q in quality:
- pattern = r'"fullPreviewHash%sPath":"(.+?)"' % q
- m = re.search(pattern, self.html)
- if m:
- self.pyfile.name += ".mp4"
- link = m.group(1).replace("\\", "")
- self.download(link)
- return
- else:
- self.logInfo(_("No %s quality video found") % q.upper())
- else:
- self.fail(_("No video found!"))
-
-
-getInfo = create_getInfo(VeohCom)
diff --git a/module/plugins/hoster/VidPlayNet.py b/module/plugins/hoster/VidPlayNet.py
deleted file mode 100644
index 76af05edd..000000000
--- a/module/plugins/hoster/VidPlayNet.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Test links:
-# BigBuckBunny_320x180.mp4 - 61.7 Mb - http://vidplay.net/38lkev0h3jv0
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class VidPlayNet(XFSHoster):
- __name__ = "VidPlayNet"
- __type__ = "hoster"
- __version__ = "0.04"
-
- __pattern__ = r'https?://(?:www\.)?vidplay\.net/\w{12}'
-
- __description__ = """VidPlay.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("t4skforce", "t4skforce1337[AT]gmail[DOT]com")]
-
-
- HOSTER_DOMAIN = "vidplay.net"
-
- NAME_PATTERN = r'<b>Password:</b></div>\s*<h[1-6]>(?P<N>[^<]+)</h[1-6]>'
-
-
-getInfo = create_getInfo(VidPlayNet)
diff --git a/module/plugins/hoster/VimeoCom.py b/module/plugins/hoster/VimeoCom.py
deleted file mode 100644
index 9e4abf702..000000000
--- a/module/plugins/hoster/VimeoCom.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class VimeoCom(SimpleHoster):
- __name__ = "VimeoCom"
- __type__ = "hoster"
- __version__ = "0.03"
-
- __pattern__ = r'https?://(?:www\.)?(player\.)?vimeo\.com/(video/)?(?P<ID>\d+)'
- __config__ = [("quality", "Lowest;Mobile;SD;HD;Highest", "Quality", "Highest"),
- ("original", "bool", "Try to download the original file first", True)]
-
- __description__ = """Vimeo.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- NAME_PATTERN = r'<title>(?P<N>.+) on Vimeo<'
- OFFLINE_PATTERN = r'class="exception_header"'
- TEMP_OFFLINE_PATTERN = r'Please try again in a few minutes.<'
-
- URL_REPLACEMENTS = [(__pattern__ + ".*", r'https://www.vimeo.com/\g<ID>')]
-
- COOKIES = [("vimeo.com", "language", "en")]
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
- self.chunkLimit = -1
-
-
- def handleFree(self):
- password = self.getPassword()
-
- if self.js and 'class="btn iconify_down_b"' in self.html:
- html = self.js.eval(self.load(self.pyfile.url, get={'action': "download", 'password': password}, decode=True))
- pattern = r'href="(?P<URL>http://vimeo\.com.+?)".*?\>(?P<QL>.+?) '
- else:
- id = re.match(self.__pattern__, self.pyfile.url).group("ID")
- html = self.load("https://player.vimeo.com/video/" + id, get={'password': password})
- pattern = r'"(?P<QL>\w+)":{"profile".*?"(?P<URL>http://pdl\.vimeocdn\.com.+?)"'
-
- link = dict([(l.group('QL').lower(), l.group('URL')) for l in re.finditer(pattern, html)])
-
- if self.getConfig("original"):
- if "original" in link:
- self.download(link[q])
- return
- else:
- self.logInfo(_("Original file not downloadable"))
-
- quality = self.getConfig("quality")
- if quality == "Highest":
- qlevel = ("hd", "sd", "mobile")
- elif quality == "Lowest":
- qlevel = ("mobile", "sd", "hd")
- else:
- qlevel = quality.lower()
-
- for q in qlevel:
- if q in link:
- self.download(link[q])
- return
- else:
- self.logInfo(_("No %s quality video found") % q.upper())
- else:
- self.fail(_("No video found!"))
-
-
-getInfo = create_getInfo(VimeoCom)
diff --git a/module/plugins/hoster/Vipleech4uCom.py b/module/plugins/hoster/Vipleech4uCom.py
deleted file mode 100644
index 7499294ed..000000000
--- a/module/plugins/hoster/Vipleech4uCom.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class Vipleech4uCom(DeadHoster):
- __name__ = "Vipleech4uCom"
- __type__ = "hoster"
- __version__ = "0.2"
-
- __pattern__ = r'http://(?:www\.)?vipleech4u\.com/manager\.php'
-
- __description__ = """Vipleech4u.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Kagenoshin", "kagenoshin@gmx.ch")]
-
-
-getInfo = create_getInfo(Vipleech4uCom)
diff --git a/module/plugins/hoster/WarserverCz.py b/module/plugins/hoster/WarserverCz.py
deleted file mode 100644
index c83d5c03e..000000000
--- a/module/plugins/hoster/WarserverCz.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class WarserverCz(DeadHoster):
- __name__ = "WarserverCz"
- __type__ = "hoster"
- __version__ = "0.13"
-
- __pattern__ = r'http://(?:www\.)?warserver\.cz/stahnout/\d+'
-
- __description__ = """Warserver.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
-getInfo = create_getInfo(WarserverCz)
diff --git a/module/plugins/hoster/WebshareCz.py b/module/plugins/hoster/WebshareCz.py
deleted file mode 100644
index 17aaff37c..000000000
--- a/module/plugins/hoster/WebshareCz.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.network.RequestFactory import getURL
-from module.plugins.internal.SimpleHoster import SimpleHoster
-
-
-def getInfo(urls):
- for url in urls:
- fid = re.search(WebshareCz.__pattern__, url).group('ID')
- api_data = getURL("https://webshare.cz/api/file_info/", post={'ident': fid})
-
- if 'File not found' in api_data:
- file_info = (url, 0, 1, url)
- else:
- name = re.search('<name>(.+)</name>', api_data).group(1)
- size = re.search('<size>(.+)</size>', api_data).group(1)
- file_info = (name, size, 2, url)
-
- yield file_info
-
-
-class WebshareCz(SimpleHoster):
- __name__ = "WebshareCz"
- __type__ = "hoster"
- __version__ = "0.14"
-
- __pattern__ = r'https?://(?:www\.)?webshare\.cz/(?:#/)?file/(?P<ID>\w+)'
-
- __description__ = """WebShare.cz hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- def handleFree(self):
- api_data = self.load('https://webshare.cz/api/file_link/', post={'ident': self.fid})
-
- self.logDebug("API data: " + api_data)
-
- m = re.search('<link>(.+)</link>', api_data)
- if m is None:
- self.error(_("Unable to detect direct link"))
-
- self.download(m.group(1), disposition=True)
-
-
- def getFileInfo(self):
- self.logDebug("URL: %s" % self.pyfile.url)
-
- self.fid = re.match(self.__pattern__, self.pyfile.url).group('ID')
-
- self.load(self.pyfile.url)
- api_data = self.load('https://webshare.cz/api/file_info/', post={'ident': self.fid})
-
- if 'File not found' in api_data:
- self.offline()
- else:
- self.pyfile.name = re.search('<name>(.+)</name>', api_data).group(1)
- self.pyfile.size = re.search('<size>(.+)</size>', api_data).group(1)
-
- self.logDebug("FILE NAME: %s FILE SIZE: %s" % (self.pyfile.name, self.pyfile.size))
diff --git a/module/plugins/hoster/WrzucTo.py b/module/plugins/hoster/WrzucTo.py
deleted file mode 100644
index 8e9653307..000000000
--- a/module/plugins/hoster/WrzucTo.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from pycurl import HTTPHEADER
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class WrzucTo(SimpleHoster):
- __name__ = "WrzucTo"
- __type__ = "hoster"
- __version__ = "0.02"
-
- __pattern__ = r'http://(?:www\.)?wrzuc\.to/(\w+(\.wt|\.html)|(\w+/?linki/\w+))'
-
- __description__ = """Wrzuc.to hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'id="file_info">\s*<strong>(?P<N>.*?)</strong>'
- SIZE_PATTERN = r'class="info">\s*<tr>\s*<td>(?P<S>.*?)</td>'
-
- COOKIES = [("wrzuc.to", "language", "en")]
-
-
- def setup(self):
- self.multiDL = True
-
-
- def handleFree(self):
- data = dict(re.findall(r'(md5|file): "(.*?)"', self.html))
- if len(data) != 2:
- self.error(_("No file ID"))
-
- self.req.http.c.setopt(HTTPHEADER, ["X-Requested-With: XMLHttpRequest"])
- self.req.http.lastURL = self.pyfile.url
- self.load("http://www.wrzuc.to/ajax/server/prepair", post={"md5": data['md5']})
-
- self.req.http.lastURL = self.pyfile.url
- self.html = self.load("http://www.wrzuc.to/ajax/server/download_link", post={"file": data['file']})
-
- data.update(re.findall(r'"(download_link|server_id)":"(.*?)"', self.html))
- if len(data) != 4:
- self.error(_("No download URL"))
-
- download_url = "http://%s.wrzuc.to/pobierz/%s" % (data['server_id'], data['download_link'])
- self.download(download_url)
-
-
-getInfo = create_getInfo(WrzucTo)
diff --git a/module/plugins/hoster/WuploadCom.py b/module/plugins/hoster/WuploadCom.py
deleted file mode 100644
index 8e01c067c..000000000
--- a/module/plugins/hoster/WuploadCom.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class WuploadCom(DeadHoster):
- __name__ = "WuploadCom"
- __type__ = "hoster"
- __version__ = "0.23"
-
- __pattern__ = r'http://(?:www\.)?wupload\..*?/file/((\w+/)?\d+)(/.*)?'
-
- __description__ = """Wupload.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de"),
- ("Paul King", None)]
-
-
-getInfo = create_getInfo(WuploadCom)
diff --git a/module/plugins/hoster/X7To.py b/module/plugins/hoster/X7To.py
deleted file mode 100644
index a4e4b04bd..000000000
--- a/module/plugins/hoster/X7To.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class X7To(DeadHoster):
- __name__ = "X7To"
- __type__ = "hoster"
- __version__ = "0.41"
-
- __pattern__ = r'http://(?:www\.)?x7\.to/'
-
- __description__ = """X7.to hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("ernieb", "ernieb")]
-
-
-getInfo = create_getInfo(X7To)
diff --git a/module/plugins/hoster/XFileSharingPro.py b/module/plugins/hoster/XFileSharingPro.py
deleted file mode 100644
index 0acad3dba..000000000
--- a/module/plugins/hoster/XFileSharingPro.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.internal.XFSHoster import XFSHoster, create_getInfo
-
-
-class XFileSharingPro(XFSHoster):
- __name__ = "XFileSharingPro"
- __type__ = "hoster"
- __version__ = "0.43"
-
- __pattern__ = r'^unmatchable$'
-
- __description__ = """XFileSharingPro dummy hoster plugin for hook"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- URL_REPLACEMENTS = [("/embed-", "/")]
-
-
- def _log(self, type, args):
- msg = " | ".join([str(a).strip() for a in args if a])
- logger = getattr(self.log, type)
- logger("%s: %s: %s" % (self.__name__, self.HOSTER_NAME, msg or _("%s MARK" % type.upper())))
-
-
- def init(self):
- super(XFileSharingPro, self).init()
-
- self.__pattern__ = self.core.pluginManager.hosterPlugins[self.__name__]['pattern']
-
- self.HOSTER_DOMAIN = re.match(self.__pattern__, self.pyfile.url).group(1).lower()
- self.HOSTER_NAME = "".join([str.capitalize() for str in self.HOSTER_DOMAIN.split('.')])
-
- account = self.core.accountManager.getAccountPlugin(self.HOSTER_NAME)
-
- if account and account.canUse():
- self.account = account
- elif self.account:
- self.account.HOSTER_DOMAIN = self.HOSTER_DOMAIN
- else:
- return
-
- self.user, data = self.account.selectAccount()
- self.req = self.account.getAccountRequest(self.user)
- self.premium = self.account.isPremium(self.user)
-
-
- def setup(self):
- self.chunkLimit = 1
- self.resumeDownload = self.premium
- self.multiDL = True
-
-
-getInfo = create_getInfo(XFileSharingPro)
diff --git a/module/plugins/hoster/XHamsterCom.py b/module/plugins/hoster/XHamsterCom.py
deleted file mode 100644
index c6e789fa8..000000000
--- a/module/plugins/hoster/XHamsterCom.py
+++ /dev/null
@@ -1,129 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urllib import unquote
-
-from module.common.json_layer import json_loads
-from module.plugins.Hoster import Hoster
-
-
-def clean_json(json_expr):
- json_expr = re.sub('[\n\r]', '', json_expr)
- json_expr = re.sub(' +', '', json_expr)
- json_expr = re.sub('\'', '"', json_expr)
-
- return json_expr
-
-
-class XHamsterCom(Hoster):
- __name__ = "XHamsterCom"
- __type__ = "hoster"
- __version__ = "0.12"
-
- __pattern__ = r'http://(?:www\.)?xhamster\.com/movies/.+'
- __config__ = [("type", ".mp4;.flv", "Preferred type", ".mp4")]
-
- __description__ = """XHamster.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = []
-
-
- def process(self, pyfile):
- self.pyfile = pyfile
-
- if not self.file_exists():
- self.offline()
-
- if self.getConfig("type"):
- self.desired_fmt = self.getConfig("type")
-
- pyfile.name = self.get_file_name() + self.desired_fmt
- self.download(self.get_file_url())
-
-
- def download_html(self):
- url = self.pyfile.url
- self.html = self.load(url)
-
-
- def get_file_url(self):
- """ returns the absolute downloadable filepath
- """
- if not self.html:
- self.download_html()
-
- flashvar_pattern = re.compile('flashvars = ({.*?});', re.S)
- json_flashvar = flashvar_pattern.search(self.html)
-
- if not json_flashvar:
- self.error(_("flashvar not found"))
-
- j = clean_json(json_flashvar.group(1))
- flashvars = json_loads(j)
-
- if flashvars['srv']:
- srv_url = flashvars['srv'] + '/'
- else:
- self.error(_("srv_url not found"))
-
- if flashvars['url_mode']:
- url_mode = flashvars['url_mode']
-
-
- else:
- self.error(_("url_mode not found"))
-
- if self.desired_fmt == ".mp4":
- file_url = re.search(r"<a href=\"" + srv_url + "(.+?)\"", self.html)
- if file_url is None:
- self.error(_("file_url not found"))
- file_url = file_url.group(1)
- long_url = srv_url + file_url
- self.logDebug("long_url = " + long_url)
- else:
- if flashvars['file']:
- file_url = unquote(flashvars['file'])
- else:
- self.error(_("file_url not found"))
-
- if url_mode == '3':
- long_url = file_url
- self.logDebug("long_url = " + long_url)
- else:
- long_url = srv_url + "key=" + file_url
- self.logDebug("long_url = " + long_url)
-
- return long_url
-
-
- def get_file_name(self):
- if not self.html:
- self.download_html()
-
- pattern = r'<title>(.*?) - xHamster\.com</title>'
- name = re.search(pattern, self.html)
- if name is None:
- pattern = r'<h1 >(.*)</h1>'
- name = re.search(pattern, self.html)
- if name is None:
- pattern = r'http://[www.]+xhamster\.com/movies/.*/(.*?)\.html?'
- name = re.match(file_name_pattern, self.pyfile.url)
- if name is None:
- pattern = r'<div id="element_str_id" style="display:none;">(.*)</div>'
- name = re.search(pattern, self.html)
- if name is None:
- return "Unknown"
-
- return name.group(1)
-
-
- def file_exists(self):
- """ returns True or False
- """
- if not self.html:
- self.download_html()
- if re.search(r"(.*Video not found.*)", self.html) is not None:
- return False
- else:
- return True
diff --git a/module/plugins/hoster/XVideosCom.py b/module/plugins/hoster/XVideosCom.py
deleted file mode 100644
index f70493d04..000000000
--- a/module/plugins/hoster/XVideosCom.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urllib import unquote
-
-from module.plugins.Hoster import Hoster
-
-
-class XVideosCom(Hoster):
- __name__ = "XVideos.com"
- __type__ = "hoster"
- __version__ = "0.1"
-
- __pattern__ = r'http://(?:www\.)?xvideos\.com/video(\d+)/.*'
-
- __description__ = """XVideos.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = []
-
-
- def process(self, pyfile):
- site = self.load(pyfile.url)
- pyfile.name = "%s (%s).flv" % (
- re.search(r"<h2>([^<]+)<span", site).group(1),
- re.match(self.__pattern__, pyfile.url).group(1),
- )
- self.download(unquote(re.search(r"flv_url=([^&]+)&", site).group(1)))
diff --git a/module/plugins/hoster/Xdcc.py b/module/plugins/hoster/Xdcc.py
deleted file mode 100644
index ef6da4a71..000000000
--- a/module/plugins/hoster/Xdcc.py
+++ /dev/null
@@ -1,207 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-import socket
-import struct
-import sys
-import time
-
-from os import makedirs
-from os.path import exists, join
-from select import select
-
-from module.plugins.Hoster import Hoster
-from module.utils import save_join
-
-
-class Xdcc(Hoster):
- __name__ = "Xdcc"
- __type__ = "hoster"
- __version__ = "0.32"
-
- __config__ = [("nick", "str", "Nickname", "pyload"),
- ("ident", "str", "Ident", "pyloadident"),
- ("realname", "str", "Realname", "pyloadreal")]
-
- __description__ = """Download from IRC XDCC bot"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.com")]
-
-
- def setup(self):
- self.debug = 0 # 0,1,2
- self.timeout = 30
- self.multiDL = False
-
-
- def process(self, pyfile):
- # change request type
- self.req = pyfile.m.core.requestFactory.getRequest(self.__name__, type="XDCC")
-
- self.pyfile = pyfile
- for _i in xrange(0, 3):
- try:
- nmn = self.doDownload(pyfile.url)
- self.logDebug("Download of %s finished." % nmn)
- return
- except socket.error, e:
- if hasattr(e, "errno"):
- errno = e.errno
- else:
- errno = e.args[0]
-
- if errno == 10054:
- self.logDebug("Server blocked our ip, retry in 5 min")
- self.setWait(300)
- self.wait()
- continue
-
- self.fail(_("Failed due to socket errors. Code: %d") % errno)
-
- self.fail(_("Server blocked our ip, retry again later manually"))
-
-
- def doDownload(self, url):
- self.pyfile.setStatus("waiting") # real link
-
- m = re.match(r'xdcc://(.*?)/#?(.*?)/(.*?)/#?(\d+)/?', url)
- server = m.group(1)
- chan = m.group(2)
- bot = m.group(3)
- pack = m.group(4)
- nick = self.getConfig('nick')
- ident = self.getConfig('ident')
- real = self.getConfig('realname')
-
- temp = server.split(':')
- ln = len(temp)
- if ln == 2:
- host, port = temp
- elif ln == 1:
- host, port = temp[0], 6667
- else:
- self.fail(_("Invalid hostname for IRC Server: %s") % server)
-
- #######################
- # CONNECT TO IRC AND IDLE FOR REAL LINK
- dl_time = time.time()
-
- sock = socket.socket()
- sock.connect((host, int(port)))
- if nick == "pyload":
- nick = "pyload-%d" % (time.time() % 1000) # last 3 digits
- sock.send("NICK %s\r\n" % nick)
- sock.send("USER %s %s bla :%s\r\n" % (ident, host, real))
- time.sleep(3)
- sock.send("JOIN #%s\r\n" % chan)
- sock.send("PRIVMSG %s :xdcc send #%s\r\n" % (bot, pack))
-
- # IRC recv loop
- readbuffer = ""
- done = False
- retry = None
- m = None
- while True:
-
- # done is set if we got our real link
- if done:
- break
-
- if retry:
- if time.time() > retry:
- retry = None
- dl_time = time.time()
- sock.send("PRIVMSG %s :xdcc send #%s\r\n" % (bot, pack))
-
- else:
- if (dl_time + self.timeout) < time.time(): # todo: add in config
- sock.send("QUIT :byebye\r\n")
- sock.close()
- self.fail(_("XDCC Bot did not answer"))
-
- fdset = select([sock], [], [], 0)
- if sock not in fdset[0]:
- continue
-
- readbuffer += sock.recv(1024)
- temp = readbuffer.split("\n")
- readbuffer = temp.pop()
-
- for line in temp:
- if self.debug is 2:
- print "*> " + unicode(line, errors='ignore')
- line = line.rstrip()
- first = line.split()
-
- if first[0] == "PING":
- sock.send("PONG %s\r\n" % first[1])
-
- if first[0] == "ERROR":
- self.fail(_("IRC-Error: %s") % line)
-
- msg = line.split(None, 3)
- if len(msg) != 4:
- continue
-
- msg = {
- "origin": msg[0][1:],
- "action": msg[1],
- "target": msg[2],
- "text": msg[3][1:]
- }
-
- if nick == msg['target'][0:len(nick)] and "PRIVMSG" == msg['action']:
- if msg['text'] == "\x01VERSION\x01":
- self.logDebug("Sending CTCP VERSION")
- sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface"))
- elif msg['text'] == "\x01TIME\x01":
- self.logDebug("Sending CTCP TIME")
- sock.send("NOTICE %s :%d\r\n" % (msg['origin'], time.time()))
- elif msg['text'] == "\x01LAG\x01":
- pass # don't know how to answer
-
- if not (bot == msg['origin'][0:len(bot)]
- and nick == msg['target'][0:len(nick)]
- and msg['action'] in ("PRIVMSG", "NOTICE")):
- continue
-
- if self.debug is 1:
- print "%s: %s" % (msg['origin'], msg['text'])
-
- if "You already requested that pack" in msg['text']:
- retry = time.time() + 300
-
- if "you must be on a known channel to request a pack" in msg['text']:
- self.fail(_("Wrong channel"))
-
- m = re.match('\x01DCC SEND (.*?) (\d+) (\d+)(?: (\d+))?\x01', msg['text'])
- if m:
- done = True
-
- # get connection data
- ip = socket.inet_ntoa(struct.pack('L', socket.ntohl(int(m.group(2)))))
- port = int(m.group(3))
- packname = m.group(1)
-
- if len(m.groups()) > 3:
- self.req.filesize = int(m.group(4))
-
- self.pyfile.name = packname
-
- download_folder = self.config['general']['download_folder']
- filename = save_join(download_folder, packname)
-
- self.logInfo(_("Downloading %s from %s:%d") % (packname, ip, port))
-
- self.pyfile.setStatus("downloading")
- newname = self.req.download(ip, port, filename, sock, self.pyfile.setProgress)
- if newname and newname != filename:
- self.logInfo(_("%(name)s saved as %(newname)s") % {"name": self.pyfile.name, "newname": newname})
- filename = newname
-
- # kill IRC socket
- # sock.send("QUIT :byebye\r\n")
- sock.close()
-
- self.lastDownload = filename
- return self.lastDownload
diff --git a/module/plugins/hoster/YibaishiwuCom.py b/module/plugins/hoster/YibaishiwuCom.py
deleted file mode 100644
index 3b4692933..000000000
--- a/module/plugins/hoster/YibaishiwuCom.py
+++ /dev/null
@@ -1,55 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.common.json_layer import json_loads
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class YibaishiwuCom(SimpleHoster):
- __name__ = "YibaishiwuCom"
- __type__ = "hoster"
- __version__ = "0.13"
-
- __pattern__ = r'http://(?:www\.)?(?:u\.)?115\.com/file/(?P<ID>\w+)'
-
- __description__ = """115.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- NAME_PATTERN = r'file_name: \'(?P<N>.+?)\''
- SIZE_PATTERN = r'file_size: \'(?P<S>.+?)\''
- OFFLINE_PATTERN = ur'<h3><i style="color:red;">哎呀提取码䞍存圚䞍劚搜搜看吧</i></h3>'
-
- LINK_PATTERN = r'(/\?ct=(pickcode|download)[^"\']+)'
-
-
- def handleFree(self):
- m = re.search(self.LINK_PATTERN, self.html)
- if m is None:
- self.error(_("LINK_PATTERN not found"))
- url = m.group(1)
- self.logDebug(('FREEUSER' if m.group(2) == 'download' else 'GUEST') + ' URL', url)
-
- res = json_loads(self.load("http://115.com" + url, decode=False))
- if "urls" in res:
- mirrors = res['urls']
- elif "data" in res:
- mirrors = res['data']
- else:
- mirrors = None
-
- for mr in mirrors:
- try:
- url = mr['url'].replace("\\", "")
- self.logDebug("Trying URL: " + url)
- self.download(url)
- break
- except:
- continue
- else:
- self.fail(_("No working link found"))
-
-
-getInfo = create_getInfo(YibaishiwuCom)
diff --git a/module/plugins/hoster/YoupornCom.py b/module/plugins/hoster/YoupornCom.py
deleted file mode 100644
index 5eb431ec8..000000000
--- a/module/plugins/hoster/YoupornCom.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Hoster import Hoster
-
-
-class YoupornCom(Hoster):
- __name__ = "YoupornCom"
- __type__ = "hoster"
- __version__ = "0.2"
-
- __pattern__ = r'http://(?:www\.)?youporn\.com/watch/.+'
-
- __description__ = """Youporn.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("willnix", "willnix@pyload.org")]
-
-
- def process(self, pyfile):
- self.pyfile = pyfile
-
- if not self.file_exists():
- self.offline()
-
- pyfile.name = self.get_file_name()
- self.download(self.get_file_url())
-
-
- def download_html(self):
- url = self.pyfile.url
- self.html = self.load(url, post={"user_choice": "Enter"}, cookies=False)
-
-
- def get_file_url(self):
- """ returns the absolute downloadable filepath
- """
- if not self.html:
- self.download_html()
-
- return re.search(r'(http://download\.youporn\.com/download/\d+\?save=1)">', self.html).group(1)
-
-
- def get_file_name(self):
- if not self.html:
- self.download_html()
-
- file_name_pattern = r'<title>(.+) - '
- return re.search(file_name_pattern, self.html).group(1).replace("&amp;", "&").replace("/", "") + '.flv'
-
-
- def file_exists(self):
- """ returns True or False
- """
- if not self.html:
- self.download_html()
- if re.search(r"(.*invalid video_id.*)", self.html) is not None:
- return False
- else:
- return True
diff --git a/module/plugins/hoster/YourfilesTo.py b/module/plugins/hoster/YourfilesTo.py
deleted file mode 100644
index 83ab89110..000000000
--- a/module/plugins/hoster/YourfilesTo.py
+++ /dev/null
@@ -1,87 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urllib import unquote
-
-from module.plugins.Hoster import Hoster
-
-
-class YourfilesTo(Hoster):
- __name__ = "YourfilesTo"
- __type__ = "hoster"
- __version__ = "0.21"
-
- __pattern__ = r'(http://)?(?:www\.)?yourfiles\.(to|biz)/\?d=\w+'
-
- __description__ = """Youfiles.to hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("jeix", "jeix@hasnomail.de"),
- ("skydancer", "skydancer@hasnomail.de")]
-
-
- def process(self, pyfile):
- self.pyfile = pyfile
- self.prepare()
- self.download(self.get_file_url())
-
-
- def prepare(self):
- if not self.file_exists():
- self.offline()
-
- self.pyfile.name = self.get_file_name()
-
- wait_time = self.get_waiting_time()
- self.setWait(wait_time)
- self.wait()
-
-
- def get_waiting_time(self):
- if not self.html:
- self.download_html()
-
- #var zzipitime = 15;
- m = re.search(r'var zzipitime = (\d+);', self.html)
- if m:
- sec = int(m.group(1))
- else:
- sec = 0
-
- return sec
-
-
- def download_html(self):
- url = self.pyfile.url
- self.html = self.load(url)
-
-
- def get_file_url(self):
- """ returns the absolute downloadable filepath
- """
- url = re.search(r"var bla = '(.*?)';", self.html)
- if url:
- url = url.group(1)
- url = unquote(url.replace("http://http:/http://", "http://").replace("dumdidum", ""))
- return url
- else:
- self.error(_("Absolute filepath not found"))
-
-
- def get_file_name(self):
- if not self.html:
- self.download_html()
-
- return re.search("<title>(.*)</title>", self.html).group(1)
-
-
- def file_exists(self):
- """ returns True or False
- """
- if not self.html:
- self.download_html()
-
- if re.search(r"HTTP Status 404", self.html) is not None:
- return False
- else:
- return True
diff --git a/module/plugins/hoster/YoutubeCom.py b/module/plugins/hoster/YoutubeCom.py
deleted file mode 100644
index bb0737440..000000000
--- a/module/plugins/hoster/YoutubeCom.py
+++ /dev/null
@@ -1,185 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import os
-import re
-import subprocess
-
-from urllib import unquote
-
-from module.plugins.Hoster import Hoster
-from module.plugins.internal.SimpleHoster import replace_patterns
-from module.utils import html_unescape
-
-
-def which(program):
- """Works exactly like the unix command which
-
- Courtesy of http://stackoverflow.com/a/377028/675646"""
-
- def is_exe(fpath):
- return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
-
- fpath, fname = os.path.split(program)
-
- if fpath:
- if is_exe(program):
- return program
- else:
- for path in os.environ['PATH'].split(os.pathsep):
- path = path.strip('"')
- exe_file = os.path.join(path, program)
- if is_exe(exe_file):
- return exe_file
-
- return None
-
-
-class YoutubeCom(Hoster):
- __name__ = "YoutubeCom"
- __type__ = "hoster"
- __version__ = "0.40"
-
- __pattern__ = r'https?://(?:[^/]*\.)?(?:youtube\.com|youtu\.be)/watch.*?[?&]v=.*'
- __config__ = [("quality", "sd;hd;fullhd;240p;360p;480p;720p;1080p;3072p", "Quality Setting", "hd"),
- ("fmt", "int", "FMT/ITAG Number (5-102, 0 for auto)", 0),
- (".mp4", "bool", "Allow .mp4", True),
- (".flv", "bool", "Allow .flv", True),
- (".webm", "bool", "Allow .webm", False),
- (".3gp", "bool", "Allow .3gp", False),
- ("3d", "bool", "Prefer 3D", False)]
-
- __description__ = """Youtube.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("spoob", "spoob@pyload.org"),
- ("zoidberg", "zoidberg@mujmail.cz")]
-
-
- URL_REPLACEMENTS = [(r'youtu\.be/', 'youtube.com/')]
-
- # Invalid characters that must be removed from the file name
- invalidChars = u'\u2605:?><"|\\'
-
- # name, width, height, quality ranking, 3D
- formats = {5 : (".flv" , 400 , 240 , 1 , False),
- 6 : (".flv" , 640 , 400 , 4 , False),
- 17 : (".3gp" , 176 , 144 , 0 , False),
- 18 : (".mp4" , 480 , 360 , 2 , False),
- 22 : (".mp4" , 1280, 720 , 8 , False),
- 43 : (".webm", 640 , 360 , 3 , False),
- 34 : (".flv" , 640 , 360 , 4 , False),
- 35 : (".flv" , 854 , 480 , 6 , False),
- 36 : (".3gp" , 400 , 240 , 1 , False),
- 37 : (".mp4" , 1920, 1080, 9 , False),
- 38 : (".mp4" , 4096, 3072, 10, False),
- 44 : (".webm", 854 , 480 , 5 , False),
- 45 : (".webm", 1280, 720 , 7 , False),
- 46 : (".webm", 1920, 1080, 9 , False),
- 82 : (".mp4" , 640 , 360 , 3 , True ),
- 83 : (".mp4" , 400 , 240 , 1 , True ),
- 84 : (".mp4" , 1280, 720 , 8 , True ),
- 85 : (".mp4" , 1920, 1080, 9 , True ),
- 100: (".webm", 640 , 360 , 3 , True ),
- 101: (".webm", 640 , 360 , 4 , True ),
- 102: (".webm", 1280, 720 , 8 , True )}
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
-
-
- def process(self, pyfile):
- pyfile.url = replace_patterns(pyfile.url, self.URL_REPLACEMENTS)
- html = self.load(pyfile.url, decode=True)
-
- if re.search(r'<div id="player-unavailable" class="\s*player-width player-height\s*">', html):
- self.offline()
-
- if "We have been receiving a large volume of requests from your network." in html:
- self.tempOffline()
-
- #get config
- use3d = self.getConfig("3d")
- if use3d:
- quality = {"sd": 82, "hd": 84, "fullhd": 85, "240p": 83, "360p": 82,
- "480p": 82, "720p": 84, "1080p": 85, "3072p": 85}
- else:
- quality = {"sd": 18, "hd": 22, "fullhd": 37, "240p": 5, "360p": 18,
- "480p": 35, "720p": 22, "1080p": 37, "3072p": 38}
- desired_fmt = self.getConfig("fmt")
- if desired_fmt and desired_fmt not in self.formats:
- self.logWarning(_("FMT %d unknown, using default") % desired_fmt)
- desired_fmt = 0
- if not desired_fmt:
- desired_fmt = quality.get(self.getConfig("quality"), 18)
-
- #parse available streams
- streams = re.search(r'"url_encoded_fmt_stream_map": "(.*?)",', html).group(1)
- streams = [x.split('\u0026') for x in streams.split(',')]
- streams = [dict((y.split('=', 1)) for y in x) for x in streams]
- streams = [(int(x['itag']), unquote(x['url'])) for x in streams]
- #self.logDebug("Found links: %s" % streams)
- self.logDebug("AVAILABLE STREAMS: %s" % [x[0] for x in streams])
-
- #build dictionary of supported itags (3D/2D)
- allowed = lambda x: self.getConfig(self.formats[x][0])
- streams = [x for x in streams if x[0] in self.formats and allowed(x[0])]
- if not streams:
- self.fail(_("No available stream meets your preferences"))
- fmt_dict = dict([x for x in streams if self.formats[x[0]][4] == use3d] or streams)
-
- self.logDebug("DESIRED STREAM: ITAG:%d (%s) %sfound, %sallowed" %
- (desired_fmt, "%s %dx%d Q:%d 3D:%s" % self.formats[desired_fmt],
- "" if desired_fmt in fmt_dict else "NOT ", "" if allowed(desired_fmt) else "NOT "))
-
- #return fmt nearest to quality index
- if desired_fmt in fmt_dict and allowed(desired_fmt):
- fmt = desired_fmt
- else:
- sel = lambda x: self.formats[x][3] # select quality index
- comp = lambda x, y: abs(sel(x) - sel(y))
-
- self.logDebug("Choosing nearest fmt: %s" % [(x, allowed(x), comp(x, desired_fmt)) for x in fmt_dict.keys()])
- fmt = reduce(lambda x, y: x if comp(x, desired_fmt) <= comp(y, desired_fmt) and
- sel(x) > sel(y) else y, fmt_dict.keys())
-
- self.logDebug("Chosen fmt: %s" % fmt)
- url = fmt_dict[fmt]
- self.logDebug("URL: %s" % url)
-
- #set file name
- file_suffix = self.formats[fmt][0] if fmt in self.formats else ".flv"
- file_name_pattern = '<meta name="title" content="(.+?)">'
- name = re.search(file_name_pattern, html).group(1).replace("/", "")
-
- # Cleaning invalid characters from the file name
- name = name.encode('ascii', 'replace')
- for c in self.invalidChars:
- name = name.replace(c, '_')
-
- pyfile.name = html_unescape(name)
-
- time = re.search(r"t=((\d+)m)?(\d+)s", pyfile.url)
- ffmpeg = which("ffmpeg")
- if ffmpeg and time:
- m, s = time.groups()[1:]
- if m is None:
- m = "0"
-
- pyfile.name += " (starting at %s:%s)" % (m, s)
- pyfile.name += file_suffix
-
- filename = self.download(url)
-
- if ffmpeg and time:
- inputfile = filename + "_"
- os.rename(filename, inputfile)
-
- subprocess.call([
- ffmpeg,
- "-ss", "00:%s:%s" % (m, s),
- "-i", inputfile,
- "-vcodec", "copy",
- "-acodec", "copy",
- filename])
- os.remove(inputfile)
diff --git a/module/plugins/hoster/ZDF.py b/module/plugins/hoster/ZDF.py
deleted file mode 100644
index 04a395e1e..000000000
--- a/module/plugins/hoster/ZDF.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from xml.etree.ElementTree import fromstring
-
-from module.plugins.Hoster import Hoster
-
-
-# Based on zdfm by Roland Beermann (http://github.com/enkore/zdfm/)
-class ZDF(Hoster):
- __name__ = "ZDF Mediathek"
- __type__ = "hoster"
- __version__ = "0.8"
-
- __pattern__ = r'http://(?:www\.)?zdf\.de/ZDFmediathek/\D*(\d+)\D*'
-
- __description__ = """ZDF.de hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = []
-
- XML_API = "http://www.zdf.de/ZDFmediathek/xmlservice/web/beitragsDetails?id=%i"
-
-
- @staticmethod
- def video_key(video):
- return (
- int(video.findtext("videoBitrate", "0")),
- any(f.text == "progressive" for f in video.iter("facet")),
- )
-
-
- @staticmethod
- def video_valid(video):
- return video.findtext("url").startswith("http") and video.findtext("url").endswith(".mp4") and \
- video.findtext("facets/facet").startswith("progressive")
-
-
- @staticmethod
- def get_id(url):
- return int(re.search(r"\D*(\d{4,})\D*", url).group(1))
-
-
- def process(self, pyfile):
- xml = fromstring(self.load(self.XML_API % self.get_id(pyfile.url)))
-
- status = xml.findtext("./status/statuscode")
- if status != "ok":
- self.fail(_("Error retrieving manifest"))
-
- video = xml.find("video")
- title = video.findtext("information/title")
-
- pyfile.name = title
-
- target_url = sorted((v for v in video.iter("formitaet") if self.video_valid(v)),
- key=self.video_key)[-1].findtext("url")
-
- self.download(target_url)
diff --git a/module/plugins/hoster/ZShareNet.py b/module/plugins/hoster/ZShareNet.py
deleted file mode 100644
index dc96facbe..000000000
--- a/module/plugins/hoster/ZShareNet.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.DeadHoster import DeadHoster, create_getInfo
-
-
-class ZShareNet(DeadHoster):
- __name__ = "ZShareNet"
- __type__ = "hoster"
- __version__ = "0.21"
-
- __pattern__ = r'https?://(?:ww[2w]\.)?zshares?\.net/.+'
-
- __description__ = """ZShare.net hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("espes", None),
- ("Cptn Sandwich", None)]
-
-
-getInfo = create_getInfo(ZShareNet)
diff --git a/module/plugins/hoster/ZeveraCom.py b/module/plugins/hoster/ZeveraCom.py
deleted file mode 100644
index fa2f6edb3..000000000
--- a/module/plugins/hoster/ZeveraCom.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.Hoster import Hoster
-
-
-class ZeveraCom(Hoster):
- __name__ = "ZeveraCom"
- __type__ = "hoster"
- __version__ = "0.21"
-
- __pattern__ = r'http://(?:www\.)?zevera\.com/.*'
-
- __description__ = """Zevera.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- def setup(self):
- self.resumeDownload = True
- self.multiDL = True
- self.chunkLimit = 1
-
-
- def process(self, pyfile):
- if not self.account:
- self.logError(_("Please enter your %s account or deactivate this plugin") % "zevera.com")
- self.fail(_("No zevera.com account provided"))
-
- self.logDebug("Old URL: %s" % pyfile.url)
-
- if self.account.getAPIData(self.req, cmd="checklink", olink=pyfile.url) != "Alive":
- self.fail(_("Offline or not downloadable - contact Zevera support"))
-
- header = self.account.getAPIData(self.req, just_header=True, cmd="generatedownloaddirect", olink=pyfile.url)
- if not "location" in header:
- self.fail(_("Unable to initialize download"))
-
- self.download(header['location'], disposition=True)
-
- check = self.checkDownload({"error": 'action="ErrorDownload.aspx'})
- if check == "error":
- self.fail(_("Error response received - contact Zevera support"))
diff --git a/module/plugins/hoster/ZippyshareCom.py b/module/plugins/hoster/ZippyshareCom.py
deleted file mode 100644
index 3edf3c5c1..000000000
--- a/module/plugins/hoster/ZippyshareCom.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from os.path import join
-from urlparse import urljoin
-
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-
-
-class ZippyshareCom(SimpleHoster):
- __name__ = "ZippyshareCom"
- __type__ = "hoster"
- __version__ = "0.62"
-
- __pattern__ = r'(?P<HOST>http://www\d{0,2}\.zippyshare\.com)/v(?:/|iew\.jsp.*key=)(?P<KEY>\d+)'
-
- __description__ = """Zippyshare.com hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- NAME_PATTERN = r'("\d{6,}/"[ ]*\+.+?"/|<title>Zippyshare.com - )(?P<N>.+?)("|</title>)'
- SIZE_PATTERN = r'>Size:.+?">(?P<S>[\d.,]+) (?P<U>[\w^_]+)'
-
- OFFLINE_PATTERN = r'>File does not exist on this server<'
-
- COOKIES = [("zippyshare.com", "ziplocale", "en")]
-
-
- def setup(self):
- self.multiDL = True
- self.chunkLimit = -1
- self.resumeDownload = True
-
-
- def handleFree(self):
- url = self.get_link()
- self.download(url)
-
-
- def get_checksum(self):
- try:
- m = re.search(r'\+[ ]*\((\d+)[ ]*\%[ ]*(\d+)[ ]*\+[ ]*(\d+)[ ]*\%[ ]*(\d+)\)[ ]*\+', self.html)
- if m:
- a1, a2, c1, c2 = map(int, m.groups())
- else:
- a1, a2 = map(int, re.search(r'\(\'downloadB\'\).omg = (\d+)%(\d+)', self.html).groups())
- c1, c2 = map(int, re.search(r'\(\'downloadB\'\).omg\) \* \((\d+)%(\d+)', self.html).groups())
-
- b = (a1 % a2) * (c1 % c2)
- except:
- self.error(_("Unable to calculate checksum"))
- else:
- return b + 18
-
-
- def get_link(self):
- checksum = self.get_checksum()
- p_url = join("d", self.info['pattern']['KEY'], str(checksum), self.pyfile.name)
- dl_link = urljoin(self.info['pattern']['HOST'], p_url)
- return dl_link
-
-
-getInfo = create_getInfo(ZippyshareCom)
diff --git a/module/plugins/hoster/__init__.py b/module/plugins/hoster/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/plugins/hoster/__init__.py
+++ /dev/null
diff --git a/module/plugins/internal/AbstractExtractor.py b/module/plugins/internal/AbstractExtractor.py
deleted file mode 100644
index 8a69ebb56..000000000
--- a/module/plugins/internal/AbstractExtractor.py
+++ /dev/null
@@ -1,109 +0,0 @@
-# -*- coding: utf-8 -*-
-
-class ArchiveError(Exception):
- pass
-
-
-class CRCError(Exception):
- pass
-
-
-class WrongPassword(Exception):
- pass
-
-
-class AbtractExtractor:
- __name__ = "AbtractExtractor"
- __version__ = "0.1"
-
- __description__ = """Abtract extractor plugin"""
- __license__ = "GPLv3"
- __authors__ = [("pyLoad Team", "admin@pyload.org")]
-
-
- @staticmethod
- def checkDeps():
- """ Check if system statisfy dependencies
- :return: boolean
- """
- return True
-
-
- @staticmethod
- def getTargets(files_ids):
- """ Filter suited targets from list of filename id tuple list
- :param files_ids: List of filepathes
- :return: List of targets, id tuple list
- """
- raise NotImplementedError
-
-
- def __init__(self, m, file, out, fullpath, overwrite, excludefiles, renice):
- """Initialize extractor for specific file
-
- :param m: ExtractArchive Hook plugin
- :param file: Absolute filepath
- :param out: Absolute path to destination directory
- :param fullpath: extract to fullpath
- :param overwrite: Overwrite existing archives
- :param renice: Renice value
- """
- self.m = m
- self.file = file
- self.out = out
- self.fullpath = fullpath
- self.overwrite = overwrite
- self.excludefiles = excludefiles
- self.renice = renice
- self.files = [] #: Store extracted files here
-
-
- def init(self):
- """ Initialize additional data structures """
- pass
-
-
- def checkArchive(self):
- """Check if password if needed. Raise ArchiveError if integrity is
- questionable.
-
- :return: boolean
- :raises ArchiveError
- """
- return False
-
-
- def checkPassword(self, password):
- """ Check if the given password is/might be correct.
- If it can not be decided at this point return true.
-
- :param password:
- :return: boolean
- """
- return True
-
-
- def extract(self, progress, password=None):
- """Extract the archive. Raise specific errors in case of failure.
-
- :param progress: Progress function, call this to update status
- :param password password to use
- :raises WrongPassword
- :raises CRCError
- :raises ArchiveError
- :return:
- """
- raise NotImplementedError
-
-
- def getDeleteFiles(self):
- """Return list of files to delete, do *not* delete them here.
-
- :return: List with paths of files to delete
- """
- raise NotImplementedError
-
-
- def getExtractedFiles(self):
- """Populate self.files at some point while extracting"""
- return self.files
diff --git a/module/plugins/internal/CaptchaService.py b/module/plugins/internal/CaptchaService.py
deleted file mode 100644
index 7009e6986..000000000
--- a/module/plugins/internal/CaptchaService.py
+++ /dev/null
@@ -1,213 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from random import random
-
-
-class CaptchaService:
- __name__ = "CaptchaService"
- __version__ = "0.15"
-
- __description__ = """Base captcha service plugin"""
- __license__ = "GPLv3"
- __authors__ = [("pyLoad Team", "admin@pyload.org")]
-
-
- KEY_PATTERN = None
-
- key = None #: last key detected
-
-
- def __init__(self, plugin):
- self.plugin = plugin
-
-
- def detect_key(self, html=None):
- if not html:
- if hasattr(self.plugin, "html") and self.plugin.html:
- html = self.plugin.html
- else:
- errmsg = _("%s html not found") % self.__name__
- self.plugin.fail(errmsg) #@TODO: replace all plugin.fail(errmsg) with plugin.error(errmsg) in 0.4.10
- raise TypeError(errmsg)
-
- m = re.search(self.KEY_PATTERN, html)
- if m:
- self.key = m.group("KEY")
- self.plugin.logDebug("%s key: %s" % (self.__name__, self.key))
- return self.key
- else:
- self.plugin.logDebug("%s key not found" % self.__name__)
- return None
-
-
- def challenge(self, key=None):
- raise NotImplementedError
-
-
- def result(self, server, challenge):
- raise NotImplementedError
-
-
-class ReCaptcha(CaptchaService):
- __name__ = "ReCaptcha"
- __version__ = "0.08"
-
- __description__ = """ReCaptcha captcha service plugin"""
- __license__ = "GPLv3"
- __authors__ = [("pyLoad Team", "admin@pyload.org")]
-
-
- KEY_PATTERN = r'recaptcha(/api|\.net)/(challenge|noscript)\?k=(?P<KEY>[\w-]+)'
- KEY_AJAX_PATTERN = r'Recaptcha\.create\s*\(\s*["\'](?P<KEY>[\w-]+)'
-
-
- def detect_key(self, html=None):
- if not html:
- if hasattr(self.plugin, "html") and self.plugin.html:
- html = self.plugin.html
- else:
- errmsg = _("ReCaptcha html not found")
- self.plugin.fail(errmsg)
- raise TypeError(errmsg)
-
- m = re.search(self.KEY_PATTERN, html) or re.search(self.KEY_AJAX_PATTERN, html)
- if m:
- self.key = m.group("KEY")
- self.plugin.logDebug("ReCaptcha key: %s" % self.key)
- return self.key
- else:
- self.plugin.logDebug("ReCaptcha key not found")
- return None
-
-
- def challenge(self, key=None):
- if not key:
- if self.detect_key():
- key = self.key
- else:
- errmsg = _("ReCaptcha key not found")
- self.plugin.fail(errmsg)
- raise TypeError(errmsg)
-
- js = self.plugin.req.load("http://www.google.com/recaptcha/api/challenge", get={'k': key})
- try:
- challenge = re.search("challenge : '(.+?)',", js).group(1)
- server = re.search("server : '(.+?)',", js).group(1)
- except:
- self.plugin.error("ReCaptcha challenge pattern not found")
-
- result = self.result(server, challenge)
-
- self.plugin.logDebug("ReCaptcha result: %s" % result, "challenge: %s" % challenge)
-
- return challenge, result
-
-
- def result(self, server, challenge):
- return self.plugin.decryptCaptcha("%simage" % server, get={'c': challenge},
- cookies=True, forceUser=True, imgtype="jpg")
-
-
-class AdsCaptcha(CaptchaService):
- __name__ = "AdsCaptcha"
- __version__ = "0.05"
-
- __description__ = """AdsCaptcha captcha service plugin"""
- __license__ = "GPLv3"
- __authors__ = [("pyLoad Team", "admin@pyload.org")]
-
-
- ID_PATTERN = r'api\.adscaptcha\.com/Get\.aspx\?[^"\']*CaptchaId=(?P<ID>\d+)'
- KEY_PATTERN = r'api\.adscaptcha\.com/Get\.aspx\?[^"\']*PublicKey=(?P<KEY>[\w-]+)'
-
-
- def detect_key(self, html=None):
- if not html:
- if hasattr(self.plugin, "html") and self.plugin.html:
- html = self.plugin.html
- else:
- errmsg = _("AdsCaptcha html not found")
- self.plugin.fail(errmsg)
- raise TypeError(errmsg)
-
- m = re.search(self.ID_PATTERN, html)
- n = re.search(self.KEY_PATTERN, html)
- if m and n:
- self.key = (m.group("ID"), m.group("KEY"))
- self.plugin.logDebug("AdsCaptcha id|key: %s | %s" % self.key)
- return self.key
- else:
- self.plugin.logDebug("AdsCaptcha id or key not found")
- return None
-
-
- def challenge(self, key=None): #: key is a tuple(CaptchaId, PublicKey)
- if not key:
- if self.detect_key():
- key = self.key
- else:
- errmsg = _("AdsCaptcha key not found")
- self.plugin.fail(errmsg)
- raise TypeError(errmsg)
-
- CaptchaId, PublicKey = key
-
- js = self.plugin.req.load("http://api.adscaptcha.com/Get.aspx", get={'CaptchaId': CaptchaId, 'PublicKey': PublicKey})
- try:
- challenge = re.search("challenge: '(.+?)',", js).group(1)
- server = re.search("server: '(.+?)',", js).group(1)
- except:
- self.plugin.error("AdsCaptcha challenge pattern not found")
-
- result = self.result(server, challenge)
-
- self.plugin.logDebug("AdsCaptcha result: %s" % result, "challenge: %s" % challenge)
-
- return challenge, result
-
-
- def result(self, server, challenge):
- return self.plugin.decryptCaptcha("%sChallenge.aspx" % server, get={'cid': challenge, 'dummy': random()},
- cookies=True, imgtype="jpg")
-
-
-class SolveMedia(CaptchaService):
- __name__ = "SolveMedia"
- __version__ = "0.06"
-
- __description__ = """SolveMedia captcha service plugin"""
- __license__ = "GPLv3"
- __authors__ = [("pyLoad Team", "admin@pyload.org")]
-
-
- KEY_PATTERN = r'api\.solvemedia\.com/papi/challenge\.(no)?script\?k=(?P<KEY>.+?)["\']'
-
-
- def challenge(self, key=None):
- if not key:
- if self.detect_key():
- key = self.key
- else:
- errmsg = _("SolveMedia key not found")
- self.plugin.fail(errmsg)
- raise TypeError(errmsg)
-
- html = self.plugin.req.load("http://api.solvemedia.com/papi/challenge.noscript", get={'k': key})
- try:
- challenge = re.search(r'<input type=hidden name="adcopy_challenge" id="adcopy_challenge" value="([^"]+)">',
- html).group(1)
- server = "http://api.solvemedia.com/papi/media"
- except:
- self.plugin.error("SolveMedia challenge pattern not found")
-
- result = self.result(server, challenge)
-
- self.plugin.logDebug("SolveMedia result: %s" % result, "challenge: %s" % challenge)
-
- return challenge, result
-
-
- def result(self, server, challenge):
- return self.plugin.decryptCaptcha(server, get={'c': challenge}, imgtype="gif")
diff --git a/module/plugins/internal/DeadCrypter.py b/module/plugins/internal/DeadCrypter.py
deleted file mode 100644
index 07c5c3881..000000000
--- a/module/plugins/internal/DeadCrypter.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from urllib import unquote
-from urlparse import urlparse
-
-from module.plugins.internal.SimpleCrypter import create_getInfo
-from module.plugins.Crypter import Crypter as _Crypter
-
-
-class DeadCrypter(_Crypter):
- __name__ = "DeadCrypter"
- __type__ = "crypter"
- __version__ = "0.04"
-
- __pattern__ = r'^unmatchable$'
-
- __description__ = """ Crypter is no longer available """
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it")]
-
-
- @classmethod
- def getInfo(cls, url="", html=""):
- return {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 1, 'url': url}
-
-
- def setup(self):
- self.pyfile.error = "Crypter is no longer available"
- self.offline() #@TODO: self.offline("Crypter is no longer available")
-
-
-getInfo = create_getInfo(DeadCrypter)
diff --git a/module/plugins/internal/DeadHoster.py b/module/plugins/internal/DeadHoster.py
deleted file mode 100644
index 6f3252f70..000000000
--- a/module/plugins/internal/DeadHoster.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from urllib import unquote
-from urlparse import urlparse
-
-from module.plugins.internal.SimpleHoster import create_getInfo
-from module.plugins.Hoster import Hoster as _Hoster
-
-
-class DeadHoster(_Hoster):
- __name__ = "DeadHoster"
- __type__ = "hoster"
- __version__ = "0.14"
-
- __pattern__ = r'^unmatchable$'
-
- __description__ = """ Hoster is no longer available """
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz")]
-
-
- @classmethod
- def getInfo(cls, url="", html=""):
- return {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 1, 'url': url}
-
-
- def setup(self):
- self.pyfile.error = "Hoster is no longer available"
- self.offline() #@TODO: self.offline("Hoster is no longer available")
-
-
-getInfo = create_getInfo(DeadHoster)
diff --git a/module/plugins/internal/MultiHoster.py b/module/plugins/internal/MultiHoster.py
deleted file mode 100644
index 6ec2e4b82..000000000
--- a/module/plugins/internal/MultiHoster.py
+++ /dev/null
@@ -1,206 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from module.plugins.Hook import Hook
-from module.utils import remove_chars
-
-
-class MultiHoster(Hook):
- __name__ = "MultiHoster"
- __type__ = "hook"
- __version__ = "0.20"
-
- __description__ = """Generic MultiHoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("pyLoad Team", "admin@pyload.org")]
-
-
- interval = 12 * 60 * 60 #: reload hosters every 12h
-
- HOSTER_REPLACEMENTS = [("1fichier.com", "onefichier.com"), ("2shared.com", "twoshared.com"),
- ("4shared.com", "fourshared.com"), ("cloudnator.com", "shragle.com"),
- ("easy-share.com", "crocko.com"), ("freakshare.net", "freakshare.com"),
- ("hellshare.com", "hellshare.cz"), ("ifile.it", "filecloud.io"),
- ("putlocker.com", "firedrive.com"), ("share-rapid.cz", "multishare.cz"),
- ("sharerapid.cz", "multishare.cz"), ("ul.to", "uploaded.to"),
- ("uploaded.net", "uploaded.to")]
- HOSTER_EXCLUDED = []
-
-
- def setup(self):
- self.hosters = []
- self.supported = []
- self.new_supported = []
-
-
- def getConfig(self, option, default=''):
- """getConfig with default value - sublass may not implements all config options"""
- try:
- return self.getConf(option)
- except KeyError:
- return default
-
-
- def getHosterCached(self):
- if not self.hosters:
- try:
- hosterSet = self.toHosterSet(self.getHoster()) - set(self.HOSTER_EXCLUDED)
- except Exception, e:
- self.logError(e)
- return []
-
- try:
- configMode = self.getConfig('hosterListMode', 'all')
- if configMode in ("listed", "unlisted"):
- configSet = self.toHosterSet(self.getConfig('hosterList', '').replace('|', ',').replace(';', ',').split(','))
-
- if configMode == "listed":
- hosterSet &= configSet
- else:
- hosterSet -= configSet
-
- except Exception, e:
- self.logError(e)
-
- self.hosters = list(hosterSet)
-
- return self.hosters
-
-
- def toHosterSet(self, hosters):
- hosters = set((str(x).strip().lower() for x in hosters))
-
- for rep in self.HOSTER_REPLACEMENTS:
- if rep[0] in hosters:
- hosters.remove(rep[0])
- hosters.add(rep[1])
-
- hosters.discard('')
- return hosters
-
-
- def getHoster(self):
- """Load list of supported hoster
-
- :return: List of domain names
- """
- raise NotImplementedError
-
-
- def coreReady(self):
- if self.cb:
- self.core.scheduler.removeJob(self.cb)
-
- self.setConfig("activated", True) #: config not in sync after plugin reload
-
- cfg_interval = self.getConfig("interval", None) #: reload interval in hours
- if cfg_interval is not None:
- self.interval = cfg_interval * 60 * 60
-
- if self.interval:
- self._periodical()
- else:
- self.periodical()
-
-
- def initPeriodical(self):
- pass
-
-
- def periodical(self):
- """reload hoster list periodically"""
- self.logInfo(_("Reloading supported hoster list"))
-
- old_supported = self.supported
- self.supported = []
- self.new_supported = []
- self.hosters = []
-
- self.overridePlugins()
-
- old_supported = [hoster for hoster in old_supported if hoster not in self.supported]
- if old_supported:
- self.logDebug("UNLOAD", ", ".join(old_supported))
- for hoster in old_supported:
- self.unloadHoster(hoster)
-
-
- def overridePlugins(self):
- pluginMap = dict((name.lower(), name) for name in self.core.pluginManager.hosterPlugins.keys())
- accountList = [name.lower() for name, data in self.core.accountManager.accounts.iteritems() if data]
- excludedList = []
-
- for hoster in self.getHosterCached():
- name = remove_chars(hoster.lower(), "-.")
-
- if name in accountList:
- excludedList.append(hoster)
- else:
- if name in pluginMap:
- self.supported.append(pluginMap[name])
- else:
- self.new_supported.append(hoster)
-
- if not self.supported and not self.new_supported:
- self.logError(_("No Hoster loaded"))
- return
-
- module = self.core.pluginManager.getPlugin(self.__name__)
- klass = getattr(module, self.__name__)
-
- # inject plugin plugin
- self.logDebug("Overwritten Hosters", ", ".join(sorted(self.supported)))
- for hoster in self.supported:
- dict = self.core.pluginManager.hosterPlugins[hoster]
- dict['new_module'] = module
- dict['new_name'] = self.__name__
-
- if excludedList:
- self.logInfo(_("The following hosters were not overwritten - account exists"), ", ".join(sorted(excludedList)))
-
- if self.new_supported:
- self.logDebug("New Hosters", ", ".join(sorted(self.new_supported)))
-
- # create new regexp
- regexp = r'.*(%s).*' % "|".join([x.replace(".", "\.") for x in self.new_supported])
- if hasattr(klass, "__pattern__") and isinstance(klass.__pattern__, basestring) and '://' in klass.__pattern__:
- regexp = r'%s|%s' % (klass.__pattern__, regexp)
-
- self.logDebug("Regexp", regexp)
-
- dict = self.core.pluginManager.hosterPlugins[self.__name__]
- dict['pattern'] = regexp
- dict['re'] = re.compile(regexp)
-
-
- def unloadHoster(self, hoster):
- dict = self.core.pluginManager.hosterPlugins[hoster]
- if "module" in dict:
- del dict['module']
-
- if "new_module" in dict:
- del dict['new_module']
- del dict['new_name']
-
-
- def unload(self):
- """Remove override for all hosters. Scheduler job is removed by hookmanager"""
- for hoster in self.supported:
- self.unloadHoster(hoster)
-
- # reset pattern
- klass = getattr(self.core.pluginManager.getPlugin(self.__name__), self.__name__)
- dict = self.core.pluginManager.hosterPlugins[self.__name__]
- dict['pattern'] = getattr(klass, "__pattern__", r'^unmatchable$')
- dict['re'] = re.compile(dict['pattern'])
-
-
- def downloadFailed(self, pyfile):
- """remove plugin override if download fails but not if file is offline/temp.offline"""
- if pyfile.hasStatus("failed") and self.getConfig("unloadFailing", True):
- hdict = self.core.pluginManager.hosterPlugins[pyfile.pluginname]
- if "new_name" in hdict and hdict['new_name'] == self.__name__:
- self.logDebug("Unload MultiHoster", pyfile.pluginname, hdict)
- self.unloadHoster(pyfile.pluginname)
- pyfile.setStatus("queued")
diff --git a/module/plugins/internal/SimpleCrypter.py b/module/plugins/internal/SimpleCrypter.py
deleted file mode 100644
index 53ffaf4a6..000000000
--- a/module/plugins/internal/SimpleCrypter.py
+++ /dev/null
@@ -1,165 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from urlparse import urlparse
-
-from module.plugins.Crypter import Crypter
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo, replace_patterns, set_cookies
-from module.utils import fixup
-
-
-class SimpleCrypter(Crypter, SimpleHoster):
- __name__ = "SimpleCrypter"
- __type__ = "crypter"
- __version__ = "0.32"
-
- __pattern__ = r'^unmatchable$'
- __config__ = [("use_subfolder", "bool", "Save package to subfolder", True), #: Overrides core.config['general']['folder_per_package']
- ("subfolder_per_package", "bool", "Create a subfolder for each package", True)]
-
- __description__ = """Simple decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("stickell", "l.stickell@yahoo.it"),
- ("zoidberg", "zoidberg@mujmail.cz"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- """
- Following patterns should be defined by each crypter:
-
- LINK_PATTERN: group(1) must be a download link or a regex to catch more links
- example: LINK_PATTERN = r'<div class="link"><a href="(.+?)"'
-
- NAME_PATTERN: (optional) folder name or webpage title
- example: NAME_PATTERN = r'<title>Files of: (?P<N>[^<]+) folder</title>'
-
- OFFLINE_PATTERN: (optional) Checks if the file is yet available online
- example: OFFLINE_PATTERN = r'File (deleted|not found)'
-
- TEMP_OFFLINE_PATTERN: (optional) Checks if the file is temporarily offline
- example: TEMP_OFFLINE_PATTERN = r'Server maintainance'
-
-
- You can override the getLinks method if you need a more sophisticated way to extract the links.
-
-
- If the links are splitted on multiple pages you can define the PAGES_PATTERN regex:
-
- PAGES_PATTERN: (optional) group(1) should be the number of overall pages containing the links
- example: PAGES_PATTERN = r'Pages: (\d+)'
-
- and its loadPage method:
-
-
- def loadPage(self, page_n):
- return the html of the page number page_n
- """
-
- LINK_PATTERN = None
-
- NAME_REPLACEMENTS = [("&#?\w+;", fixup)]
- URL_REPLACEMENTS = []
-
- TEXT_ENCODING = False #: Set to True or encoding name if encoding in http header is not correct
- COOKIES = True #: or False or list of tuples [(domain, name, value)]
-
- LOGIN_ACCOUNT = False
- LOGIN_PREMIUM = False
-
-
- #@TODO: Remove in 0.4.10
- def init(self):
- account_name = (self.__name__ + ".py").replace("Folder.py", "").replace(".py", "")
- account = self.core.accountManager.getAccountPlugin(account_name)
-
- if account and account.canUse():
- self.user, data = account.selectAccount()
- self.req = account.getAccountRequest(self.user)
- self.premium = account.isPremium(self.user)
-
- self.account = account
-
-
- def prepare(self):
- if self.LOGIN_ACCOUNT and not self.account:
- self.fail(_("Required account not found"))
-
- if self.LOGIN_PREMIUM and not self.premium:
- self.fail(_("Required premium account not found"))
-
- self.info = {}
- self.links = []
-
- self.req.setOption("timeout", 120)
-
- if isinstance(self.COOKIES, list):
- set_cookies(self.req.cj, self.COOKIES)
-
- self.pyfile.url = replace_patterns(self.pyfile.url, self.URL_REPLACEMENTS)
-
-
- def decrypt(self, pyfile):
- self.prepare()
-
- self.preload()
-
- if self.html is None:
- self.fail(_("No html retrieved"))
-
- self.checkInfo()
-
- self.links = self.getLinks()
-
- if hasattr(self, 'PAGES_PATTERN') and hasattr(self, 'loadPage'):
- self.handleMultiPages()
-
- self.logDebug("Package has %d links" % len(self.links))
-
- if self.links:
- self.packages = [(self.info['name'], self.links, self.info['folder'])]
-
-
- def checkStatus(self):
- status = self.info['status']
-
- if status is 1:
- self.offline()
-
- elif status is 6:
- self.tempOffline()
-
-
- def checkNameSize(self):
- name = self.info['name']
- url = self.info['url']
-
- if name and name != url:
- self.pyfile.name = name
- else:
- self.pyfile.name = self.info['name'] = urlparse(name).path.split('/')[-1]
-
- folder = self.info['folder'] = self.pyfile.name
-
- self.logDebug("File name: %s" % self.pyfile.name,
- "File folder: %s" % folder)
-
-
- def getLinks(self):
- """
- Returns the links extracted from self.html
- You should override this only if it's impossible to extract links using only the LINK_PATTERN.
- """
- return re.findall(self.LINK_PATTERN, self.html)
-
-
- def handleMultiPages(self):
- try:
- m = re.search(self.PAGES_PATTERN, self.html)
- pages = int(m.group(1))
- except:
- pages = 1
-
- for p in xrange(2, pages + 1):
- self.html = self.loadPage(p)
- self.links += self.getLinks()
diff --git a/module/plugins/internal/SimpleHoster.py b/module/plugins/internal/SimpleHoster.py
deleted file mode 100644
index 530b67692..000000000
--- a/module/plugins/internal/SimpleHoster.py
+++ /dev/null
@@ -1,569 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import time
-from urllib import unquote
-from urlparse import urljoin, urlparse
-
-from module.PyFile import statusMap as _statusMap
-from module.network.CookieJar import CookieJar
-from module.network.RequestFactory import getURL
-from module.plugins.Hoster import Hoster
-from module.plugins.Plugin import Fail
-from module.utils import fixup, parseFileSize
-
-
-#@TODO: Adapt and move to PyFile in 0.4.10
-statusMap = dict((v, k) for k, v in _statusMap.iteritems())
-
-
-#@TODO: Remove in 0.4.10 and redirect to self.error instead
-def _error(self, reason, type):
- if not reason and not type:
- type = "unknown"
-
- msg = _("%s error") % type.strip().capitalize() if type else _("Error")
- msg += ": %s" % reason.strip() if reason else ""
- msg += _(" | Plugin may be out of date")
-
- raise Fail(msg)
-
-
-#@TODO: Remove in 0.4.10
-def _wait(self, seconds, reconnect):
- if seconds:
- self.setWait(int(seconds) + 1)
-
- if reconnect is not None:
- self.wantReconnect = reconnect
-
- super(SimpleHoster, self).wait()
-
-
-def replace_patterns(string, ruleslist):
- for r in ruleslist:
- rf, rt = r
- string = re.sub(rf, rt, string)
- return string
-
-
-def set_cookies(cj, cookies):
- for cookie in cookies:
- if isinstance(cookie, tuple) and len(cookie) == 3:
- domain, name, value = cookie
- cj.setCookie(domain, name, value)
-
-
-def parseHtmlTagAttrValue(attr_name, tag):
- m = re.search(r"%s\s*=\s*([\"']?)((?<=\")[^\"]+|(?<=')[^']+|[^>\s\"'][^>\s]*)\1" % attr_name, tag, re.I)
- return m.group(2) if m else None
-
-
-def parseHtmlForm(attr_str, html, input_names={}):
- for form in re.finditer(r"(?P<TAG><form[^>]*%s[^>]*>)(?P<CONTENT>.*?)</?(form|body|html)[^>]*>" % attr_str,
- html, re.S | re.I):
- inputs = {}
- action = parseHtmlTagAttrValue("action", form.group('TAG'))
-
- for inputtag in re.finditer(r'(<(input|textarea)[^>]*>)([^<]*(?=</\2)|)', form.group('CONTENT'), re.S | re.I):
- name = parseHtmlTagAttrValue("name", inputtag.group(1))
- if name:
- value = parseHtmlTagAttrValue("value", inputtag.group(1))
- if not value:
- inputs[name] = inputtag.group(3) or ''
- else:
- inputs[name] = value
-
- if input_names:
- # check input attributes
- for key, val in input_names.iteritems():
- if key in inputs:
- if isinstance(val, basestring) and inputs[key] == val:
- continue
- elif isinstance(val, tuple) and inputs[key] in val:
- continue
- elif hasattr(val, "search") and re.match(val, inputs[key]):
- continue
- break #: attibute value does not match
- else:
- break #: attibute name does not match
- else:
- return action, inputs #: passed attribute check
- else:
- # no attribute check
- return action, inputs
-
- return {}, None #: no matching form found
-
-
-#: Deprecated
-def parseFileInfo(plugin, url="", html=""):
- info = plugin.getInfo(url, html)
- return info['name'], info['size'], info['status'], info['url']
-
-
-#@TODO: Remove in 0.4.10
-#@NOTE: Every plugin must have own parseInfo classmethod to work with 0.4.10
-def create_getInfo(plugin):
- return lambda urls: [(info['name'], info['size'], info['status'], info['url']) for info in plugin.parseInfo(urls)]
-
-
-def timestamp():
- return int(time() * 1000)
-
-
-#@TODO: Move to hoster class in 0.4.10
-def _isDirectLink(self, url, resumable=True):
- header = self.load(url, ref=True, just_header=True, decode=True)
-
- if not 'location' in header or not header['location']:
- return ""
-
- location = header['location']
-
- resumable = False #@NOTE: Testing...
-
- if resumable: #: sometimes http code may be wrong...
- if 'location' in self.load(location, ref=True, cookies=True, just_header=True, decode=True):
- return ""
- else:
- if not 'code' in header or header['code'] != 302:
- return ""
-
- if urlparse(location).scheme:
- link = location
- else:
- p = urlparse(url)
- base = "%s://%s" % (p.scheme, p.netloc)
- link = urljoin(base, location)
-
- return link
-
-
-class SimpleHoster(Hoster):
- __name__ = "SimpleHoster"
- __type__ = "hoster"
- __version__ = "0.71"
-
- __pattern__ = r'^unmatchable$'
-
- __description__ = """Simple hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- """
- Info patterns should be defined by each hoster:
-
- INFO_PATTERN: (optional) Name and Size of the file
- example: INFO_PATTERN = r'(?P<N>file_name) (?P<S>file_size) (?P<U>size_unit)'
- or
- NAME_PATTERN: (optional) Name that will be set for the file
- example: NAME_PATTERN = r'(?P<N>file_name)'
- SIZE_PATTERN: (optional) Size that will be checked for the file
- example: SIZE_PATTERN = r'(?P<S>file_size) (?P<U>size_unit)'
-
- HASHSUM_PATTERN: (optional) Hash code and type of the file
- example: HASHSUM_PATTERN = r'(?P<H>hash_code) (?P<T>MD5)'
-
- OFFLINE_PATTERN: (optional) Check if the file is yet available online
- example: OFFLINE_PATTERN = r'File (deleted|not found)'
-
- TEMP_OFFLINE_PATTERN: (optional) Check if the file is temporarily offline
- example: TEMP_OFFLINE_PATTERN = r'Server (maintenance|maintainance)'
-
-
- Error handling patterns are all optional:
-
- WAIT_PATTERN: (optional) Detect waiting time
- example: WAIT_PATTERN = r''
-
- PREMIUM_ONLY_PATTERN: (optional) Check if the file can be downloaded only with a premium account
- example: PREMIUM_ONLY_PATTERN = r'Premium account required'
-
- ERROR_PATTERN: (optional) Detect any error preventing download
- example: ERROR_PATTERN = r''
-
-
- Instead overriding handleFree and handlePremium methods you can define the following patterns for direct download:
-
- LINK_FREE_PATTERN: (optional) group(1) should be the direct link for free download
- example: LINK_FREE_PATTERN = r'<div class="link"><a href="(.+?)"'
-
- LINK_PREMIUM_PATTERN: (optional) group(1) should be the direct link for premium download
- example: LINK_PREMIUM_PATTERN = r'<div class="link"><a href="(.+?)"'
- """
-
- NAME_REPLACEMENTS = [("&#?\w+;", fixup)]
- SIZE_REPLACEMENTS = []
- URL_REPLACEMENTS = []
-
- TEXT_ENCODING = False #: Set to True or encoding name if encoding value in http header is not correct
- COOKIES = True #: or False or list of tuples [(domain, name, value)]
- FORCE_CHECK_TRAFFIC = False #: Set to True to force checking traffic left for premium account
- CHECK_DIRECT_LINK = None #: Set to True to check for direct link, set to None to do it only if self.account is True
- MULTI_HOSTER = False #: Set to True to leech other hoster link (according its multihoster hook if available)
- CONTENT_DISPOSITION = False #: Set to True to replace file name with content-disposition value from http header
-
-
- @classmethod
- def parseInfo(cls, urls):
- for url in urls:
- url = replace_patterns(url, cls.FILE_URL_REPLACEMENTS if hasattr(cls, "FILE_URL_REPLACEMENTS") else cls.URL_REPLACEMENTS) #@TODO: Remove FILE_URL_REPLACEMENTS check in 0.4.10
- yield cls.getInfo(url)
-
-
- @classmethod
- def getInfo(cls, url="", html=""):
- info = {'name': urlparse(unquote(url)).path.split('/')[-1] or _("Unknown"), 'size': 0, 'status': 3, 'url': url}
-
- if not html:
- try:
- if not url:
- info['error'] = "missing url"
- info['status'] = 1
- raise
-
- try:
- html = getURL(url, cookies=cls.COOKIES, decode=not cls.TEXT_ENCODING)
-
- if isinstance(cls.TEXT_ENCODING, basestring):
- html = unicode(html, cls.TEXT_ENCODING)
-
- except BadHeader, e:
- info['error'] = "%d: %s" % (e.code, e.content)
-
- if e.code is 404:
- info['status'] = 1
- raise
-
- if e.code is 503:
- info['status'] = 6
- raise
- except:
- return info
-
- online = False
-
- if hasattr(cls, "OFFLINE_PATTERN") and re.search(cls.OFFLINE_PATTERN, html):
- info['status'] = 1
-
- elif hasattr(cls, "FILE_OFFLINE_PATTERN") and re.search(cls.FILE_OFFLINE_PATTERN, html): #@TODO: Remove in 0.4.10
- info['status'] = 1
-
- elif hasattr(cls, "TEMP_OFFLINE_PATTERN") and re.search(cls.TEMP_OFFLINE_PATTERN, html):
- info['status'] = 6
-
- else:
- try:
- info['pattern'] = re.match(cls.__pattern__, url).groupdict() #: pattern groups will be saved here, please save api stuff to info['api']
- except:
- pass
-
- for pattern in ("FILE_INFO_PATTERN", "INFO_PATTERN",
- "FILE_NAME_PATTERN", "NAME_PATTERN",
- "FILE_SIZE_PATTERN", "SIZE_PATTERN",
- "HASHSUM_PATTERN"): #@TODO: Remove old patterns starting with "FILE_" in 0.4.10
- try:
- attr = getattr(cls, pattern)
- dict = re.search(attr, html).groupdict()
-
- if all(True for k in dict if k not in info['pattern']):
- info['pattern'].update(dict)
-
- except AttributeError:
- continue
-
- else:
- online = True
-
- if online:
- info['status'] = 2
-
- if 'N' in info['pattern']:
- info['name'] = replace_patterns(unquote(info['pattern']['N'].strip()),
- cls.FILE_NAME_REPLACEMENTS if hasattr(cls, "FILE_NAME_REPLACEMENTS") else cls.NAME_REPLACEMENTS) #@TODO: Remove FILE_NAME_REPLACEMENTS check in 0.4.10
-
- if 'S' in info['pattern']:
- size = replace_patterns(info['pattern']['S'] + info['pattern']['U'] if 'U' in info else info['pattern']['S'],
- cls.FILE_SIZE_REPLACEMENTS if hasattr(cls, "FILE_SIZE_REPLACEMENTS") else cls.SIZE_REPLACEMENTS) #@TODO: Remove FILE_SIZE_REPLACEMENTS check in 0.4.10
- info['size'] = parseFileSize(size)
-
- elif isinstance(info['size'], basestring):
- unit = info['units'] if 'units' in info else None
- info['size'] = parseFileSize(info['size'], unit)
-
- if 'H' in info['pattern']:
- hashtype = info['pattern']['T'] if 'T' in info['pattern'] else "hash"
- info[hashtype] = info['pattern']['H']
-
- return info
-
-
- def setup(self):
- self.resumeDownload = self.multiDL = self.premium
-
-
- def prepare(self):
- self.info = {}
- self.link = "" #@TODO: Move to hoster class in 0.4.10
- self.directDL = False #@TODO: Move to hoster class in 0.4.10
- self.multihost = False #@TODO: Move to hoster class in 0.4.10
-
- self.req.setOption("timeout", 120)
-
- if isinstance(self.COOKIES, list):
- set_cookies(self.req.cj, self.COOKIES)
-
- if (self.MULTI_HOSTER
- and (self.__pattern__ != self.core.pluginManager.hosterPlugins[self.__name__]['pattern']
- or re.match(self.__pattern__, self.pyfile.url) is None)):
-
- self.logInfo("Multi hoster detected")
-
- if self.account:
- self.multihost = True
- return
- else:
- self.fail(_("Only registered or premium users can use url leech feature"))
-
- if self.CHECK_DIRECT_LINK is None:
- self.directDL = bool(self.account)
-
- self.pyfile.url = replace_patterns(self.pyfile.url,
- self.FILE_URL_REPLACEMENTS if hasattr(self, "FILE_URL_REPLACEMENTS") else self.URL_REPLACEMENTS) #@TODO: Remove FILE_URL_REPLACEMENTS check in 0.4.10
-
-
- def preload(self):
- self.html = self.load(self.pyfile.url, cookies=bool(self.COOKIES), decode=not self.TEXT_ENCODING)
-
- if isinstance(self.TEXT_ENCODING, basestring):
- self.html = unicode(self.html, self.TEXT_ENCODING)
-
-
- def process(self, pyfile):
- self.prepare()
-
- if self.multihost:
- self.logDebug("Looking for leeched download link...")
- self.handleMulti()
-
- elif self.directDL:
- self.logDebug("Looking for direct download link...")
- self.handleDirect()
-
- if not self.link:
- self.preload()
-
- if self.html is None:
- self.fail(_("No html retrieved"))
-
- self.checkErrors()
-
- premium_only = 'error' in self.info and self.info['error'] == "premium-only"
-
- self._updateInfo(self.getInfo(pyfile.url, self.html))
-
- self.checkNameSize()
-
- #: Usually premium only pages doesn't show any file information
- if not premium_only:
- self.checkStatus()
-
- if self.premium and (not self.FORCE_CHECK_TRAFFIC or self.checkTrafficLeft()):
- self.logDebug("Handled as premium download")
- self.handlePremium()
-
- elif premium_only:
- self.fail(_("Link require a premium account to be handled"))
-
- else:
- self.logDebug("Handled as free download")
- self.handleFree()
-
- self.downloadLink(self.link)
- self.checkFile()
-
-
- def downloadLink(self, link):
- if not link:
- return
-
- self.download(link, disposition=self.CONTENT_DISPOSITION)
-
-
- def checkFile(self):
- if self.checkDownload({'empty': re.compile(r"^$")}) is "empty": #@TODO: Move to hoster in 0.4.10
- self.fail(_("Empty file"))
-
-
- def checkErrors(self):
- if hasattr(self, 'ERROR_PATTERN'):
- m = re.search(self.ERROR_PATTERN, self.html)
- if m:
- errmsg = self.info['error'] = m.group(1)
- self.error(errmsg)
-
- if hasattr(self, 'PREMIUM_ONLY_PATTERN'):
- m = re.search(self.PREMIUM_ONLY_PATTERN, self.html)
- if m:
- self.info['error'] = "premium-only"
- return
-
- if hasattr(self, 'WAIT_PATTERN'):
- m = re.search(self.WAIT_PATTERN, self.html)
- if m:
- wait_time = sum([int(v) * {"hr": 3600, "hour": 3600, "min": 60, "sec": 1}[u.lower()] for v, u in
- re.findall(r'(\d+)\s*(hr|hour|min|sec)', m, re.I)])
- self.wait(wait_time, False)
- return
-
- self.info.pop('error', None)
-
-
- def checkStatus(self):
- status = self.info['status']
-
- if status is 1:
- self.offline()
-
- elif status is 6:
- self.tempOffline()
-
- elif status is not 2:
- self.logInfo(_("File status: %s") % statusMap[status],
- _("File info: %s") % self.info)
- self.error(_("No file info retrieved"))
-
-
- def checkNameSize(self):
- name = self.info['name']
- size = self.info['size']
- url = self.info['url']
-
- if name and name != url:
- self.pyfile.name = name
- else:
- self.pyfile.name = name = self.info['name'] = urlparse(name).path.split('/')[-1]
-
- if size > 0:
- self.pyfile.size = size
- else:
- size = "Unknown"
-
- self.logDebug("File name: %s" % name,
- "File size: %s" % size)
-
-
- def checkInfo(self):
- self.checkErrors()
-
- self._updateInfo(self.getInfo(self.pyfile.url, self.html or ""))
-
- self.checkNameSize()
- self.checkStatus()
-
-
- #: Deprecated
- def getFileInfo(self):
- self.info = {}
- self.checkInfo()
- return self.info
-
-
- def _updateInfo(self, info):
- self.logDebug(_("File info (before update): %s") % self.info)
- self.info.update(info)
- self.logDebug(_("File info (after update): %s") % self.info)
-
-
- def handleDirect(self):
- link = _isDirectLink(self, self.pyfile.url, self.resumeDownload)
-
- if link:
- self.logInfo(_("Direct download link detected"))
-
- self.link = link
-
- self._updateInfo(self.getInfo(self.pyfile.url))
- self.checkNameSize()
- else:
- self.logDebug(_("Direct download link not found"))
-
-
- def handleMulti(self): #: Multi-hoster handler
- pass
-
-
- def handleFree(self):
- if not hasattr(self, 'LINK_FREE_PATTERN'):
- self.fail(_("Free download not implemented"))
-
- try:
- m = re.search(self.LINK_FREE_PATTERN, self.html)
- if m is None:
- self.error(_("Free download link not found"))
-
- self.link = m.group(1)
-
- except Exception, e:
- self.fail(e)
-
-
- def handlePremium(self):
- if not hasattr(self, 'LINK_PREMIUM_PATTERN'):
- self.fail(_("Premium download not implemented"))
-
- try:
- m = re.search(self.LINK_PREMIUM_PATTERN, self.html)
- if m is None:
- self.error(_("Premium download link not found"))
-
- self.link = m.group(1)
-
- except Exception, e:
- self.fail(e)
-
-
- def longWait(self, wait_time=None, max_tries=3):
- if wait_time and isinstance(wait_time, (int, long, float)):
- time_str = "%dh %dm" % divmod(wait_time / 60, 60)
- else:
- wait_time = 900
- time_str = _("(unknown time)")
- max_tries = 100
-
- self.logInfo(_("Download limit reached, reconnect or wait %s") % time_str)
-
- self.setWait(wait_time, True)
- self.wait()
- self.retry(max_tries=max_tries, reason=_("Download limit reached"))
-
-
- def parseHtmlForm(self, attr_str="", input_names={}):
- return parseHtmlForm(attr_str, self.html, input_names)
-
-
- def checkTrafficLeft(self):
- traffic = self.account.getAccountInfo(self.user, True)['trafficleft']
-
- if traffic is None:
- return False
- elif traffic == -1:
- return True
- else:
- size = self.pyfile.size / 1024
- self.logInfo(_("Filesize: %i KiB, Traffic left for user %s: %i KiB") % (size, self.user, traffic))
- return size <= traffic
-
-
- #@TODO: Remove in 0.4.10
- def wait(self, seconds=0, reconnect=None):
- return _wait(self, seconds, reconnect)
-
-
- def error(self, reason="", type="parse"):
- return _error(self, reason, type)
diff --git a/module/plugins/internal/UnRar.py b/module/plugins/internal/UnRar.py
deleted file mode 100644
index c15a4c96e..000000000
--- a/module/plugins/internal/UnRar.py
+++ /dev/null
@@ -1,221 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import os
-import re
-
-from glob import glob
-from os.path import basename, join
-from string import digits
-from subprocess import Popen, PIPE
-
-from module.plugins.internal.AbstractExtractor import AbtractExtractor, WrongPassword, ArchiveError, CRCError
-from module.utils import save_join, decode
-
-
-def renice(pid, value):
- if os.name != "nt" and value:
- try:
- Popen(["renice", str(value), str(pid)], stdout=PIPE, stderr=PIPE, bufsize=-1)
- except:
- print "Renice failed"
-
-
-class UnRar(AbtractExtractor):
- __name__ = "UnRar"
- __version__ = "0.19"
-
- __description__ = """Rar extractor plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org")]
-
-
- CMD = "unrar"
-
- # there are some more uncovered rar formats
- re_version = re.compile(r'UNRAR ([\w .]+?) freeware')
- re_splitfile = re.compile(r'(.*)\.part(\d+)\.rar$', re.I)
- re_partfiles = re.compile(r'.*\.(rar|r\d+)', re.I)
- re_filelist = re.compile(r'(.+)\s+(\d+)\s+(\d+)\s+')
- re_filelist5 = re.compile(r'(.+)\s+(\d+)\s+\d\d-\d\d-\d\d\s+\d\d:\d\d\s+(.+)')
- re_wrongpwd = re.compile(r'(Corrupt file or wrong password|password incorrect)', re.I)
-
-
- @staticmethod
- def checkDeps():
- if os.name == "nt":
- UnRar.CMD = join(pypath, "UnRAR.exe")
- p = Popen([UnRar.CMD], stdout=PIPE, stderr=PIPE)
- p.communicate()
- else:
- try:
- p = Popen([UnRar.CMD], stdout=PIPE, stderr=PIPE)
- p.communicate()
- except OSError:
-
- # fallback to rar
- UnRar.CMD = "rar"
- p = Popen([UnRar.CMD], stdout=PIPE, stderr=PIPE)
- p.communicate()
-
- return True
-
-
- @staticmethod
- def getTargets(files_ids):
- result = []
-
- for file, id in files_ids:
- if not file.endswith(".rar"):
- continue
-
- match = UnRar.re_splitfile.findall(file)
- if match:
- # only add first parts
- if int(match[0][1]) == 1:
- result.append((file, id))
- else:
- result.append((file, id))
-
- return result
-
-
- def init(self):
- self.passwordProtected = False
- self.headerProtected = False #: list files will not work without password
- self.smallestFile = None #: small file to test passwords
- self.password = "" #: save the correct password
-
-
- def checkArchive(self):
- p = self.call_unrar("l", "-v", self.file)
- out, err = p.communicate()
- if self.re_wrongpwd.search(err):
- self.passwordProtected = True
- self.headerProtected = True
- return True
-
- # output only used to check if passworded files are present
- if self.re_version.search(out):
- for attr, size, name in self.re_filelist5.findall(out):
- if attr.startswith("*"):
- self.passwordProtected = True
- return True
- else:
- for name, size, packed in self.re_filelist.findall(out):
- if name.startswith("*"):
- self.passwordProtected = True
- return True
-
- self.listContent()
- if not self.files:
- raise ArchiveError("Empty Archive")
-
- return False
-
-
- def checkPassword(self, password):
- # at this point we can only verify header protected files
- if self.headerProtected:
- p = self.call_unrar("l", "-v", self.file, password=password)
- out, err = p.communicate()
- if self.re_wrongpwd.search(err):
- return False
-
- return True
-
-
- def extract(self, progress, password=None):
- command = "x" if self.fullpath else "e"
-
- p = self.call_unrar(command, self.file, self.out, password=password)
- renice(p.pid, self.renice)
-
- progress(0)
- progressstring = ""
- while True:
- c = p.stdout.read(1)
- # quit loop on eof
- if not c:
- break
- # reading a percentage sign -> set progress and restart
- if c == '%':
- progress(int(progressstring))
- progressstring = ""
- # not reading a digit -> therefore restart
- elif c not in digits:
- progressstring = ""
- # add digit to progressstring
- else:
- progressstring = progressstring + c
- progress(100)
-
- # retrieve stderr
- err = p.stderr.read()
-
- if "CRC failed" in err and not password and not self.passwordProtected:
- raise CRCError
- elif "CRC failed" in err:
- raise WrongPassword
- if err.strip(): #: raise error if anything is on stderr
- raise ArchiveError(err.strip())
- if p.returncode:
- raise ArchiveError("Process terminated")
-
- if not self.files:
- self.password = password
- self.listContent()
-
-
- def getDeleteFiles(self):
- if ".part" in basename(self.file):
- return glob(re.sub("(?<=\.part)([01]+)", "*", self.file, re.I))
- # get files which matches .r* and filter unsuited files out
- parts = glob(re.sub(r"(?<=\.r)ar$", "*", self.file, re.I))
- return filter(lambda x: self.re_partfiles.match(x), parts)
-
-
- def listContent(self):
- command = "vb" if self.fullpath else "lb"
- p = self.call_unrar(command, "-v", self.file, password=self.password)
- out, err = p.communicate()
-
- if "Cannot open" in err:
- raise ArchiveError("Cannot open file")
-
- if err.strip(): #: only log error at this point
- self.m.logError(err.strip())
-
- result = set()
-
- for f in decode(out).splitlines():
- f = f.strip()
- result.add(save_join(self.out, f))
-
- self.files = result
-
-
- def call_unrar(self, command, *xargs, **kwargs):
- args = []
- # overwrite flag
- args.append("-o+") if self.overwrite else args.append("-o-")
-
- if self.excludefiles:
- for word in self.excludefiles.split(';'):
- args.append("-x%s" % word)
-
- # assume yes on all queries
- args.append("-y")
-
- # set a password
- if "password" in kwargs and kwargs['password']:
- args.append("-p%s" % kwargs['password'])
- else:
- args.append("-p-")
-
- # NOTE: return codes are not reliable, some kind of threading, cleanup whatever issue
- call = [self.CMD, command] + args + list(xargs)
- self.m.logDebug(" ".join(call))
-
- p = Popen(call, stdout=PIPE, stderr=PIPE)
-
- return p
diff --git a/module/plugins/internal/UnZip.py b/module/plugins/internal/UnZip.py
deleted file mode 100644
index e754141a1..000000000
--- a/module/plugins/internal/UnZip.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import sys
-import zipfile
-
-from module.plugins.internal.AbstractExtractor import AbtractExtractor
-
-
-class UnZip(AbtractExtractor):
- __name__ = "UnZip"
- __version__ = "0.1"
-
- __description__ = """Zip extractor plugin"""
- __license__ = "GPLv3"
- __authors__ = [("RaNaN", "RaNaN@pyload.org")]
-
-
- @staticmethod
- def checkDeps():
- return sys.version_info[:2] >= (2, 6)
-
-
- @staticmethod
- def getTargets(files_ids):
- result = []
-
- for file, id in files_ids:
- if file.endswith(".zip"):
- result.append((file, id))
-
- return result
-
-
- def extract(self, progress, password=None):
- z = zipfile.ZipFile(self.file)
- self.files = z.namelist()
- z.extractall(self.out)
-
-
- def getDeleteFiles(self):
- return [self.file]
diff --git a/module/plugins/internal/XFSAccount.py b/module/plugins/internal/XFSAccount.py
deleted file mode 100644
index 2094b1480..000000000
--- a/module/plugins/internal/XFSAccount.py
+++ /dev/null
@@ -1,160 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from time import gmtime, mktime, strptime
-from urlparse import urljoin
-
-from module.plugins.Account import Account
-from module.plugins.internal.SimpleHoster import parseHtmlForm, set_cookies
-
-
-class XFSAccount(Account):
- __name__ = "XFSAccount"
- __type__ = "account"
- __version__ = "0.32"
-
- __description__ = """XFileSharing account plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = None
- HOSTER_URL = None
-
- COOKIES = [(HOSTER_DOMAIN, "lang", "english")]
-
- PREMIUM_PATTERN = r'\(Premium only\)'
-
- VALID_UNTIL_PATTERN = r'Premium.[Aa]ccount expire:.*?(\d{1,2} [\w^_]+ \d{4})'
-
- TRAFFIC_LEFT_PATTERN = r'Traffic available today:.*?<b>\s*(?P<S>[\d.,]+|[Uu]nlimited)\s*(?:(?P<U>[\w^_]+)\s*)?</b>'
- TRAFFIC_LEFT_UNIT = "MB" #: used only if no group <U> was found
-
- LEECH_TRAFFIC_PATTERN = r'Leech Traffic left:<b>.*?(?P<S>[\d.,]+|[Uu]nlimited)\s*(?:(?P<U>[\w^_]+)\s*)?</b>'
- LEECH_TRAFFIC_UNIT = "MB" #: used only if no group <U> was found
-
- LOGIN_FAIL_PATTERN = r'>\s*(Incorrect Login or Password|Error<)'
-
-
- def __init__(self, manager, accounts): #@TODO: remove in 0.4.10
- self.init()
- return super(XFSAccount, self).__init__(manager, accounts)
-
-
- def init(self):
- # if not self.HOSTER_DOMAIN:
- # self.fail(_("Missing HOSTER_DOMAIN"))
-
- if not self.HOSTER_URL:
- self.HOSTER_URL = "http://www.%s/" % self.HOSTER_DOMAIN
-
-
- def loadAccountInfo(self, user, req):
- validuntil = None
- trafficleft = None
- leechtraffic = None
- premium = None
-
- html = req.load(self.HOSTER_URL, get={'op': "my_account"}, decode=True)
-
- premium = True if re.search(self.PREMIUM_PATTERN, html) else False
-
- m = re.search(self.VALID_UNTIL_PATTERN, html)
- if m:
- expiredate = m.group(1).strip()
- self.logDebug("Expire date: " + expiredate)
-
- try:
- validuntil = mktime(strptime(expiredate, "%d %B %Y"))
-
- except Exception, e:
- self.logError(e)
-
- else:
- self.logDebug("Valid until: %s" % validuntil)
-
- if validuntil > mktime(gmtime()):
- premium = True
- trafficleft = -1
- else:
- premium = False
- validuntil = None #: registered account type (not premium)
- else:
- self.logDebug("VALID_UNTIL_PATTERN not found")
-
- m = re.search(self.TRAFFIC_LEFT_PATTERN, html)
- if m:
- try:
- traffic = m.groupdict()
- size = traffic['S']
-
- if "nlimited" in size:
- trafficleft = -1
- if validuntil is None:
- validuntil = -1
- else:
- if 'U' in traffic:
- unit = traffic['U']
- elif isinstance(self.TRAFFIC_LEFT_UNIT, basestring):
- unit = self.TRAFFIC_LEFT_UNIT
- else:
- unit = ""
-
- trafficleft = self.parseTraffic(size + unit)
-
- except Exception, e:
- self.logError(e)
- else:
- self.logDebug("TRAFFIC_LEFT_PATTERN not found")
-
- leech = [m.groupdict() for m in re.finditer(self.LEECH_TRAFFIC_PATTERN, html)]
- if leech:
- leechtraffic = 0
- try:
- for traffic in leech:
- size = traffic['S']
-
- if "nlimited" in size:
- leechtraffic = -1
- if validuntil is None:
- validuntil = -1
- break
- else:
- if 'U' in traffic:
- unit = traffic['U']
- elif isinstance(self.LEECH_TRAFFIC_UNIT, basestring):
- unit = self.LEECH_TRAFFIC_UNIT
- else:
- unit = ""
-
- leechtraffic += self.parseTraffic(size + unit)
-
- except Exception, e:
- self.logError(e)
- else:
- self.logDebug("LEECH_TRAFFIC_PATTERN not found")
-
- return {'validuntil': validuntil, 'trafficleft': trafficleft, 'leechtraffic': leechtraffic, 'premium': premium}
-
-
- def login(self, user, data, req):
- if isinstance(self.COOKIES, list):
- set_cookies(req.cj, self.COOKIES)
-
- url = urljoin(self.HOSTER_URL, "login.html")
- html = req.load(url, decode=True)
-
- action, inputs = parseHtmlForm('name="FL"', html)
- if not inputs:
- inputs = {'op': "login",
- 'redirect': self.HOSTER_URL}
-
- inputs.update({'login': user,
- 'password': data['password']})
-
- html = req.load(self.HOSTER_URL, post=inputs, decode=True)
-
- if re.search(self.LOGIN_FAIL_PATTERN, html):
- self.wrongPassword()
diff --git a/module/plugins/internal/XFSCrypter.py b/module/plugins/internal/XFSCrypter.py
deleted file mode 100644
index 62fd8c017..000000000
--- a/module/plugins/internal/XFSCrypter.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from module.plugins.internal.SimpleCrypter import SimpleCrypter
-
-
-class XFSCrypter(SimpleCrypter):
- __name__ = "XFSCrypter"
- __type__ = "crypter"
- __version__ = "0.04"
-
- __pattern__ = r'^unmatchable$'
-
- __description__ = """XFileSharing decrypter plugin"""
- __license__ = "GPLv3"
- __authors__ = [("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = None
- HOSTER_NAME = None
-
- URL_REPLACEMENTS = [(r'&?per_page=\d+', ""), (r'[?/&]+$', ""), (r'(.+/[^?]+)$', r'\1?'), (r'$', r'&per_page=10000')]
-
- COOKIES = [(HOSTER_DOMAIN, "lang", "english")]
-
- LINK_PATTERN = r'<(?:td|TD).*?>\s*<a href="(.+?)".*?>.+?(?:</a>)?\s*</(?:td|TD)>'
- NAME_PATTERN = r'<[tT]itle>.*?\: (?P<N>.+) folder</[tT]itle>'
-
- OFFLINE_PATTERN = r'>\s*\w+ (Not Found|file (was|has been) removed)'
- TEMP_OFFLINE_PATTERN = r'>\s*\w+ server (is in )?(maintenance|maintainance)'
diff --git a/module/plugins/internal/XFSHoster.py b/module/plugins/internal/XFSHoster.py
deleted file mode 100644
index 3ae0692dc..000000000
--- a/module/plugins/internal/XFSHoster.py
+++ /dev/null
@@ -1,345 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import re
-
-from random import random
-from time import sleep
-
-from pycurl import FOLLOWLOCATION, LOW_SPEED_TIME
-
-from module.plugins.hoster.UnrestrictLi import secondsToMidnight
-from module.plugins.internal.CaptchaService import ReCaptcha, SolveMedia
-from module.plugins.internal.SimpleHoster import SimpleHoster, create_getInfo
-from module.utils import html_unescape
-
-
-class XFSHoster(SimpleHoster):
- __name__ = "XFSHoster"
- __type__ = "hoster"
- __version__ = "0.27"
-
- __pattern__ = r'^unmatchable$'
-
- __description__ = """XFileSharing hoster plugin"""
- __license__ = "GPLv3"
- __authors__ = [("zoidberg", "zoidberg@mujmail.cz"),
- ("stickell", "l.stickell@yahoo.it"),
- ("Walter Purcaro", "vuolter@gmail.com")]
-
-
- HOSTER_DOMAIN = None
- HOSTER_NAME = None
-
- TEXT_ENCODING = False
- COOKIES = [(HOSTER_DOMAIN, "lang", "english")]
- CHECK_DIRECT_LINK = None
- MULTI_HOSTER = True #@NOTE: Should be default to False for safe, but I'm lazy...
-
- NAME_PATTERN = r'(>Filename:</b></td><td nowrap>|name="fname" value="|<span class="name">)(?P<N>.+?)(\s*<|")'
- SIZE_PATTERN = r'(>Size:</b></td><td>|>File:.*>|<span class="size">)(?P<S>[\d.,]+)\s*(?P<U>[\w^_]+)'
-
- OFFLINE_PATTERN = r'>\s*\w+ (Not Found|file (was|has been) removed)'
- TEMP_OFFLINE_PATTERN = r'>\s*\w+ server (is in )?(maintenance|maintainance)'
-
- WAIT_PATTERN = r'<span id="countdown_str">.*?>(\d+)</span>|id="countdown" value=".*?(\d+).*?"'
- PREMIUM_ONLY_PATTERN = r'>This file is available for Premium Users only'
- ERROR_PATTERN = r'(?:class=["\']err["\'].*?>|<[Cc]enter><b>|>Error</td>|>\(ERROR:)(?:\s*<.+?>\s*)*(.+?)(?:["\']|<|\))'
-
- LEECH_LINK_PATTERN = r'<h2>Download Link</h2>\s*<textarea[^>]*>([^<]+)'
- LINK_PATTERN = None #: final download url pattern
-
- CAPTCHA_PATTERN = r'(https?://[^"\']+?/captchas?/[^"\']+)'
- CAPTCHA_BLOCK_PATTERN = r'>Enter code.*?<div.*?>(.+?)</div>'
- RECAPTCHA_PATTERN = None
- SOLVEMEDIA_PATTERN = None
-
- FORM_PATTERN = None
- FORM_INPUTS_MAP = None #: dict passed as input_names to parseHtmlForm
-
-
- def setup(self):
- self.chunkLimit = 1
- self.resumeDownload = self.multiDL = self.premium
-
-
- def prepare(self):
- """ Initialize important variables """
- if not self.HOSTER_DOMAIN:
- self.fail(_("Missing HOSTER_DOMAIN"))
-
- if not self.HOSTER_NAME:
- self.HOSTER_NAME = "".join([str.capitalize() for str in self.HOSTER_DOMAIN.split('.')])
-
- if not self.LINK_PATTERN:
- pattern = r'(https?://(www\.)?([^/]*?%s|\d+\.\d+\.\d+\.\d+)(\:\d+)?(/d/|(/files)?/\d+/\w+/).+?)["\'<]'
- self.LINK_PATTERN = pattern % self.HOSTER_DOMAIN.replace('.', '\.')
-
- self.captcha = None
- self.errmsg = None
- self.passwords = self.getPassword().splitlines()
-
- super(XFSHoster, self).prepare()
-
- if self.CHECK_DIRECT_LINK is None:
- self.directDL = bool(self.premium)
-
-
- def handleFree(self):
- link = self.getDownloadLink()
-
- if link:
- if self.captcha:
- self.correctCaptcha()
-
- self.download(link, ref=True, cookies=True, disposition=True)
-
- elif self.errmsg:
- if 'captcha' in self.errmsg:
- self.fail(_("No valid captcha code entered"))
- else:
- self.fail(self.errmsg)
-
- else:
- self.fail(_("Download link not found"))
-
-
- def handlePremium(self):
- return self.handleFree()
-
-
- def getDownloadLink(self):
- for i in xrange(1, 6):
- self.logDebug("Getting download link: #%d" % i)
-
- self.checkErrors()
-
- m = re.search(self.LINK_PATTERN, self.html, re.S)
- if m:
- break
-
- data = self.getPostParameters()
-
- self.req.http.c.setopt(FOLLOWLOCATION, 0)
-
- self.html = self.load(self.pyfile.url, post=data, ref=True, decode=True)
-
- self.req.http.c.setopt(FOLLOWLOCATION, 1)
-
- m = re.search(r'Location\s*:\s*(.+)', self.req.http.header, re.I)
- if m and not "op=" in m.group(1):
- break
-
- m = re.search(self.LINK_PATTERN, self.html, re.S)
- if m:
- break
- else:
- self.logError(data['op'] if 'op' in data else _("UNKNOWN"))
- return ""
-
- self.errmsg = None
-
- return m.group(1).strip() #@TODO: Remove .strip() in 0.4.10
-
-
- def handleMulti(self):
- #only tested with easybytez.com
- self.html = self.load("http://www.%s/" % self.HOSTER_DOMAIN)
-
- action, inputs = self.parseHtmlForm()
-
- upload_id = "%012d" % int(random() * 10 ** 12)
- action += upload_id + "&js_on=1&utype=prem&upload_type=url"
-
- inputs['tos'] = '1'
- inputs['url_mass'] = self.pyfile.url
- inputs['up1oad_type'] = 'url'
-
- self.logDebug(action, inputs)
-
- self.req.setOption("timeout", 600) #: wait for file to upload to easybytez.com
-
- self.html = self.load(action, post=inputs)
-
- self.checkErrors()
-
- action, inputs = self.parseHtmlForm('F1')
- if not inputs:
- if self.errmsg:
- self.retry(reason=self.errmsg)
- else:
- self.error(_("TEXTAREA F1 not found"))
-
- self.logDebug(inputs)
-
- stmsg = inputs['st']
-
- if stmsg == 'OK':
- self.html = self.load(action, post=inputs)
-
- elif 'Can not leech file' in stmsg:
- self.retry(20, 3 * 60, _("Can not leech file"))
-
- elif 'today' in stmsg:
- self.retry(wait_time=secondsToMidnight(gmt=2), reason=_("You've used all Leech traffic today"))
-
- else:
- self.fail(stmsg)
-
- #get easybytez.com link for uploaded file
- m = re.search(self.LEECH_LINK_PATTERN, self.html)
- if m is None:
- self.error(_("LEECH_LINK_PATTERN not found"))
-
- header = self.load(m.group(1), just_header=True, decode=True)
-
- if 'location' in header: #: Direct download link
- self.link = header['location']
- else:
- self.fail(_("Download link not found"))
-
-
- def checkErrors(self):
- m = re.search(self.PREMIUM_ONLY_PATTERN, self.html)
- if m:
- self.info['error'] = "premium-only"
- return
-
- m = re.search(self.ERROR_PATTERN, self.html)
-
- if m is None:
- self.errmsg = None
- else:
- self.errmsg = m.group(1).strip()
-
- self.logWarning(re.sub(r"<.*?>", " ", self.errmsg))
-
- if 'wait' in self.errmsg:
- wait_time = sum([int(v) * {"hr": 3600, "hour": 3600, "min": 60, "sec": 1}[u.lower()] for v, u in
- re.findall(r'(\d+)\s*(hr|hour|min|sec)', self.errmsg, re.I)])
- self.wait(wait_time, True)
-
- elif 'country' in self.errmsg:
- self.fail(_("Downloads are disabled for your country"))
-
- elif 'captcha' in self.errmsg:
- self.invalidCaptcha()
-
- elif 'premium' in self.errmsg and 'require' in self.errmsg:
- self.fail(_("File can be downloaded by premium users only"))
-
- elif 'limit' in self.errmsg:
- if 'days' in self.errmsg:
- delay = secondsToMidnight(gmt=2)
- retries = 3
- else:
- delay = 1 * 60 * 60
- retries = 24
-
- self.wantReconnect = True
- self.retry(retries, delay, _("Download limit exceeded"))
-
- elif 'countdown' in self.errmsg or 'Expired' in self.errmsg:
- self.retry(reason=_("Link expired"))
-
- elif 'maintenance' in self.errmsg or 'maintainance' in self.errmsg:
- self.tempOffline()
-
- elif 'download files up to' in self.errmsg:
- self.fail(_("File too large for free download"))
-
- else:
- self.wantReconnect = True
- self.retry(wait_time=60, reason=self.errmsg)
-
- if self.errmsg:
- self.info['error'] = self.errmsg
- else:
- self.info.pop('error', None)
-
-
- def getPostParameters(self):
- if self.FORM_PATTERN or self.FORM_INPUTS_MAP:
- action, inputs = self.parseHtmlForm(self.FORM_PATTERN or "", self.FORM_INPUTS_MAP or {})
- else:
- action, inputs = self.parseHtmlForm(input_names={'op': re.compile(r'^download')})
-
- if not inputs:
- action, inputs = self.parseHtmlForm('F1')
- if not inputs:
- if self.errmsg:
- self.retry(reason=self.errmsg)
- else:
- self.error(_("TEXTAREA F1 not found"))
-
- self.logDebug(inputs)
-
- if 'op' in inputs:
- if "password" in inputs:
- if self.passwords:
- inputs['password'] = self.passwords.pop(0)
- else:
- self.fail(_("Missing password"))
-
- if not self.premium:
- m = re.search(self.WAIT_PATTERN, self.html)
- if m:
- wait_time = int(m.group(1))
- self.setWait(wait_time, False)
-
- self.captcha = self.handleCaptcha(inputs)
-
- self.wait()
- else:
- inputs['referer'] = self.pyfile.url
-
- if self.premium:
- inputs['method_premium'] = "Premium Download"
- inputs.pop('method_free', None)
- else:
- inputs['method_free'] = "Free Download"
- inputs.pop('method_premium', None)
-
- return inputs
-
-
- def handleCaptcha(self, inputs):
- m = re.search(self.CAPTCHA_PATTERN, self.html)
- if m:
- captcha_url = m.group(1)
- inputs['code'] = self.decryptCaptcha(captcha_url)
- return 1
-
- m = re.search(self.CAPTCHA_BLOCK_PATTERN, self.html, re.S)
- if m:
- captcha_div = m.group(1)
- numerals = re.findall(r'<span.*?padding-left\s*:\s*(\d+).*?>(\d)</span>', html_unescape(captcha_div))
- self.logDebug(captcha_div)
- inputs['code'] = "".join([a[1] for a in sorted(numerals, key=lambda num: int(num[0]))])
- self.logDebug("Captcha code: %s" % inputs['code'], numerals)
- return 2
-
- recaptcha = ReCaptcha(self)
- try:
- captcha_key = re.search(self.RECAPTCHA_PATTERN, self.html).group(1)
- except:
- captcha_key = recaptcha.detect_key()
- else:
- self.logDebug("ReCaptcha key: %s" % captcha_key)
-
- if captcha_key:
- inputs['recaptcha_challenge_field'], inputs['recaptcha_response_field'] = recaptcha.challenge(captcha_key)
- return 3
-
- solvemedia = SolveMedia(self)
- try:
- captcha_key = re.search(self.SOLVEMEDIA_PATTERN, self.html).group(1)
- except:
- captcha_key = solvemedia.detect_key()
- else:
- self.logDebug("SolveMedia key: %s" % captcha_key)
-
- if captcha_key:
- inputs['adcopy_challenge'], inputs['adcopy_response'] = solvemedia.challenge(captcha_key)
- return 4
-
- return 0
diff --git a/module/plugins/internal/__init__.py b/module/plugins/internal/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/plugins/internal/__init__.py
+++ /dev/null
diff --git a/module/remote/ClickAndLoadBackend.py b/module/remote/ClickAndLoadBackend.py
deleted file mode 100644
index ad8031587..000000000
--- a/module/remote/ClickAndLoadBackend.py
+++ /dev/null
@@ -1,170 +0,0 @@
-# -*- 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
-"""
-import re
-from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
-from cgi import FieldStorage
-from urllib import unquote
-from base64 import standard_b64decode
-from binascii import unhexlify
-
-try:
- from Crypto.Cipher import AES
-except:
- pass
-
-from RemoteManager import BackendBase
-
-core = None
-js = None
-
-class ClickAndLoadBackend(BackendBase):
- def setup(self, host, port):
- self.httpd = HTTPServer((host, port), CNLHandler)
- global core, js
- core = self.m.core
- js = core.js
-
- def serve(self):
- while self.enabled:
- self.httpd.handle_request()
-
-class CNLHandler(BaseHTTPRequestHandler):
-
- def add_package(self, name, urls, queue=0):
- print "name", name
- print "urls", urls
- print "queue", queue
-
- def get_post(self, name, default=""):
- if name in self.post:
- return self.post[name]
- else:
- return default
-
- def start_response(self, string):
-
- self.send_response(200)
-
- self.send_header("Content-Length", len(string))
- self.send_header("Content-Language", "de")
- self.send_header("Vary", "Accept-Language, Cookie")
- self.send_header("Cache-Control", "no-cache, must-revalidate")
- self.send_header("Content-type", "text/html")
- self.end_headers()
-
- def do_GET(self):
- path = self.path.strip("/").lower()
- #self.wfile.write(path+"\n")
-
- self.map = [ (r"add$", self.add),
- (r"addcrypted$", self.addcrypted),
- (r"addcrypted2$", self.addcrypted2),
- (r"flashgot", self.flashgot),
- (r"crossdomain\.xml", self.crossdomain),
- (r"checkSupportForUrl", self.checksupport),
- (r"jdcheck.js", self.jdcheck),
- (r"", self.flash) ]
-
- func = None
- for r, f in self.map:
- if re.match(r"(flash(got)?/?)?"+r, path):
- func = f
- break
-
- if func:
- try:
- resp = func()
- if not resp: resp = "success"
- resp += "\r\n"
- self.start_response(resp)
- self.wfile.write(resp)
- except Exception,e :
- self.send_error(500, str(e))
- else:
- self.send_error(404, "Not Found")
-
- def do_POST(self):
- form = FieldStorage(
- fp=self.rfile,
- headers=self.headers,
- environ={'REQUEST_METHOD':'POST',
- 'CONTENT_TYPE':self.headers['Content-Type'],
- })
-
- self.post = {}
- for name in form.keys():
- self.post[name] = form[name].value
-
- return self.do_GET()
-
- def flash(self):
- return "JDownloader"
-
- def add(self):
- package = self.get_post('referer', 'ClickAndLoad Package')
- urls = filter(lambda x: x != "", self.get_post('urls').split("\n"))
-
- self.add_package(package, urls, 0)
-
- def addcrypted(self):
- package = self.get_post('referer', 'ClickAndLoad Package')
- dlc = self.get_post('crypted').replace(" ", "+")
-
- core.upload_container(package, dlc)
-
- def addcrypted2(self):
- package = self.get_post("source", "ClickAndLoad Package")
- crypted = self.get_post("crypted")
- jk = self.get_post("jk")
-
- crypted = standard_b64decode(unquote(crypted.replace(" ", "+")))
- jk = "%s f()" % jk
- jk = js.eval(jk)
- Key = unhexlify(jk)
- IV = Key
-
- obj = AES.new(Key, AES.MODE_CBC, IV)
- result = obj.decrypt(crypted).replace("\x00", "").replace("\r","").split("\n")
-
- result = filter(lambda x: x != "", result)
-
- self.add_package(package, result, 0)
-
-
- def flashgot(self):
- autostart = int(self.get_post('autostart', 0))
- package = self.get_post('package', "FlashGot")
- urls = filter(lambda x: x != "", self.get_post('urls').split("\n"))
-
- self.add_package(package, urls, autostart)
-
- def crossdomain(self):
- rep = "<?xml version=\"1.0\"?>\n"
- rep += "<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n"
- rep += "<cross-domain-policy>\n"
- rep += "<allow-access-from domain=\"*\" />\n"
- rep += "</cross-domain-policy>"
- return rep
-
- def checksupport(self):
- pass
-
- def jdcheck(self):
- rep = "jdownloader=true;\n"
- rep += "var version='10629';\n"
- return rep
diff --git a/module/remote/RemoteManager.py b/module/remote/RemoteManager.py
deleted file mode 100644
index 36eb52a4a..000000000
--- a/module/remote/RemoteManager.py
+++ /dev/null
@@ -1,91 +0,0 @@
-# -*- 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: mkaay
-"""
-
-from threading import Thread
-from traceback import print_exc
-
-class BackendBase(Thread):
- def __init__(self, manager):
- Thread.__init__(self)
- self.m = manager
- self.core = manager.core
- self.enabled = True
- self.running = False
-
- def run(self):
- self.running = True
- try:
- self.serve()
- except Exception, e:
- self.core.log.error(_("Remote backend error: %s") % e)
- if self.core.debug:
- print_exc()
- finally:
- self.running = False
-
- def setup(self, host, port):
- pass
-
- def checkDeps(self):
- return True
-
- def serve(self):
- pass
-
- def shutdown(self):
- pass
-
- def stop(self):
- self.enabled = False# set flag and call shutdowm message, so thread can react
- self.shutdown()
-
-
-class RemoteManager():
- available = []
-
- def __init__(self, core):
- self.core = core
- self.backends = []
-
- if self.core.remote:
- self.available.append("ThriftBackend")
-# else:
-# self.available.append("SocketBackend")
-
-
- def startBackends(self):
- host = self.core.config["remote"]["listenaddr"]
- port = self.core.config["remote"]["port"]
-
- for b in self.available:
- klass = getattr(__import__("module.remote.%s" % b, globals(), locals(), [b], -1), b)
- backend = klass(self)
- if not backend.checkDeps():
- continue
- try:
- backend.setup(host, port)
- self.core.log.info(_("Starting %(name)s: %(addr)s:%(port)s") % {"name": b, "addr": host, "port": port})
- except Exception, e:
- self.core.log.error(_("Failed loading backend %(name)s | %(error)s") % {"name": b, "error": str(e)})
- if self.core.debug:
- print_exc()
- else:
- backend.start()
- self.backends.append(backend)
-
- port += 1
diff --git a/module/remote/SocketBackend.py b/module/remote/SocketBackend.py
deleted file mode 100644
index 1a157cf1d..000000000
--- a/module/remote/SocketBackend.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import SocketServer
-
-from RemoteManager import BackendBase
-
-class RequestHandler(SocketServer.BaseRequestHandler):
-
- def setup(self):
- pass
-
- def handle(self):
-
- print self.request.recv(1024)
-
-
-
-class SocketBackend(BackendBase):
-
- def setup(self, host, port):
- #local only
- self.server = SocketServer.ThreadingTCPServer(("localhost", port), RequestHandler)
-
- def serve(self):
- self.server.serve_forever()
diff --git a/module/remote/ThriftBackend.py b/module/remote/ThriftBackend.py
deleted file mode 100644
index b4a2bb25e..000000000
--- a/module/remote/ThriftBackend.py
+++ /dev/null
@@ -1,56 +0,0 @@
-# -*- 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: mkaay, RaNaN
-"""
-from os.path import exists
-
-from module.remote.RemoteManager import BackendBase
-
-from thriftbackend.Processor import Processor
-from thriftbackend.Protocol import ProtocolFactory
-from thriftbackend.Socket import ServerSocket
-from thriftbackend.Transport import TransportFactory
-#from thriftbackend.Transport import TransportFactoryCompressed
-
-from thrift.server import TServer
-
-class ThriftBackend(BackendBase):
- def setup(self, host, port):
- processor = Processor(self.core.api)
-
- key = None
- cert = None
-
- if self.core.config['ssl']['activated']:
- if exists(self.core.config['ssl']['cert']) and exists(self.core.config['ssl']['key']):
- self.core.log.info(_("Using SSL ThriftBackend"))
- key = self.core.config['ssl']['key']
- cert = self.core.config['ssl']['cert']
-
- transport = ServerSocket(port, host, key, cert)
-
-
-# tfactory = TransportFactoryCompressed()
- tfactory = TransportFactory()
- pfactory = ProtocolFactory()
-
- self.server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)
- #self.server = TNonblockingServer.TNonblockingServer(processor, transport, tfactory, pfactory)
-
- #server = TServer.TThreadPoolServer(processor, transport, tfactory, pfactory)
-
- def serve(self):
- self.server.serve()
diff --git a/module/remote/__init__.py b/module/remote/__init__.py
deleted file mode 100644
index 9298f5337..000000000
--- a/module/remote/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# -*- coding: utf-8 -*-
-activated = True
diff --git a/module/remote/socketbackend/__init__.py b/module/remote/socketbackend/__init__.py
deleted file mode 100644
index de6d13128..000000000
--- a/module/remote/socketbackend/__init__.py
+++ /dev/null
@@ -1,2 +0,0 @@
-__author__ = 'christian'
- \ No newline at end of file
diff --git a/module/remote/socketbackend/create_ttypes.py b/module/remote/socketbackend/create_ttypes.py
deleted file mode 100644
index 05662cb50..000000000
--- a/module/remote/socketbackend/create_ttypes.py
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import inspect
-import sys
-from os.path import abspath, dirname, join
-
-path = dirname(abspath(__file__))
-module = join(path, "..", "..")
-
-sys.path.append(join(module, "lib"))
-sys.path.append(join(module, "remote"))
-
-from thriftbackend.thriftgen.pyload import ttypes
-from thriftbackend.thriftgen.pyload.Pyload import Iface
-
-
-def main():
-
- enums = []
- classes = []
-
- print "generating lightweight ttypes.py"
-
- for name in dir(ttypes):
- klass = getattr(ttypes, name)
-
- if name in ("TBase", "TExceptionBase") or name.startswith("_") or not (issubclass(klass, ttypes.TBase) or issubclass(klass, ttypes.TExceptionBase)):
- continue
-
- if hasattr(klass, "thrift_spec"):
- classes.append(klass)
- else:
- enums.append(klass)
-
-
- f = open(join(path, "ttypes.py"), "wb")
-
- f.write(
- """#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# Autogenerated by pyload
-# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
-
-class BaseObject(object):
-\t__slots__ = []
-
-""")
-
- ## generate enums
- for enum in enums:
- name = enum.__name__
- f.write("class %s:\n" % name)
-
- for attr in dir(enum):
- if attr.startswith("_") or attr in ("read", "write"): continue
-
- f.write("\t%s = %s\n" % (attr, getattr(enum, attr)))
-
- f.write("\n")
-
- for klass in classes:
- name = klass.__name__
- base = "Exception" if issubclass(klass, ttypes.TExceptionBase) else "BaseObject"
- f.write("class %s(%s):\n" % (name, base))
- f.write("\t__slots__ = %s\n\n" % klass.__slots__)
-
- #create init
- args = ["self"] + ["%s=None" % x for x in klass.__slots__]
-
- f.write("\tdef __init__(%s):\n" % ", ".join(args))
- for attr in klass.__slots__:
- f.write("\t\tself.%s = %s\n" % (attr, attr))
-
- f.write("\n")
-
- f.write("class Iface:\n")
-
- for name in dir(Iface):
- if name.startswith("_"): continue
-
- func = inspect.getargspec(getattr(Iface, name))
-
- f.write("\tdef %s(%s):\n\t\tpass\n" % (name, ", ".join(func.args)))
-
- f.write("\n")
-
- f.close()
-
-if __name__ == "__main__":
- main() \ No newline at end of file
diff --git a/module/remote/socketbackend/ttypes.py b/module/remote/socketbackend/ttypes.py
deleted file mode 100644
index f8ea121fa..000000000
--- a/module/remote/socketbackend/ttypes.py
+++ /dev/null
@@ -1,383 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# Autogenerated by pyload
-# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
-
-class BaseObject(object):
- __slots__ = []
-
-class Destination:
- Collector = 0
- Queue = 1
-
-class DownloadStatus:
- Aborted = 9
- Custom = 11
- Decrypting = 10
- Downloading = 12
- Failed = 8
- Finished = 0
- Offline = 1
- Online = 2
- Processing = 13
- Queued = 3
- Skipped = 4
- Starting = 7
- TempOffline = 6
- Unknown = 14
- Waiting = 5
-
-class ElementType:
- File = 1
- Package = 0
-
-class Input:
- BOOL = 4
- CHOICE = 6
- CLICK = 5
- LIST = 8
- MULTIPLE = 7
- NONE = 0
- PASSWORD = 3
- TABLE = 9
- TEXT = 1
- TEXTBOX = 2
-
-class Output:
- CAPTCHA = 1
- NOTIFICATION = 4
- QUESTION = 2
-
-class AccountInfo(BaseObject):
- __slots__ = ['validuntil', 'login', 'options', 'valid', 'trafficleft', 'maxtraffic', 'premium', 'type']
-
- def __init__(self, validuntil=None, login=None, options=None, valid=None, trafficleft=None, maxtraffic=None, premium=None, type=None):
- self.validuntil = validuntil
- self.login = login
- self.options = options
- self.valid = valid
- self.trafficleft = trafficleft
- self.maxtraffic = maxtraffic
- self.premium = premium
- self.type = type
-
-class CaptchaTask(BaseObject):
- __slots__ = ['tid', 'data', 'type', 'resultType']
-
- def __init__(self, tid=None, data=None, type=None, resultType=None):
- self.tid = tid
- self.data = data
- self.type = type
- self.resultType = resultType
-
-class ConfigItem(BaseObject):
- __slots__ = ['name', 'description', 'value', 'type']
-
- def __init__(self, name=None, description=None, value=None, type=None):
- self.name = name
- self.description = description
- self.value = value
- self.type = type
-
-class ConfigSection(BaseObject):
- __slots__ = ['name', 'description', 'items', 'outline']
-
- def __init__(self, name=None, description=None, items=None, outline=None):
- self.name = name
- self.description = description
- self.items = items
- self.outline = outline
-
-class DownloadInfo(BaseObject):
- __slots__ = ['fid', 'name', 'speed', 'eta', 'format_eta', 'bleft', 'size', 'format_size', 'percent', 'status', 'statusmsg', 'format_wait', 'wait_until', 'packageID', 'packageName', 'plugin']
-
- def __init__(self, fid=None, name=None, speed=None, eta=None, format_eta=None, bleft=None, size=None, format_size=None, percent=None, status=None, statusmsg=None, format_wait=None, wait_until=None, packageID=None, packageName=None, plugin=None):
- self.fid = fid
- self.name = name
- self.speed = speed
- self.eta = eta
- self.format_eta = format_eta
- self.bleft = bleft
- self.size = size
- self.format_size = format_size
- self.percent = percent
- self.status = status
- self.statusmsg = statusmsg
- self.format_wait = format_wait
- self.wait_until = wait_until
- self.packageID = packageID
- self.packageName = packageName
- self.plugin = plugin
-
-class EventInfo(BaseObject):
- __slots__ = ['eventname', 'id', 'type', 'destination']
-
- def __init__(self, eventname=None, id=None, type=None, destination=None):
- self.eventname = eventname
- self.id = id
- self.type = type
- self.destination = destination
-
-class FileData(BaseObject):
- __slots__ = ['fid', 'url', 'name', 'plugin', 'size', 'format_size', 'status', 'statusmsg', 'packageID', 'error', 'order']
-
- def __init__(self, fid=None, url=None, name=None, plugin=None, size=None, format_size=None, status=None, statusmsg=None, packageID=None, error=None, order=None):
- self.fid = fid
- self.url = url
- self.name = name
- self.plugin = plugin
- self.size = size
- self.format_size = format_size
- self.status = status
- self.statusmsg = statusmsg
- self.packageID = packageID
- self.error = error
- self.order = order
-
-class FileDoesNotExists(Exception):
- __slots__ = ['fid']
-
- def __init__(self, fid=None):
- self.fid = fid
-
-class InteractionTask(BaseObject):
- __slots__ = ['iid', 'input', 'structure', 'preset', 'output', 'data', 'title', 'description', 'plugin']
-
- def __init__(self, iid=None, input=None, structure=None, preset=None, output=None, data=None, title=None, description=None, plugin=None):
- self.iid = iid
- self.input = input
- self.structure = structure
- self.preset = preset
- self.output = output
- self.data = data
- self.title = title
- self.description = description
- self.plugin = plugin
-
-class OnlineCheck(BaseObject):
- __slots__ = ['rid', 'data']
-
- def __init__(self, rid=None, data=None):
- self.rid = rid
- self.data = data
-
-class OnlineStatus(BaseObject):
- __slots__ = ['name', 'plugin', 'packagename', 'status', 'size']
-
- def __init__(self, name=None, plugin=None, packagename=None, status=None, size=None):
- self.name = name
- self.plugin = plugin
- self.packagename = packagename
- self.status = status
- self.size = size
-
-class PackageData(BaseObject):
- __slots__ = ['pid', 'name', 'folder', 'site', 'password', 'dest', 'order', 'linksdone', 'sizedone', 'sizetotal', 'linkstotal', 'links', 'fids']
-
- def __init__(self, pid=None, name=None, folder=None, site=None, password=None, dest=None, order=None, linksdone=None, sizedone=None, sizetotal=None, linkstotal=None, links=None, fids=None):
- self.pid = pid
- self.name = name
- self.folder = folder
- self.site = site
- self.password = password
- self.dest = dest
- self.order = order
- self.linksdone = linksdone
- self.sizedone = sizedone
- self.sizetotal = sizetotal
- self.linkstotal = linkstotal
- self.links = links
- self.fids = fids
-
-class PackageDoesNotExists(Exception):
- __slots__ = ['pid']
-
- def __init__(self, pid=None):
- self.pid = pid
-
-class ServerStatus(BaseObject):
- __slots__ = ['pause', 'active', 'queue', 'total', 'speed', 'download', 'reconnect']
-
- def __init__(self, pause=None, active=None, queue=None, total=None, speed=None, download=None, reconnect=None):
- self.pause = pause
- self.active = active
- self.queue = queue
- self.total = total
- self.speed = speed
- self.download = download
- self.reconnect = reconnect
-
-class ServiceCall(BaseObject):
- __slots__ = ['plugin', 'func', 'arguments', 'parseArguments']
-
- def __init__(self, plugin=None, func=None, arguments=None, parseArguments=None):
- self.plugin = plugin
- self.func = func
- self.arguments = arguments
- self.parseArguments = parseArguments
-
-class ServiceDoesNotExists(Exception):
- __slots__ = ['plugin', 'func']
-
- def __init__(self, plugin=None, func=None):
- self.plugin = plugin
- self.func = func
-
-class ServiceException(Exception):
- __slots__ = ['msg']
-
- def __init__(self, msg=None):
- self.msg = msg
-
-class UserData(BaseObject):
- __slots__ = ['name', 'email', 'role', 'permission', 'templateName']
-
- def __init__(self, name=None, email=None, role=None, permission=None, templateName=None):
- self.name = name
- self.email = email
- self.role = role
- self.permission = permission
- self.templateName = templateName
-
-class Iface:
- def addFiles(self, pid, links):
- pass
- def addPackage(self, name, links, dest):
- pass
- def call(self, info):
- pass
- def checkOnlineStatus(self, urls):
- pass
- def checkOnlineStatusContainer(self, urls, filename, data):
- pass
- def checkURLs(self, urls):
- pass
- def deleteFiles(self, fids):
- pass
- def deleteFinished(self):
- pass
- def deletePackages(self, pids):
- pass
- def freeSpace(self):
- pass
- def generateAndAddPackages(self, links, dest):
- pass
- def generatePackages(self, links):
- pass
- def getAccountTypes(self):
- pass
- def getAccounts(self, refresh):
- pass
- def getAllInfo(self):
- pass
- def getAllUserData(self):
- pass
- def getCaptchaTask(self, exclusive):
- pass
- def getCaptchaTaskStatus(self, tid):
- pass
- def getCollector(self):
- pass
- def getCollectorData(self):
- pass
- def getConfig(self):
- pass
- def getConfigValue(self, category, option, section):
- pass
- def getEvents(self, uuid):
- pass
- def getFileData(self, fid):
- pass
- def getFileOrder(self, pid):
- pass
- def getInfoByPlugin(self, plugin):
- pass
- def getLog(self, offset):
- pass
- def getPackageData(self, pid):
- pass
- def getPackageInfo(self, pid):
- pass
- def getPackageOrder(self, destination):
- pass
- def getPluginConfig(self):
- pass
- def getQueue(self):
- pass
- def getQueueData(self):
- pass
- def getServerVersion(self):
- pass
- def getServices(self):
- pass
- def getUserData(self, username, password):
- pass
- def hasService(self, plugin, func):
- pass
- def isCaptchaWaiting(self):
- pass
- def isTimeDownload(self):
- pass
- def isTimeReconnect(self):
- pass
- def kill(self):
- pass
- def login(self, username, password):
- pass
- def moveFiles(self, fids, pid):
- pass
- def movePackage(self, destination, pid):
- pass
- def orderFile(self, fid, position):
- pass
- def orderPackage(self, pid, position):
- pass
- def parseURLs(self, html, url):
- pass
- def pauseServer(self):
- pass
- def pollResults(self, rid):
- pass
- def pullFromQueue(self, pid):
- pass
- def pushToQueue(self, pid):
- pass
- def recheckPackage(self, pid):
- pass
- def removeAccount(self, plugin, account):
- pass
- def restart(self):
- pass
- def restartFailed(self):
- pass
- def restartFile(self, fid):
- pass
- def restartPackage(self, pid):
- pass
- def setCaptchaResult(self, tid, result):
- pass
- def setConfigValue(self, category, option, value, section):
- pass
- def setPackageData(self, pid, data):
- pass
- def setPackageName(self, pid, name):
- pass
- def statusDownloads(self):
- pass
- def statusServer(self):
- pass
- def stopAllDownloads(self):
- pass
- def stopDownloads(self, fids):
- pass
- def togglePause(self):
- pass
- def toggleReconnect(self):
- pass
- def unpauseServer(self):
- pass
- def updateAccount(self, plugin, account, password, options):
- pass
- def uploadContainer(self, filename, data):
- pass
-
diff --git a/module/remote/thriftbackend/Processor.py b/module/remote/thriftbackend/Processor.py
deleted file mode 100644
index a8b87c82c..000000000
--- a/module/remote/thriftbackend/Processor.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from thriftgen.pyload import Pyload
-
-class Processor(Pyload.Processor):
- def __init__(self, *args, **kwargs):
- Pyload.Processor.__init__(self, *args, **kwargs)
- self.authenticated = {}
-
- def process(self, iprot, oprot):
- trans = oprot.trans
- if trans not in self.authenticated:
- self.authenticated[trans] = False
- oldclose = trans.close
-
- def wrap():
- if self in self.authenticated:
- del self.authenticated[trans]
- oldclose()
-
- trans.close = wrap
- authenticated = self.authenticated[trans]
- (name, type, seqid) = iprot.readMessageBegin()
-
- # unknown method
- if name not in self._processMap:
- iprot.skip(Pyload.TType.STRUCT)
- iprot.readMessageEnd()
- x = Pyload.TApplicationException(Pyload.TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % name)
- oprot.writeMessageBegin(name, Pyload.TMessageType.EXCEPTION, seqid)
- x.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
- return
-
- # not logged in
- elif not authenticated and not name == "login":
- iprot.skip(Pyload.TType.STRUCT)
- iprot.readMessageEnd()
- # 20 - Not logged in (in situ declared error code)
- x = Pyload.TApplicationException(20, 'Not logged in')
- oprot.writeMessageBegin(name, Pyload.TMessageType.EXCEPTION, seqid)
- x.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
- return
-
- elif not authenticated and name == "login":
- args = Pyload.login_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = Pyload.login_result()
- # api login
- self.authenticated[trans] = self._handler.checkAuth(args.username, args.password, trans.remoteaddr[0])
-
- result.success = True if self.authenticated[trans] else False
- oprot.writeMessageBegin("login", Pyload.TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- elif self._handler.isAuthorized(name, authenticated):
- self._processMap[name](self, seqid, iprot, oprot)
-
- else:
- #no permission
- iprot.skip(Pyload.TType.STRUCT)
- iprot.readMessageEnd()
- # 21 - Not authorized
- x = Pyload.TApplicationException(21, 'Not authorized')
- oprot.writeMessageBegin(name, Pyload.TMessageType.EXCEPTION, seqid)
- x.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
- return
-
- return True
diff --git a/module/remote/thriftbackend/Protocol.py b/module/remote/thriftbackend/Protocol.py
deleted file mode 100644
index c42d01459..000000000
--- a/module/remote/thriftbackend/Protocol.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from thrift.protocol import TBinaryProtocol
-
-class Protocol(TBinaryProtocol.TBinaryProtocol):
- def writeString(self, str):
- try:
- str = str.encode("utf8", "ignore")
- except Exception, e:
- pass
-
- self.writeI32(len(str))
- self.trans.write(str)
-
- def readString(self):
- len = self.readI32()
- str = self.trans.readAll(len)
- try:
- str = str.decode("utf8", "ignore")
- except:
- pass
-
- return str
-
-
-class ProtocolFactory(TBinaryProtocol.TBinaryProtocolFactory):
-
- def getProtocol(self, trans):
- prot = Protocol(trans, self.strictRead, self.strictWrite)
- return prot \ No newline at end of file
diff --git a/module/remote/thriftbackend/Socket.py b/module/remote/thriftbackend/Socket.py
deleted file mode 100644
index 2243f9df2..000000000
--- a/module/remote/thriftbackend/Socket.py
+++ /dev/null
@@ -1,129 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import sys
-import socket
-import errno
-
-from time import sleep
-
-from thrift.transport.TSocket import TSocket, TServerSocket, TTransportException
-
-WantReadError = Exception #overwritten when ssl is used
-
-class SecureSocketConnection:
- def __init__(self, connection):
- self.__dict__["connection"] = connection
-
- def __getattr__(self, name):
- return getattr(self.__dict__["connection"], name)
-
- def __setattr__(self, name, value):
- setattr(self.__dict__["connection"], name, value)
-
- def shutdown(self, how=1):
- self.__dict__["connection"].shutdown()
-
- def accept(self):
- connection, address = self.__dict__["connection"].accept()
- return SecureSocketConnection(connection), address
-
- def send(self, buff):
- try:
- return self.__dict__["connection"].send(buff)
- except WantReadError:
- sleep(0.1)
- return self.send(buff)
-
- def recv(self, buff):
- try:
- return self.__dict__["connection"].recv(buff)
- except WantReadError:
- sleep(0.1)
- return self.recv(buff)
-
-class Socket(TSocket):
- def __init__(self, host='localhost', port=7228, ssl=False):
- TSocket.__init__(self, host, port)
- self.ssl = ssl
-
- def open(self):
- if self.ssl:
- SSL = __import__("OpenSSL", globals(), locals(), "SSL", -1).SSL
- WantReadError = SSL.WantReadError
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- c = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
- c.set_connect_state()
- self.handle = SecureSocketConnection(c)
- else:
- self.handle = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
- #errno 104 connection reset
-
- self.handle.settimeout(self._timeout)
- self.handle.connect((self.host, self.port))
-
- def read(self, sz):
- try:
- buff = self.handle.recv(sz)
- except socket.error, e:
- if (e.args[0] == errno.ECONNRESET and
- (sys.platform == 'darwin' or sys.platform.startswith('freebsd'))):
- # freebsd and Mach don't follow POSIX semantic of recv
- # and fail with ECONNRESET if peer performed shutdown.
- # See corresponding comment and code in TSocket::read()
- # in lib/cpp/src/transport/TSocket.cpp.
- self.close()
- # Trigger the check to raise the END_OF_FILE exception below.
- buff = ''
- else:
- raise
- except Exception, e:
- # SSL connection was closed
- if e.args == (-1, 'Unexpected EOF'):
- buff = ''
- elif e.args == ([('SSL routines', 'SSL23_GET_CLIENT_HELLO', 'unknown protocol')],):
- #a socket not using ssl tried to connect
- buff = ''
- else:
- raise
-
- if not len(buff):
- raise TTransportException(type=TTransportException.END_OF_FILE, message='TSocket read 0 bytes')
- return buff
-
-
-class ServerSocket(TServerSocket, Socket):
- def __init__(self, port=7228, host="0.0.0.0", key="", cert=""):
- self.host = host
- self.port = port
- self.key = key
- self.cert = cert
- self.handle = None
-
- def listen(self):
- if self.cert and self.key:
- SSL = __import__("OpenSSL", globals(), locals(), "SSL", -1).SSL
- WantReadError = SSL.WantReadError
- ctx = SSL.Context(SSL.SSLv23_METHOD)
- ctx.use_privatekey_file(self.key)
- ctx.use_certificate_file(self.cert)
-
- tmpConnection = SSL.Connection(ctx, socket.socket(socket.AF_INET, socket.SOCK_STREAM))
- tmpConnection.set_accept_state()
- self.handle = SecureSocketConnection(tmpConnection)
-
- else:
- self.handle = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
-
- self.handle.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- if hasattr(self.handle, 'set_timeout'):
- self.handle.set_timeout(None)
- self.handle.bind((self.host, self.port))
- self.handle.listen(128)
-
- def accept(self):
- client, addr = self.handle.accept()
- result = Socket()
- result.setHandle(client)
- return result
diff --git a/module/remote/thriftbackend/ThriftClient.py b/module/remote/thriftbackend/ThriftClient.py
deleted file mode 100644
index 74363cf62..000000000
--- a/module/remote/thriftbackend/ThriftClient.py
+++ /dev/null
@@ -1,109 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import sys
-from socket import error
-from os.path import dirname, abspath, join
-from traceback import print_exc
-
-try:
- import thrift
-except ImportError:
- sys.path.append(abspath(join(dirname(abspath(__file__)), "..", "..", "lib")))
-
-from thrift.transport import TTransport
-#from thrift.transport.TZlibTransport import TZlibTransport
-from Socket import Socket
-from Protocol import Protocol
-
-# modules should import ttypes from here, when want to avoid importing API
-
-from thriftgen.pyload import Pyload
-from thriftgen.pyload.ttypes import *
-
-ConnectionClosed = TTransport.TTransportException
-
-class WrongLogin(Exception):
- pass
-
-class NoConnection(Exception):
- pass
-
-class NoSSL(Exception):
- pass
-
-class ThriftClient:
- def __init__(self, host="localhost", port=7227, user="", password=""):
-
- self.createConnection(host, port)
- try:
- self.transport.open()
- except error, e:
- if e.args and e.args[0] in (111, 10061):
- raise NoConnection
- else:
- print_exc()
- raise NoConnection
-
- try:
- correct = self.client.login(user, password)
- except error, e:
- if e.args and e.args[0] == 104:
- #connection reset by peer, probably wants ssl
- try:
- self.createConnection(host, port, True)
- #set timeout or a ssl socket will block when querying none ssl server
- self.socket.setTimeout(10)
-
- except ImportError:
- #@TODO untested
- raise NoSSL
- try:
- self.transport.open()
- correct = self.client.login(user, password)
- finally:
- self.socket.setTimeout(None)
- elif e.args and e.args[0] == 32:
- raise NoConnection
- else:
- print_exc()
- raise NoConnection
-
- if not correct:
- self.transport.close()
- raise WrongLogin
-
- def createConnection(self, host, port, ssl=False):
- self.socket = Socket(host, port, ssl)
- self.transport = TTransport.TBufferedTransport(self.socket)
-# self.transport = TZlibTransport(TTransport.TBufferedTransport(self.socket))
-
- protocol = Protocol(self.transport)
- self.client = Pyload.Client(protocol)
-
- def close(self):
- self.transport.close()
-
- def __getattr__(self, item):
- return getattr(self.client, item)
-
-if __name__ == "__main__":
-
- client = ThriftClient(user="User", password="pwhere")
-
- print client.getServerVersion()
- print client.statusServer()
- print client.statusDownloads()
- q = client.getQueue()
-
-# for p in q:
-# data = client.getPackageData(p.pid)
-# print data
-# print "Package Name: ", data.name
-
-
- print client.getServices()
- print client.call(Pyload.ServiceCall("UpdateManager", "recheckForUpdates"))
-
- print client.getConfigValue("download", "limit_speed", "core")
-
- client.close() \ No newline at end of file
diff --git a/module/remote/thriftbackend/ThriftTest.py b/module/remote/thriftbackend/ThriftTest.py
deleted file mode 100644
index 69ac6a745..000000000
--- a/module/remote/thriftbackend/ThriftTest.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import sys
-from os.path import join,abspath,dirname
-
-path = join((abspath(dirname(__file__))), "..","..", "lib")
-sys.path.append(path)
-
-from thriftgen.pyload import Pyload
-from thriftgen.pyload.ttypes import *
-from Socket import Socket
-
-from thrift import Thrift
-from thrift.transport import TTransport
-
-from Protocol import Protocol
-
-from time import time
-
-import xmlrpclib
-
-def bench(f, *args, **kwargs):
- s = time()
- ret = [f(*args, **kwargs) for i in range(0,100)]
- e = time()
- try:
- print "%s: %f s" % (f._Method__name, e-s)
- except :
- print "%s: %f s" % (f.__name__, e-s)
- return ret
-
-from getpass import getpass
-user = raw_input("user ")
-passwd = getpass("password ")
-
-server_url = "http%s://%s:%s@%s:%s/" % (
- "",
- user,
- passwd,
- "127.0.0.1",
- 7227
-)
-proxy = xmlrpclib.ServerProxy(server_url, allow_none=True)
-
-bench(proxy.get_server_version)
-bench(proxy.status_server)
-bench(proxy.status_downloads)
-#bench(proxy.get_queue)
-#bench(proxy.get_collector)
-print
-try:
-
- # Make socket
- transport = Socket('localhost', 7228, False)
-
- # Buffering is critical. Raw sockets are very slow
- transport = TTransport.TBufferedTransport(transport)
-
- # Wrap in a protocol
- protocol = Protocol(transport)
-
- # Create a client to use the protocol encoder
- client = Pyload.Client(protocol)
-
- # Connect!
- transport.open()
-
- print "Login", client.login(user, passwd)
-
- bench(client.getServerVersion)
- bench(client.statusServer)
- bench(client.statusDownloads)
- #bench(client.getQueue)
- #bench(client.getCollector)
-
- print
- print client.getServerVersion()
- print client.statusServer()
- print client.statusDownloads()
- q = client.getQueue()
-
- for p in q:
- data = client.getPackageData(p.pid)
- print data
- print "Package Name: ", data.name
-
- # Close!
- transport.close()
-
-except Thrift.TException, tx:
- print 'ThriftExpection: %s' % tx.message
diff --git a/module/remote/thriftbackend/Transport.py b/module/remote/thriftbackend/Transport.py
deleted file mode 100644
index 5772c5a9e..000000000
--- a/module/remote/thriftbackend/Transport.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# -*- coding: utf-8 -*-
-
-from thrift.transport.TTransport import TBufferedTransport
-from thrift.transport.TZlibTransport import TZlibTransport
-
-class Transport(TBufferedTransport):
- DEFAULT_BUFFER = 4096
-
- def __init__(self, trans, rbuf_size = DEFAULT_BUFFER):
- TBufferedTransport.__init__(self, trans, rbuf_size)
- self.handle = trans.handle
- self.remoteaddr = trans.handle.getpeername()
-
-class TransportCompressed(TZlibTransport):
- DEFAULT_BUFFER = 4096
-
- def __init__(self, trans, rbuf_size = DEFAULT_BUFFER):
- TZlibTransport.__init__(self, trans, rbuf_size)
- self.handle = trans.handle
- self.remoteaddr = trans.handle.getpeername()
-
-class TransportFactory:
- def getTransport(self, trans):
- buffered = Transport(trans)
- return buffered
-
-class TransportFactoryCompressed:
- _last_trans = None
- _last_z = None
-
- def getTransport(self, trans, compresslevel=9):
- if trans == self._last_trans:
- return self._last_z
- ztrans = TransportCompressed(Transport(trans), compresslevel)
- self._last_trans = trans
- self._last_z = ztrans
- return ztrans \ No newline at end of file
diff --git a/module/remote/thriftbackend/__init__.py b/module/remote/thriftbackend/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/remote/thriftbackend/__init__.py
+++ /dev/null
diff --git a/module/remote/thriftbackend/pyload.thrift b/module/remote/thriftbackend/pyload.thrift
deleted file mode 100644
index 1542e651a..000000000
--- a/module/remote/thriftbackend/pyload.thrift
+++ /dev/null
@@ -1,337 +0,0 @@
-namespace java org.pyload.thrift
-
-typedef i32 FileID
-typedef i32 PackageID
-typedef i32 TaskID
-typedef i32 ResultID
-typedef i32 InteractionID
-typedef list<string> LinkList
-typedef string PluginName
-typedef byte Progress
-typedef byte Priority
-
-
-enum DownloadStatus {
- Finished
- Offline,
- Online,
- Queued,
- Skipped,
- Waiting,
- TempOffline,
- Starting,
- Failed,
- Aborted,
- Decrypting,
- Custom,
- Downloading,
- Processing,
- Unknown
-}
-
-enum Destination {
- Collector,
- Queue
-}
-
-enum ElementType {
- Package,
- File
-}
-
-// types for user interaction
-// some may only be place holder currently not supported
-// also all input - output combination are not reasonable, see InteractionManager for further info
-enum Input {
- NONE,
- TEXT,
- TEXTBOX,
- PASSWORD,
- BOOL, // confirm like, yes or no dialog
- CLICK, // for positional captchas
- CHOICE, // choice from list
- MULTIPLE, // multiple choice from list of elements
- LIST, // arbitary list of elements
- TABLE // table like data structure
-}
-// more can be implemented by need
-
-// this describes the type of the outgoing interaction
-// ensure they can be logcial or'ed
-enum Output {
- CAPTCHA = 1,
- QUESTION = 2,
- NOTIFICATION = 4,
-}
-
-struct DownloadInfo {
- 1: FileID fid,
- 2: string name,
- 3: i64 speed,
- 4: i32 eta,
- 5: string format_eta,
- 6: i64 bleft,
- 7: i64 size,
- 8: string format_size,
- 9: Progress percent,
- 10: DownloadStatus status,
- 11: string statusmsg,
- 12: string format_wait,
- 13: i64 wait_until,
- 14: PackageID packageID,
- 15: string packageName,
- 16: PluginName plugin,
-}
-
-struct ServerStatus {
- 1: bool pause,
- 2: i16 active,
- 3: i16 queue,
- 4: i16 total,
- 5: i64 speed,
- 6: bool download,
- 7: bool reconnect
-}
-
-struct ConfigItem {
- 1: string name,
- 2: string description,
- 3: string value,
- 4: string type,
-}
-
-struct ConfigSection {
- 1: string name,
- 2: string description,
- 3: list<ConfigItem> items,
- 4: optional string outline
-}
-
-struct FileData {
- 1: FileID fid,
- 2: string url,
- 3: string name,
- 4: PluginName plugin,
- 5: i64 size,
- 6: string format_size,
- 7: DownloadStatus status,
- 8: string statusmsg,
- 9: PackageID packageID,
- 10: string error,
- 11: i16 order
-}
-
-struct PackageData {
- 1: PackageID pid,
- 2: string name,
- 3: string folder,
- 4: string site,
- 5: string password,
- 6: Destination dest,
- 7: i16 order,
- 8: optional i16 linksdone,
- 9: optional i64 sizedone,
- 10: optional i64 sizetotal,
- 11: optional i16 linkstotal,
- 12: optional list<FileData> links,
- 13: optional list<FileID> fids
-}
-
-struct InteractionTask {
- 1: InteractionID iid,
- 2: Input input,
- 3: list<string> structure,
- 4: list<string> preset,
- 5: Output output,
- 6: list<string> data,
- 7: string title,
- 8: string description,
- 9: string plugin,
-}
-
-struct CaptchaTask {
- 1: i16 tid,
- 2: binary data,
- 3: string type,
- 4: string resultType
-}
-
-struct EventInfo {
- 1: string eventname,
- 2: optional i32 id,
- 3: optional ElementType type,
- 4: optional Destination destination
-}
-
-struct UserData {
- 1: string name,
- 2: string email,
- 3: i32 role,
- 4: i32 permission,
- 5: string templateName
-}
-
-struct AccountInfo {
- 1: i64 validuntil,
- 2: string login,
- 3: map<string, list<string>> options,
- 4: bool valid,
- 5: i64 trafficleft,
- 6: i64 maxtraffic,
- 7: bool premium,
- 8: string type,
-}
-
-struct ServiceCall {
- 1: PluginName plugin,
- 2: string func,
- 3: optional list<string> arguments,
- 4: optional bool parseArguments, //default False
-}
-
-struct OnlineStatus {
- 1: string name,
- 2: PluginName plugin,
- 3: string packagename,
- 4: DownloadStatus status,
- 5: i64 size, // size <= 0 : unknown
-}
-
-struct OnlineCheck {
- 1: ResultID rid, // -1 -> nothing more to get
- 2: map<string, OnlineStatus> data, //url to result
-}
-
-
-// exceptions
-
-exception PackageDoesNotExists{
- 1: PackageID pid
-}
-
-exception FileDoesNotExists{
- 1: FileID fid
-}
-
-exception ServiceDoesNotExists{
- 1: string plugin
- 2: string func
-}
-
-exception ServiceException{
- 1: string msg
-}
-
-service Pyload {
-
- //config
- string getConfigValue(1: string category, 2: string option, 3: string section),
- void setConfigValue(1: string category, 2: string option, 3: string value, 4: string section),
- map<string, ConfigSection> getConfig(),
- map<string, ConfigSection> getPluginConfig(),
-
- // server status
- void pauseServer(),
- void unpauseServer(),
- bool togglePause(),
- ServerStatus statusServer(),
- i64 freeSpace(),
- string getServerVersion(),
- void kill(),
- void restart(),
- list<string> getLog(1: i32 offset),
- bool isTimeDownload(),
- bool isTimeReconnect(),
- bool toggleReconnect(),
-
- // download preparing
-
- // packagename - urls
- map<string, LinkList> generatePackages(1: LinkList links),
- map<PluginName, LinkList> checkURLs(1: LinkList urls),
- map<PluginName, LinkList> parseURLs(1: string html, 2: string url),
-
- // parses results and generates packages
- OnlineCheck checkOnlineStatus(1: LinkList urls),
- OnlineCheck checkOnlineStatusContainer(1: LinkList urls, 2: string filename, 3: binary data)
-
- // poll results from previosly started online check
- OnlineCheck pollResults(1: ResultID rid),
-
- // downloads - information
- list<DownloadInfo> statusDownloads(),
- PackageData getPackageData(1: PackageID pid) throws (1: PackageDoesNotExists e),
- PackageData getPackageInfo(1: PackageID pid) throws (1: PackageDoesNotExists e),
- FileData getFileData(1: FileID fid) throws (1: FileDoesNotExists e),
- list<PackageData> getQueue(),
- list<PackageData> getCollector(),
- list<PackageData> getQueueData(),
- list<PackageData> getCollectorData(),
- map<i16, PackageID> getPackageOrder(1: Destination destination),
- map<i16, FileID> getFileOrder(1: PackageID pid)
-
- // downloads - adding/deleting
- list<PackageID> generateAndAddPackages(1: LinkList links, 2: Destination dest),
- PackageID addPackage(1: string name, 2: LinkList links, 3: Destination dest),
- void addFiles(1: PackageID pid, 2: LinkList links),
- void uploadContainer(1: string filename, 2: binary data),
- void deleteFiles(1: list<FileID> fids),
- void deletePackages(1: list<PackageID> pids),
-
- // downloads - modifying
- void pushToQueue(1: PackageID pid),
- void pullFromQueue(1: PackageID pid),
- void restartPackage(1: PackageID pid),
- void restartFile(1: FileID fid),
- void recheckPackage(1: PackageID pid),
- void stopAllDownloads(),
- void stopDownloads(1: list<FileID> fids),
- void setPackageName(1: PackageID pid, 2: string name),
- void movePackage(1: Destination destination, 2: PackageID pid),
- void moveFiles(1: list<FileID> fids, 2: PackageID pid),
- void orderPackage(1: PackageID pid, 2: i16 position),
- void orderFile(1: FileID fid, 2: i16 position),
- void setPackageData(1: PackageID pid, 2: map<string, string> data) throws (1: PackageDoesNotExists e),
- list<PackageID> deleteFinished(),
- void restartFailed(),
-
- //events
- list<EventInfo> getEvents(1: string uuid)
-
- //accounts
- list<AccountInfo> getAccounts(1: bool refresh),
- list<string> getAccountTypes()
- void updateAccount(1: PluginName plugin, 2: string account, 3: string password, 4: map<string, string> options),
- void removeAccount(1: PluginName plugin, 2: string account),
-
- //auth
- bool login(1: string username, 2: string password),
- UserData getUserData(1: string username, 2:string password),
- map<string, UserData> getAllUserData(),
-
- //services
-
- // servicename : description
- map<PluginName, map<string, string>> getServices(),
- bool hasService(1: PluginName plugin, 2: string func),
- string call(1: ServiceCall info) throws (1: ServiceDoesNotExists ex, 2: ServiceException e),
-
-
- //info
- // {plugin: {name: value}}
- map<PluginName, map<string,string>> getAllInfo(),
- map<string, string> getInfoByPlugin(1: PluginName plugin),
-
- //scheduler
-
- // TODO
-
-
- // User interaction
-
- //captcha
- bool isCaptchaWaiting(),
- CaptchaTask getCaptchaTask(1: bool exclusive),
- string getCaptchaTaskStatus(1: TaskID tid),
- void setCaptchaResult(1: TaskID tid, 2: string result),
-}
diff --git a/module/remote/thriftbackend/thriftgen/__init__.py b/module/remote/thriftbackend/thriftgen/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/remote/thriftbackend/thriftgen/__init__.py
+++ /dev/null
diff --git a/module/remote/thriftbackend/thriftgen/pyload/Pyload-remote b/module/remote/thriftbackend/thriftgen/pyload/Pyload-remote
deleted file mode 100755
index bfaf5b078..000000000
--- a/module/remote/thriftbackend/thriftgen/pyload/Pyload-remote
+++ /dev/null
@@ -1,571 +0,0 @@
-#!/usr/bin/env python
-#
-# Autogenerated by Thrift Compiler (0.9.0-dev)
-#
-# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
-#
-# options string: py:slots,dynamic
-#
-
-import sys
-import pprint
-from urlparse import urlparse
-from thrift.transport import TTransport
-from thrift.transport import TSocket
-from thrift.transport import THttpClient
-from thrift.protocol import TBinaryProtocol
-
-import Pyload
-from ttypes import *
-
-if len(sys.argv) <= 1 or sys.argv[1] == '--help':
- print ''
- print 'Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] function [arg1 [arg2...]]'
- print ''
- print 'Functions:'
- print ' string getConfigValue(string category, string option, string section)'
- print ' void setConfigValue(string category, string option, string value, string section)'
- print ' getConfig()'
- print ' getPluginConfig()'
- print ' void pauseServer()'
- print ' void unpauseServer()'
- print ' bool togglePause()'
- print ' ServerStatus statusServer()'
- print ' i64 freeSpace()'
- print ' string getServerVersion()'
- print ' void kill()'
- print ' void restart()'
- print ' getLog(i32 offset)'
- print ' bool isTimeDownload()'
- print ' bool isTimeReconnect()'
- print ' bool toggleReconnect()'
- print ' generatePackages(LinkList links)'
- print ' checkURLs(LinkList urls)'
- print ' parseURLs(string html, string url)'
- print ' OnlineCheck checkOnlineStatus(LinkList urls)'
- print ' OnlineCheck checkOnlineStatusContainer(LinkList urls, string filename, string data)'
- print ' OnlineCheck pollResults(ResultID rid)'
- print ' statusDownloads()'
- print ' PackageData getPackageData(PackageID pid)'
- print ' PackageData getPackageInfo(PackageID pid)'
- print ' FileData getFileData(FileID fid)'
- print ' getQueue()'
- print ' getCollector()'
- print ' getQueueData()'
- print ' getCollectorData()'
- print ' getPackageOrder(Destination destination)'
- print ' getFileOrder(PackageID pid)'
- print ' generateAndAddPackages(LinkList links, Destination dest)'
- print ' PackageID addPackage(string name, LinkList links, Destination dest)'
- print ' void addFiles(PackageID pid, LinkList links)'
- print ' void uploadContainer(string filename, string data)'
- print ' void deleteFiles( fids)'
- print ' void deletePackages( pids)'
- print ' void pushToQueue(PackageID pid)'
- print ' void pullFromQueue(PackageID pid)'
- print ' void restartPackage(PackageID pid)'
- print ' void restartFile(FileID fid)'
- print ' void recheckPackage(PackageID pid)'
- print ' void stopAllDownloads()'
- print ' void stopDownloads( fids)'
- print ' void setPackageName(PackageID pid, string name)'
- print ' void movePackage(Destination destination, PackageID pid)'
- print ' void moveFiles( fids, PackageID pid)'
- print ' void orderPackage(PackageID pid, i16 position)'
- print ' void orderFile(FileID fid, i16 position)'
- print ' void setPackageData(PackageID pid, data)'
- print ' deleteFinished()'
- print ' void restartFailed()'
- print ' getEvents(string uuid)'
- print ' getAccounts(bool refresh)'
- print ' getAccountTypes()'
- print ' void updateAccount(PluginName plugin, string account, string password, options)'
- print ' void removeAccount(PluginName plugin, string account)'
- print ' bool login(string username, string password)'
- print ' UserData getUserData(string username, string password)'
- print ' getAllUserData()'
- print ' getServices()'
- print ' bool hasService(PluginName plugin, string func)'
- print ' string call(ServiceCall info)'
- print ' getAllInfo()'
- print ' getInfoByPlugin(PluginName plugin)'
- print ' bool isCaptchaWaiting()'
- print ' CaptchaTask getCaptchaTask(bool exclusive)'
- print ' string getCaptchaTaskStatus(TaskID tid)'
- print ' void setCaptchaResult(TaskID tid, string result)'
- print ''
- sys.exit(0)
-
-pp = pprint.PrettyPrinter(indent = 2)
-host = 'localhost'
-port = 9090
-uri = ''
-framed = False
-http = False
-argi = 1
-
-if sys.argv[argi] == '-h':
- parts = sys.argv[argi+1].split(':')
- host = parts[0]
- if len(parts) > 1:
- port = int(parts[1])
- argi += 2
-
-if sys.argv[argi] == '-u':
- url = urlparse(sys.argv[argi+1])
- parts = url[1].split(':')
- host = parts[0]
- if len(parts) > 1:
- port = int(parts[1])
- else:
- port = 80
- uri = url[2]
- if url[4]:
- uri += '?%s' % url[4]
- http = True
- argi += 2
-
-if sys.argv[argi] == '-f' or sys.argv[argi] == '-framed':
- framed = True
- argi += 1
-
-cmd = sys.argv[argi]
-args = sys.argv[argi+1:]
-
-if http:
- transport = THttpClient.THttpClient(host, port, uri)
-else:
- socket = TSocket.TSocket(host, port)
- if framed:
- transport = TTransport.TFramedTransport(socket)
- else:
- transport = TTransport.TBufferedTransport(socket)
-protocol = TBinaryProtocol.TBinaryProtocol(transport)
-client = Pyload.Client(protocol)
-transport.open()
-
-if cmd == 'getConfigValue':
- if len(args) != 3:
- print 'getConfigValue requires 3 args'
- sys.exit(1)
- pp.pprint(client.getConfigValue(args[0],args[1],args[2],))
-
-elif cmd == 'setConfigValue':
- if len(args) != 4:
- print 'setConfigValue requires 4 args'
- sys.exit(1)
- pp.pprint(client.setConfigValue(args[0],args[1],args[2],args[3],))
-
-elif cmd == 'getConfig':
- if len(args) != 0:
- print 'getConfig requires 0 args'
- sys.exit(1)
- pp.pprint(client.getConfig())
-
-elif cmd == 'getPluginConfig':
- if len(args) != 0:
- print 'getPluginConfig requires 0 args'
- sys.exit(1)
- pp.pprint(client.getPluginConfig())
-
-elif cmd == 'pauseServer':
- if len(args) != 0:
- print 'pauseServer requires 0 args'
- sys.exit(1)
- pp.pprint(client.pauseServer())
-
-elif cmd == 'unpauseServer':
- if len(args) != 0:
- print 'unpauseServer requires 0 args'
- sys.exit(1)
- pp.pprint(client.unpauseServer())
-
-elif cmd == 'togglePause':
- if len(args) != 0:
- print 'togglePause requires 0 args'
- sys.exit(1)
- pp.pprint(client.togglePause())
-
-elif cmd == 'statusServer':
- if len(args) != 0:
- print 'statusServer requires 0 args'
- sys.exit(1)
- pp.pprint(client.statusServer())
-
-elif cmd == 'freeSpace':
- if len(args) != 0:
- print 'freeSpace requires 0 args'
- sys.exit(1)
- pp.pprint(client.freeSpace())
-
-elif cmd == 'getServerVersion':
- if len(args) != 0:
- print 'getServerVersion requires 0 args'
- sys.exit(1)
- pp.pprint(client.getServerVersion())
-
-elif cmd == 'kill':
- if len(args) != 0:
- print 'kill requires 0 args'
- sys.exit(1)
- pp.pprint(client.kill())
-
-elif cmd == 'restart':
- if len(args) != 0:
- print 'restart requires 0 args'
- sys.exit(1)
- pp.pprint(client.restart())
-
-elif cmd == 'getLog':
- if len(args) != 1:
- print 'getLog requires 1 args'
- sys.exit(1)
- pp.pprint(client.getLog(eval(args[0]),))
-
-elif cmd == 'isTimeDownload':
- if len(args) != 0:
- print 'isTimeDownload requires 0 args'
- sys.exit(1)
- pp.pprint(client.isTimeDownload())
-
-elif cmd == 'isTimeReconnect':
- if len(args) != 0:
- print 'isTimeReconnect requires 0 args'
- sys.exit(1)
- pp.pprint(client.isTimeReconnect())
-
-elif cmd == 'toggleReconnect':
- if len(args) != 0:
- print 'toggleReconnect requires 0 args'
- sys.exit(1)
- pp.pprint(client.toggleReconnect())
-
-elif cmd == 'generatePackages':
- if len(args) != 1:
- print 'generatePackages requires 1 args'
- sys.exit(1)
- pp.pprint(client.generatePackages(eval(args[0]),))
-
-elif cmd == 'checkURLs':
- if len(args) != 1:
- print 'checkURLs requires 1 args'
- sys.exit(1)
- pp.pprint(client.checkURLs(eval(args[0]),))
-
-elif cmd == 'parseURLs':
- if len(args) != 2:
- print 'parseURLs requires 2 args'
- sys.exit(1)
- pp.pprint(client.parseURLs(args[0],args[1],))
-
-elif cmd == 'checkOnlineStatus':
- if len(args) != 1:
- print 'checkOnlineStatus requires 1 args'
- sys.exit(1)
- pp.pprint(client.checkOnlineStatus(eval(args[0]),))
-
-elif cmd == 'checkOnlineStatusContainer':
- if len(args) != 3:
- print 'checkOnlineStatusContainer requires 3 args'
- sys.exit(1)
- pp.pprint(client.checkOnlineStatusContainer(eval(args[0]),args[1],args[2],))
-
-elif cmd == 'pollResults':
- if len(args) != 1:
- print 'pollResults requires 1 args'
- sys.exit(1)
- pp.pprint(client.pollResults(eval(args[0]),))
-
-elif cmd == 'statusDownloads':
- if len(args) != 0:
- print 'statusDownloads requires 0 args'
- sys.exit(1)
- pp.pprint(client.statusDownloads())
-
-elif cmd == 'getPackageData':
- if len(args) != 1:
- print 'getPackageData requires 1 args'
- sys.exit(1)
- pp.pprint(client.getPackageData(eval(args[0]),))
-
-elif cmd == 'getPackageInfo':
- if len(args) != 1:
- print 'getPackageInfo requires 1 args'
- sys.exit(1)
- pp.pprint(client.getPackageInfo(eval(args[0]),))
-
-elif cmd == 'getFileData':
- if len(args) != 1:
- print 'getFileData requires 1 args'
- sys.exit(1)
- pp.pprint(client.getFileData(eval(args[0]),))
-
-elif cmd == 'getQueue':
- if len(args) != 0:
- print 'getQueue requires 0 args'
- sys.exit(1)
- pp.pprint(client.getQueue())
-
-elif cmd == 'getCollector':
- if len(args) != 0:
- print 'getCollector requires 0 args'
- sys.exit(1)
- pp.pprint(client.getCollector())
-
-elif cmd == 'getQueueData':
- if len(args) != 0:
- print 'getQueueData requires 0 args'
- sys.exit(1)
- pp.pprint(client.getQueueData())
-
-elif cmd == 'getCollectorData':
- if len(args) != 0:
- print 'getCollectorData requires 0 args'
- sys.exit(1)
- pp.pprint(client.getCollectorData())
-
-elif cmd == 'getPackageOrder':
- if len(args) != 1:
- print 'getPackageOrder requires 1 args'
- sys.exit(1)
- pp.pprint(client.getPackageOrder(eval(args[0]),))
-
-elif cmd == 'getFileOrder':
- if len(args) != 1:
- print 'getFileOrder requires 1 args'
- sys.exit(1)
- pp.pprint(client.getFileOrder(eval(args[0]),))
-
-elif cmd == 'generateAndAddPackages':
- if len(args) != 2:
- print 'generateAndAddPackages requires 2 args'
- sys.exit(1)
- pp.pprint(client.generateAndAddPackages(eval(args[0]),eval(args[1]),))
-
-elif cmd == 'addPackage':
- if len(args) != 3:
- print 'addPackage requires 3 args'
- sys.exit(1)
- pp.pprint(client.addPackage(args[0],eval(args[1]),eval(args[2]),))
-
-elif cmd == 'addFiles':
- if len(args) != 2:
- print 'addFiles requires 2 args'
- sys.exit(1)
- pp.pprint(client.addFiles(eval(args[0]),eval(args[1]),))
-
-elif cmd == 'uploadContainer':
- if len(args) != 2:
- print 'uploadContainer requires 2 args'
- sys.exit(1)
- pp.pprint(client.uploadContainer(args[0],args[1],))
-
-elif cmd == 'deleteFiles':
- if len(args) != 1:
- print 'deleteFiles requires 1 args'
- sys.exit(1)
- pp.pprint(client.deleteFiles(eval(args[0]),))
-
-elif cmd == 'deletePackages':
- if len(args) != 1:
- print 'deletePackages requires 1 args'
- sys.exit(1)
- pp.pprint(client.deletePackages(eval(args[0]),))
-
-elif cmd == 'pushToQueue':
- if len(args) != 1:
- print 'pushToQueue requires 1 args'
- sys.exit(1)
- pp.pprint(client.pushToQueue(eval(args[0]),))
-
-elif cmd == 'pullFromQueue':
- if len(args) != 1:
- print 'pullFromQueue requires 1 args'
- sys.exit(1)
- pp.pprint(client.pullFromQueue(eval(args[0]),))
-
-elif cmd == 'restartPackage':
- if len(args) != 1:
- print 'restartPackage requires 1 args'
- sys.exit(1)
- pp.pprint(client.restartPackage(eval(args[0]),))
-
-elif cmd == 'restartFile':
- if len(args) != 1:
- print 'restartFile requires 1 args'
- sys.exit(1)
- pp.pprint(client.restartFile(eval(args[0]),))
-
-elif cmd == 'recheckPackage':
- if len(args) != 1:
- print 'recheckPackage requires 1 args'
- sys.exit(1)
- pp.pprint(client.recheckPackage(eval(args[0]),))
-
-elif cmd == 'stopAllDownloads':
- if len(args) != 0:
- print 'stopAllDownloads requires 0 args'
- sys.exit(1)
- pp.pprint(client.stopAllDownloads())
-
-elif cmd == 'stopDownloads':
- if len(args) != 1:
- print 'stopDownloads requires 1 args'
- sys.exit(1)
- pp.pprint(client.stopDownloads(eval(args[0]),))
-
-elif cmd == 'setPackageName':
- if len(args) != 2:
- print 'setPackageName requires 2 args'
- sys.exit(1)
- pp.pprint(client.setPackageName(eval(args[0]),args[1],))
-
-elif cmd == 'movePackage':
- if len(args) != 2:
- print 'movePackage requires 2 args'
- sys.exit(1)
- pp.pprint(client.movePackage(eval(args[0]),eval(args[1]),))
-
-elif cmd == 'moveFiles':
- if len(args) != 2:
- print 'moveFiles requires 2 args'
- sys.exit(1)
- pp.pprint(client.moveFiles(eval(args[0]),eval(args[1]),))
-
-elif cmd == 'orderPackage':
- if len(args) != 2:
- print 'orderPackage requires 2 args'
- sys.exit(1)
- pp.pprint(client.orderPackage(eval(args[0]),eval(args[1]),))
-
-elif cmd == 'orderFile':
- if len(args) != 2:
- print 'orderFile requires 2 args'
- sys.exit(1)
- pp.pprint(client.orderFile(eval(args[0]),eval(args[1]),))
-
-elif cmd == 'setPackageData':
- if len(args) != 2:
- print 'setPackageData requires 2 args'
- sys.exit(1)
- pp.pprint(client.setPackageData(eval(args[0]),eval(args[1]),))
-
-elif cmd == 'deleteFinished':
- if len(args) != 0:
- print 'deleteFinished requires 0 args'
- sys.exit(1)
- pp.pprint(client.deleteFinished())
-
-elif cmd == 'restartFailed':
- if len(args) != 0:
- print 'restartFailed requires 0 args'
- sys.exit(1)
- pp.pprint(client.restartFailed())
-
-elif cmd == 'getEvents':
- if len(args) != 1:
- print 'getEvents requires 1 args'
- sys.exit(1)
- pp.pprint(client.getEvents(args[0],))
-
-elif cmd == 'getAccounts':
- if len(args) != 1:
- print 'getAccounts requires 1 args'
- sys.exit(1)
- pp.pprint(client.getAccounts(eval(args[0]),))
-
-elif cmd == 'getAccountTypes':
- if len(args) != 0:
- print 'getAccountTypes requires 0 args'
- sys.exit(1)
- pp.pprint(client.getAccountTypes())
-
-elif cmd == 'updateAccount':
- if len(args) != 4:
- print 'updateAccount requires 4 args'
- sys.exit(1)
- pp.pprint(client.updateAccount(eval(args[0]),args[1],args[2],eval(args[3]),))
-
-elif cmd == 'removeAccount':
- if len(args) != 2:
- print 'removeAccount requires 2 args'
- sys.exit(1)
- pp.pprint(client.removeAccount(eval(args[0]),args[1],))
-
-elif cmd == 'login':
- if len(args) != 2:
- print 'login requires 2 args'
- sys.exit(1)
- pp.pprint(client.login(args[0],args[1],))
-
-elif cmd == 'getUserData':
- if len(args) != 2:
- print 'getUserData requires 2 args'
- sys.exit(1)
- pp.pprint(client.getUserData(args[0],args[1],))
-
-elif cmd == 'getAllUserData':
- if len(args) != 0:
- print 'getAllUserData requires 0 args'
- sys.exit(1)
- pp.pprint(client.getAllUserData())
-
-elif cmd == 'getServices':
- if len(args) != 0:
- print 'getServices requires 0 args'
- sys.exit(1)
- pp.pprint(client.getServices())
-
-elif cmd == 'hasService':
- if len(args) != 2:
- print 'hasService requires 2 args'
- sys.exit(1)
- pp.pprint(client.hasService(eval(args[0]),args[1],))
-
-elif cmd == 'call':
- if len(args) != 1:
- print 'call requires 1 args'
- sys.exit(1)
- pp.pprint(client.call(eval(args[0]),))
-
-elif cmd == 'getAllInfo':
- if len(args) != 0:
- print 'getAllInfo requires 0 args'
- sys.exit(1)
- pp.pprint(client.getAllInfo())
-
-elif cmd == 'getInfoByPlugin':
- if len(args) != 1:
- print 'getInfoByPlugin requires 1 args'
- sys.exit(1)
- pp.pprint(client.getInfoByPlugin(eval(args[0]),))
-
-elif cmd == 'isCaptchaWaiting':
- if len(args) != 0:
- print 'isCaptchaWaiting requires 0 args'
- sys.exit(1)
- pp.pprint(client.isCaptchaWaiting())
-
-elif cmd == 'getCaptchaTask':
- if len(args) != 1:
- print 'getCaptchaTask requires 1 args'
- sys.exit(1)
- pp.pprint(client.getCaptchaTask(eval(args[0]),))
-
-elif cmd == 'getCaptchaTaskStatus':
- if len(args) != 1:
- print 'getCaptchaTaskStatus requires 1 args'
- sys.exit(1)
- pp.pprint(client.getCaptchaTaskStatus(eval(args[0]),))
-
-elif cmd == 'setCaptchaResult':
- if len(args) != 2:
- print 'setCaptchaResult requires 2 args'
- sys.exit(1)
- pp.pprint(client.setCaptchaResult(eval(args[0]),args[1],))
-
-else:
- print 'Unrecognized method %s' % cmd
- sys.exit(1)
-
-transport.close()
diff --git a/module/remote/thriftbackend/thriftgen/pyload/Pyload.py b/module/remote/thriftbackend/thriftgen/pyload/Pyload.py
deleted file mode 100644
index 78a42f16a..000000000
--- a/module/remote/thriftbackend/thriftgen/pyload/Pyload.py
+++ /dev/null
@@ -1,5534 +0,0 @@
-#
-# Autogenerated by Thrift Compiler (0.9.0-dev)
-#
-# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
-#
-# options string: py:slots,dynamic
-#
-
-from thrift.Thrift import TType, TMessageType, TException
-from ttypes import *
-from thrift.Thrift import TProcessor
-from thrift.protocol.TBase import TBase, TExceptionBase
-
-
-class Iface(object):
- def getConfigValue(self, category, option, section):
- """
- Parameters:
- - category
- - option
- - section
- """
- pass
-
- def setConfigValue(self, category, option, value, section):
- """
- Parameters:
- - category
- - option
- - value
- - section
- """
- pass
-
- def getConfig(self, ):
- pass
-
- def getPluginConfig(self, ):
- pass
-
- def pauseServer(self, ):
- pass
-
- def unpauseServer(self, ):
- pass
-
- def togglePause(self, ):
- pass
-
- def statusServer(self, ):
- pass
-
- def freeSpace(self, ):
- pass
-
- def getServerVersion(self, ):
- pass
-
- def kill(self, ):
- pass
-
- def restart(self, ):
- pass
-
- def getLog(self, offset):
- """
- Parameters:
- - offset
- """
- pass
-
- def isTimeDownload(self, ):
- pass
-
- def isTimeReconnect(self, ):
- pass
-
- def toggleReconnect(self, ):
- pass
-
- def generatePackages(self, links):
- """
- Parameters:
- - links
- """
- pass
-
- def checkURLs(self, urls):
- """
- Parameters:
- - urls
- """
- pass
-
- def parseURLs(self, html, url):
- """
- Parameters:
- - html
- - url
- """
- pass
-
- def checkOnlineStatus(self, urls):
- """
- Parameters:
- - urls
- """
- pass
-
- def checkOnlineStatusContainer(self, urls, filename, data):
- """
- Parameters:
- - urls
- - filename
- - data
- """
- pass
-
- def pollResults(self, rid):
- """
- Parameters:
- - rid
- """
- pass
-
- def statusDownloads(self, ):
- pass
-
- def getPackageData(self, pid):
- """
- Parameters:
- - pid
- """
- pass
-
- def getPackageInfo(self, pid):
- """
- Parameters:
- - pid
- """
- pass
-
- def getFileData(self, fid):
- """
- Parameters:
- - fid
- """
- pass
-
- def getQueue(self, ):
- pass
-
- def getCollector(self, ):
- pass
-
- def getQueueData(self, ):
- pass
-
- def getCollectorData(self, ):
- pass
-
- def getPackageOrder(self, destination):
- """
- Parameters:
- - destination
- """
- pass
-
- def getFileOrder(self, pid):
- """
- Parameters:
- - pid
- """
- pass
-
- def generateAndAddPackages(self, links, dest):
- """
- Parameters:
- - links
- - dest
- """
- pass
-
- def addPackage(self, name, links, dest):
- """
- Parameters:
- - name
- - links
- - dest
- """
- pass
-
- def addFiles(self, pid, links):
- """
- Parameters:
- - pid
- - links
- """
- pass
-
- def uploadContainer(self, filename, data):
- """
- Parameters:
- - filename
- - data
- """
- pass
-
- def deleteFiles(self, fids):
- """
- Parameters:
- - fids
- """
- pass
-
- def deletePackages(self, pids):
- """
- Parameters:
- - pids
- """
- pass
-
- def pushToQueue(self, pid):
- """
- Parameters:
- - pid
- """
- pass
-
- def pullFromQueue(self, pid):
- """
- Parameters:
- - pid
- """
- pass
-
- def restartPackage(self, pid):
- """
- Parameters:
- - pid
- """
- pass
-
- def restartFile(self, fid):
- """
- Parameters:
- - fid
- """
- pass
-
- def recheckPackage(self, pid):
- """
- Parameters:
- - pid
- """
- pass
-
- def stopAllDownloads(self, ):
- pass
-
- def stopDownloads(self, fids):
- """
- Parameters:
- - fids
- """
- pass
-
- def setPackageName(self, pid, name):
- """
- Parameters:
- - pid
- - name
- """
- pass
-
- def movePackage(self, destination, pid):
- """
- Parameters:
- - destination
- - pid
- """
- pass
-
- def moveFiles(self, fids, pid):
- """
- Parameters:
- - fids
- - pid
- """
- pass
-
- def orderPackage(self, pid, position):
- """
- Parameters:
- - pid
- - position
- """
- pass
-
- def orderFile(self, fid, position):
- """
- Parameters:
- - fid
- - position
- """
- pass
-
- def setPackageData(self, pid, data):
- """
- Parameters:
- - pid
- - data
- """
- pass
-
- def deleteFinished(self, ):
- pass
-
- def restartFailed(self, ):
- pass
-
- def getEvents(self, uuid):
- """
- Parameters:
- - uuid
- """
- pass
-
- def getAccounts(self, refresh):
- """
- Parameters:
- - refresh
- """
- pass
-
- def getAccountTypes(self, ):
- pass
-
- def updateAccount(self, plugin, account, password, options):
- """
- Parameters:
- - plugin
- - account
- - password
- - options
- """
- pass
-
- def removeAccount(self, plugin, account):
- """
- Parameters:
- - plugin
- - account
- """
- pass
-
- def login(self, username, password):
- """
- Parameters:
- - username
- - password
- """
- pass
-
- def getUserData(self, username, password):
- """
- Parameters:
- - username
- - password
- """
- pass
-
- def getAllUserData(self, ):
- pass
-
- def getServices(self, ):
- pass
-
- def hasService(self, plugin, func):
- """
- Parameters:
- - plugin
- - func
- """
- pass
-
- def call(self, info):
- """
- Parameters:
- - info
- """
- pass
-
- def getAllInfo(self, ):
- pass
-
- def getInfoByPlugin(self, plugin):
- """
- Parameters:
- - plugin
- """
- pass
-
- def isCaptchaWaiting(self, ):
- pass
-
- def getCaptchaTask(self, exclusive):
- """
- Parameters:
- - exclusive
- """
- pass
-
- def getCaptchaTaskStatus(self, tid):
- """
- Parameters:
- - tid
- """
- pass
-
- def setCaptchaResult(self, tid, result):
- """
- Parameters:
- - tid
- - result
- """
- pass
-
-
-class Client(Iface):
- def __init__(self, iprot, oprot=None):
- self._iprot = self._oprot = iprot
- if oprot is not None:
- self._oprot = oprot
- self._seqid = 0
-
- def getConfigValue(self, category, option, section):
- """
- Parameters:
- - category
- - option
- - section
- """
- self.send_getConfigValue(category, option, section)
- return self.recv_getConfigValue()
-
- def send_getConfigValue(self, category, option, section):
- self._oprot.writeMessageBegin('getConfigValue', TMessageType.CALL, self._seqid)
- args = getConfigValue_args()
- args.category = category
- args.option = option
- args.section = section
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getConfigValue(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getConfigValue_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getConfigValue failed: unknown result");
-
- def setConfigValue(self, category, option, value, section):
- """
- Parameters:
- - category
- - option
- - value
- - section
- """
- self.send_setConfigValue(category, option, value, section)
- self.recv_setConfigValue()
-
- def send_setConfigValue(self, category, option, value, section):
- self._oprot.writeMessageBegin('setConfigValue', TMessageType.CALL, self._seqid)
- args = setConfigValue_args()
- args.category = category
- args.option = option
- args.value = value
- args.section = section
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_setConfigValue(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = setConfigValue_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def getConfig(self, ):
- self.send_getConfig()
- return self.recv_getConfig()
-
- def send_getConfig(self, ):
- self._oprot.writeMessageBegin('getConfig', TMessageType.CALL, self._seqid)
- args = getConfig_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getConfig(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getConfig_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getConfig failed: unknown result");
-
- def getPluginConfig(self, ):
- self.send_getPluginConfig()
- return self.recv_getPluginConfig()
-
- def send_getPluginConfig(self, ):
- self._oprot.writeMessageBegin('getPluginConfig', TMessageType.CALL, self._seqid)
- args = getPluginConfig_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getPluginConfig(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getPluginConfig_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getPluginConfig failed: unknown result");
-
- def pauseServer(self, ):
- self.send_pauseServer()
- self.recv_pauseServer()
-
- def send_pauseServer(self, ):
- self._oprot.writeMessageBegin('pauseServer', TMessageType.CALL, self._seqid)
- args = pauseServer_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_pauseServer(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = pauseServer_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def unpauseServer(self, ):
- self.send_unpauseServer()
- self.recv_unpauseServer()
-
- def send_unpauseServer(self, ):
- self._oprot.writeMessageBegin('unpauseServer', TMessageType.CALL, self._seqid)
- args = unpauseServer_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_unpauseServer(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = unpauseServer_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def togglePause(self, ):
- self.send_togglePause()
- return self.recv_togglePause()
-
- def send_togglePause(self, ):
- self._oprot.writeMessageBegin('togglePause', TMessageType.CALL, self._seqid)
- args = togglePause_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_togglePause(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = togglePause_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "togglePause failed: unknown result");
-
- def statusServer(self, ):
- self.send_statusServer()
- return self.recv_statusServer()
-
- def send_statusServer(self, ):
- self._oprot.writeMessageBegin('statusServer', TMessageType.CALL, self._seqid)
- args = statusServer_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_statusServer(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = statusServer_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "statusServer failed: unknown result");
-
- def freeSpace(self, ):
- self.send_freeSpace()
- return self.recv_freeSpace()
-
- def send_freeSpace(self, ):
- self._oprot.writeMessageBegin('freeSpace', TMessageType.CALL, self._seqid)
- args = freeSpace_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_freeSpace(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = freeSpace_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "freeSpace failed: unknown result");
-
- def getServerVersion(self, ):
- self.send_getServerVersion()
- return self.recv_getServerVersion()
-
- def send_getServerVersion(self, ):
- self._oprot.writeMessageBegin('getServerVersion', TMessageType.CALL, self._seqid)
- args = getServerVersion_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getServerVersion(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getServerVersion_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getServerVersion failed: unknown result");
-
- def kill(self, ):
- self.send_kill()
- self.recv_kill()
-
- def send_kill(self, ):
- self._oprot.writeMessageBegin('kill', TMessageType.CALL, self._seqid)
- args = kill_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_kill(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = kill_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def restart(self, ):
- self.send_restart()
- self.recv_restart()
-
- def send_restart(self, ):
- self._oprot.writeMessageBegin('restart', TMessageType.CALL, self._seqid)
- args = restart_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_restart(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = restart_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def getLog(self, offset):
- """
- Parameters:
- - offset
- """
- self.send_getLog(offset)
- return self.recv_getLog()
-
- def send_getLog(self, offset):
- self._oprot.writeMessageBegin('getLog', TMessageType.CALL, self._seqid)
- args = getLog_args()
- args.offset = offset
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getLog(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getLog_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getLog failed: unknown result");
-
- def isTimeDownload(self, ):
- self.send_isTimeDownload()
- return self.recv_isTimeDownload()
-
- def send_isTimeDownload(self, ):
- self._oprot.writeMessageBegin('isTimeDownload', TMessageType.CALL, self._seqid)
- args = isTimeDownload_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_isTimeDownload(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = isTimeDownload_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "isTimeDownload failed: unknown result");
-
- def isTimeReconnect(self, ):
- self.send_isTimeReconnect()
- return self.recv_isTimeReconnect()
-
- def send_isTimeReconnect(self, ):
- self._oprot.writeMessageBegin('isTimeReconnect', TMessageType.CALL, self._seqid)
- args = isTimeReconnect_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_isTimeReconnect(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = isTimeReconnect_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "isTimeReconnect failed: unknown result");
-
- def toggleReconnect(self, ):
- self.send_toggleReconnect()
- return self.recv_toggleReconnect()
-
- def send_toggleReconnect(self, ):
- self._oprot.writeMessageBegin('toggleReconnect', TMessageType.CALL, self._seqid)
- args = toggleReconnect_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_toggleReconnect(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = toggleReconnect_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "toggleReconnect failed: unknown result");
-
- def generatePackages(self, links):
- """
- Parameters:
- - links
- """
- self.send_generatePackages(links)
- return self.recv_generatePackages()
-
- def send_generatePackages(self, links):
- self._oprot.writeMessageBegin('generatePackages', TMessageType.CALL, self._seqid)
- args = generatePackages_args()
- args.links = links
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_generatePackages(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = generatePackages_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "generatePackages failed: unknown result");
-
- def checkURLs(self, urls):
- """
- Parameters:
- - urls
- """
- self.send_checkURLs(urls)
- return self.recv_checkURLs()
-
- def send_checkURLs(self, urls):
- self._oprot.writeMessageBegin('checkURLs', TMessageType.CALL, self._seqid)
- args = checkURLs_args()
- args.urls = urls
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_checkURLs(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = checkURLs_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "checkURLs failed: unknown result");
-
- def parseURLs(self, html, url):
- """
- Parameters:
- - html
- - url
- """
- self.send_parseURLs(html, url)
- return self.recv_parseURLs()
-
- def send_parseURLs(self, html, url):
- self._oprot.writeMessageBegin('parseURLs', TMessageType.CALL, self._seqid)
- args = parseURLs_args()
- args.html = html
- args.url = url
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_parseURLs(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = parseURLs_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "parseURLs failed: unknown result");
-
- def checkOnlineStatus(self, urls):
- """
- Parameters:
- - urls
- """
- self.send_checkOnlineStatus(urls)
- return self.recv_checkOnlineStatus()
-
- def send_checkOnlineStatus(self, urls):
- self._oprot.writeMessageBegin('checkOnlineStatus', TMessageType.CALL, self._seqid)
- args = checkOnlineStatus_args()
- args.urls = urls
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_checkOnlineStatus(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = checkOnlineStatus_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "checkOnlineStatus failed: unknown result");
-
- def checkOnlineStatusContainer(self, urls, filename, data):
- """
- Parameters:
- - urls
- - filename
- - data
- """
- self.send_checkOnlineStatusContainer(urls, filename, data)
- return self.recv_checkOnlineStatusContainer()
-
- def send_checkOnlineStatusContainer(self, urls, filename, data):
- self._oprot.writeMessageBegin('checkOnlineStatusContainer', TMessageType.CALL, self._seqid)
- args = checkOnlineStatusContainer_args()
- args.urls = urls
- args.filename = filename
- args.data = data
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_checkOnlineStatusContainer(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = checkOnlineStatusContainer_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "checkOnlineStatusContainer failed: unknown result");
-
- def pollResults(self, rid):
- """
- Parameters:
- - rid
- """
- self.send_pollResults(rid)
- return self.recv_pollResults()
-
- def send_pollResults(self, rid):
- self._oprot.writeMessageBegin('pollResults', TMessageType.CALL, self._seqid)
- args = pollResults_args()
- args.rid = rid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_pollResults(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = pollResults_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "pollResults failed: unknown result");
-
- def statusDownloads(self, ):
- self.send_statusDownloads()
- return self.recv_statusDownloads()
-
- def send_statusDownloads(self, ):
- self._oprot.writeMessageBegin('statusDownloads', TMessageType.CALL, self._seqid)
- args = statusDownloads_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_statusDownloads(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = statusDownloads_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "statusDownloads failed: unknown result");
-
- def getPackageData(self, pid):
- """
- Parameters:
- - pid
- """
- self.send_getPackageData(pid)
- return self.recv_getPackageData()
-
- def send_getPackageData(self, pid):
- self._oprot.writeMessageBegin('getPackageData', TMessageType.CALL, self._seqid)
- args = getPackageData_args()
- args.pid = pid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getPackageData(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getPackageData_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- if result.e is not None:
- raise result.e
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getPackageData failed: unknown result");
-
- def getPackageInfo(self, pid):
- """
- Parameters:
- - pid
- """
- self.send_getPackageInfo(pid)
- return self.recv_getPackageInfo()
-
- def send_getPackageInfo(self, pid):
- self._oprot.writeMessageBegin('getPackageInfo', TMessageType.CALL, self._seqid)
- args = getPackageInfo_args()
- args.pid = pid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getPackageInfo(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getPackageInfo_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- if result.e is not None:
- raise result.e
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getPackageInfo failed: unknown result");
-
- def getFileData(self, fid):
- """
- Parameters:
- - fid
- """
- self.send_getFileData(fid)
- return self.recv_getFileData()
-
- def send_getFileData(self, fid):
- self._oprot.writeMessageBegin('getFileData', TMessageType.CALL, self._seqid)
- args = getFileData_args()
- args.fid = fid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getFileData(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getFileData_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- if result.e is not None:
- raise result.e
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getFileData failed: unknown result");
-
- def getQueue(self, ):
- self.send_getQueue()
- return self.recv_getQueue()
-
- def send_getQueue(self, ):
- self._oprot.writeMessageBegin('getQueue', TMessageType.CALL, self._seqid)
- args = getQueue_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getQueue(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getQueue_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getQueue failed: unknown result");
-
- def getCollector(self, ):
- self.send_getCollector()
- return self.recv_getCollector()
-
- def send_getCollector(self, ):
- self._oprot.writeMessageBegin('getCollector', TMessageType.CALL, self._seqid)
- args = getCollector_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getCollector(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getCollector_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getCollector failed: unknown result");
-
- def getQueueData(self, ):
- self.send_getQueueData()
- return self.recv_getQueueData()
-
- def send_getQueueData(self, ):
- self._oprot.writeMessageBegin('getQueueData', TMessageType.CALL, self._seqid)
- args = getQueueData_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getQueueData(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getQueueData_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getQueueData failed: unknown result");
-
- def getCollectorData(self, ):
- self.send_getCollectorData()
- return self.recv_getCollectorData()
-
- def send_getCollectorData(self, ):
- self._oprot.writeMessageBegin('getCollectorData', TMessageType.CALL, self._seqid)
- args = getCollectorData_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getCollectorData(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getCollectorData_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getCollectorData failed: unknown result");
-
- def getPackageOrder(self, destination):
- """
- Parameters:
- - destination
- """
- self.send_getPackageOrder(destination)
- return self.recv_getPackageOrder()
-
- def send_getPackageOrder(self, destination):
- self._oprot.writeMessageBegin('getPackageOrder', TMessageType.CALL, self._seqid)
- args = getPackageOrder_args()
- args.destination = destination
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getPackageOrder(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getPackageOrder_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getPackageOrder failed: unknown result");
-
- def getFileOrder(self, pid):
- """
- Parameters:
- - pid
- """
- self.send_getFileOrder(pid)
- return self.recv_getFileOrder()
-
- def send_getFileOrder(self, pid):
- self._oprot.writeMessageBegin('getFileOrder', TMessageType.CALL, self._seqid)
- args = getFileOrder_args()
- args.pid = pid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getFileOrder(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getFileOrder_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getFileOrder failed: unknown result");
-
- def generateAndAddPackages(self, links, dest):
- """
- Parameters:
- - links
- - dest
- """
- self.send_generateAndAddPackages(links, dest)
- return self.recv_generateAndAddPackages()
-
- def send_generateAndAddPackages(self, links, dest):
- self._oprot.writeMessageBegin('generateAndAddPackages', TMessageType.CALL, self._seqid)
- args = generateAndAddPackages_args()
- args.links = links
- args.dest = dest
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_generateAndAddPackages(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = generateAndAddPackages_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "generateAndAddPackages failed: unknown result");
-
- def addPackage(self, name, links, dest):
- """
- Parameters:
- - name
- - links
- - dest
- """
- self.send_addPackage(name, links, dest)
- return self.recv_addPackage()
-
- def send_addPackage(self, name, links, dest):
- self._oprot.writeMessageBegin('addPackage', TMessageType.CALL, self._seqid)
- args = addPackage_args()
- args.name = name
- args.links = links
- args.dest = dest
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_addPackage(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = addPackage_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "addPackage failed: unknown result");
-
- def addFiles(self, pid, links):
- """
- Parameters:
- - pid
- - links
- """
- self.send_addFiles(pid, links)
- self.recv_addFiles()
-
- def send_addFiles(self, pid, links):
- self._oprot.writeMessageBegin('addFiles', TMessageType.CALL, self._seqid)
- args = addFiles_args()
- args.pid = pid
- args.links = links
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_addFiles(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = addFiles_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def uploadContainer(self, filename, data):
- """
- Parameters:
- - filename
- - data
- """
- self.send_uploadContainer(filename, data)
- self.recv_uploadContainer()
-
- def send_uploadContainer(self, filename, data):
- self._oprot.writeMessageBegin('uploadContainer', TMessageType.CALL, self._seqid)
- args = uploadContainer_args()
- args.filename = filename
- args.data = data
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_uploadContainer(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = uploadContainer_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def deleteFiles(self, fids):
- """
- Parameters:
- - fids
- """
- self.send_deleteFiles(fids)
- self.recv_deleteFiles()
-
- def send_deleteFiles(self, fids):
- self._oprot.writeMessageBegin('deleteFiles', TMessageType.CALL, self._seqid)
- args = deleteFiles_args()
- args.fids = fids
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_deleteFiles(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = deleteFiles_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def deletePackages(self, pids):
- """
- Parameters:
- - pids
- """
- self.send_deletePackages(pids)
- self.recv_deletePackages()
-
- def send_deletePackages(self, pids):
- self._oprot.writeMessageBegin('deletePackages', TMessageType.CALL, self._seqid)
- args = deletePackages_args()
- args.pids = pids
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_deletePackages(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = deletePackages_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def pushToQueue(self, pid):
- """
- Parameters:
- - pid
- """
- self.send_pushToQueue(pid)
- self.recv_pushToQueue()
-
- def send_pushToQueue(self, pid):
- self._oprot.writeMessageBegin('pushToQueue', TMessageType.CALL, self._seqid)
- args = pushToQueue_args()
- args.pid = pid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_pushToQueue(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = pushToQueue_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def pullFromQueue(self, pid):
- """
- Parameters:
- - pid
- """
- self.send_pullFromQueue(pid)
- self.recv_pullFromQueue()
-
- def send_pullFromQueue(self, pid):
- self._oprot.writeMessageBegin('pullFromQueue', TMessageType.CALL, self._seqid)
- args = pullFromQueue_args()
- args.pid = pid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_pullFromQueue(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = pullFromQueue_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def restartPackage(self, pid):
- """
- Parameters:
- - pid
- """
- self.send_restartPackage(pid)
- self.recv_restartPackage()
-
- def send_restartPackage(self, pid):
- self._oprot.writeMessageBegin('restartPackage', TMessageType.CALL, self._seqid)
- args = restartPackage_args()
- args.pid = pid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_restartPackage(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = restartPackage_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def restartFile(self, fid):
- """
- Parameters:
- - fid
- """
- self.send_restartFile(fid)
- self.recv_restartFile()
-
- def send_restartFile(self, fid):
- self._oprot.writeMessageBegin('restartFile', TMessageType.CALL, self._seqid)
- args = restartFile_args()
- args.fid = fid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_restartFile(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = restartFile_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def recheckPackage(self, pid):
- """
- Parameters:
- - pid
- """
- self.send_recheckPackage(pid)
- self.recv_recheckPackage()
-
- def send_recheckPackage(self, pid):
- self._oprot.writeMessageBegin('recheckPackage', TMessageType.CALL, self._seqid)
- args = recheckPackage_args()
- args.pid = pid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_recheckPackage(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = recheckPackage_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def stopAllDownloads(self, ):
- self.send_stopAllDownloads()
- self.recv_stopAllDownloads()
-
- def send_stopAllDownloads(self, ):
- self._oprot.writeMessageBegin('stopAllDownloads', TMessageType.CALL, self._seqid)
- args = stopAllDownloads_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_stopAllDownloads(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = stopAllDownloads_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def stopDownloads(self, fids):
- """
- Parameters:
- - fids
- """
- self.send_stopDownloads(fids)
- self.recv_stopDownloads()
-
- def send_stopDownloads(self, fids):
- self._oprot.writeMessageBegin('stopDownloads', TMessageType.CALL, self._seqid)
- args = stopDownloads_args()
- args.fids = fids
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_stopDownloads(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = stopDownloads_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def setPackageName(self, pid, name):
- """
- Parameters:
- - pid
- - name
- """
- self.send_setPackageName(pid, name)
- self.recv_setPackageName()
-
- def send_setPackageName(self, pid, name):
- self._oprot.writeMessageBegin('setPackageName', TMessageType.CALL, self._seqid)
- args = setPackageName_args()
- args.pid = pid
- args.name = name
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_setPackageName(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = setPackageName_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def movePackage(self, destination, pid):
- """
- Parameters:
- - destination
- - pid
- """
- self.send_movePackage(destination, pid)
- self.recv_movePackage()
-
- def send_movePackage(self, destination, pid):
- self._oprot.writeMessageBegin('movePackage', TMessageType.CALL, self._seqid)
- args = movePackage_args()
- args.destination = destination
- args.pid = pid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_movePackage(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = movePackage_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def moveFiles(self, fids, pid):
- """
- Parameters:
- - fids
- - pid
- """
- self.send_moveFiles(fids, pid)
- self.recv_moveFiles()
-
- def send_moveFiles(self, fids, pid):
- self._oprot.writeMessageBegin('moveFiles', TMessageType.CALL, self._seqid)
- args = moveFiles_args()
- args.fids = fids
- args.pid = pid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_moveFiles(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = moveFiles_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def orderPackage(self, pid, position):
- """
- Parameters:
- - pid
- - position
- """
- self.send_orderPackage(pid, position)
- self.recv_orderPackage()
-
- def send_orderPackage(self, pid, position):
- self._oprot.writeMessageBegin('orderPackage', TMessageType.CALL, self._seqid)
- args = orderPackage_args()
- args.pid = pid
- args.position = position
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_orderPackage(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = orderPackage_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def orderFile(self, fid, position):
- """
- Parameters:
- - fid
- - position
- """
- self.send_orderFile(fid, position)
- self.recv_orderFile()
-
- def send_orderFile(self, fid, position):
- self._oprot.writeMessageBegin('orderFile', TMessageType.CALL, self._seqid)
- args = orderFile_args()
- args.fid = fid
- args.position = position
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_orderFile(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = orderFile_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def setPackageData(self, pid, data):
- """
- Parameters:
- - pid
- - data
- """
- self.send_setPackageData(pid, data)
- self.recv_setPackageData()
-
- def send_setPackageData(self, pid, data):
- self._oprot.writeMessageBegin('setPackageData', TMessageType.CALL, self._seqid)
- args = setPackageData_args()
- args.pid = pid
- args.data = data
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_setPackageData(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = setPackageData_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.e is not None:
- raise result.e
- return
-
- def deleteFinished(self, ):
- self.send_deleteFinished()
- return self.recv_deleteFinished()
-
- def send_deleteFinished(self, ):
- self._oprot.writeMessageBegin('deleteFinished', TMessageType.CALL, self._seqid)
- args = deleteFinished_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_deleteFinished(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = deleteFinished_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "deleteFinished failed: unknown result");
-
- def restartFailed(self, ):
- self.send_restartFailed()
- self.recv_restartFailed()
-
- def send_restartFailed(self, ):
- self._oprot.writeMessageBegin('restartFailed', TMessageType.CALL, self._seqid)
- args = restartFailed_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_restartFailed(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = restartFailed_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def getEvents(self, uuid):
- """
- Parameters:
- - uuid
- """
- self.send_getEvents(uuid)
- return self.recv_getEvents()
-
- def send_getEvents(self, uuid):
- self._oprot.writeMessageBegin('getEvents', TMessageType.CALL, self._seqid)
- args = getEvents_args()
- args.uuid = uuid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getEvents(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getEvents_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getEvents failed: unknown result");
-
- def getAccounts(self, refresh):
- """
- Parameters:
- - refresh
- """
- self.send_getAccounts(refresh)
- return self.recv_getAccounts()
-
- def send_getAccounts(self, refresh):
- self._oprot.writeMessageBegin('getAccounts', TMessageType.CALL, self._seqid)
- args = getAccounts_args()
- args.refresh = refresh
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getAccounts(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getAccounts_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getAccounts failed: unknown result");
-
- def getAccountTypes(self, ):
- self.send_getAccountTypes()
- return self.recv_getAccountTypes()
-
- def send_getAccountTypes(self, ):
- self._oprot.writeMessageBegin('getAccountTypes', TMessageType.CALL, self._seqid)
- args = getAccountTypes_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getAccountTypes(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getAccountTypes_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getAccountTypes failed: unknown result");
-
- def updateAccount(self, plugin, account, password, options):
- """
- Parameters:
- - plugin
- - account
- - password
- - options
- """
- self.send_updateAccount(plugin, account, password, options)
- self.recv_updateAccount()
-
- def send_updateAccount(self, plugin, account, password, options):
- self._oprot.writeMessageBegin('updateAccount', TMessageType.CALL, self._seqid)
- args = updateAccount_args()
- args.plugin = plugin
- args.account = account
- args.password = password
- args.options = options
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_updateAccount(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = updateAccount_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def removeAccount(self, plugin, account):
- """
- Parameters:
- - plugin
- - account
- """
- self.send_removeAccount(plugin, account)
- self.recv_removeAccount()
-
- def send_removeAccount(self, plugin, account):
- self._oprot.writeMessageBegin('removeAccount', TMessageType.CALL, self._seqid)
- args = removeAccount_args()
- args.plugin = plugin
- args.account = account
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_removeAccount(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = removeAccount_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
- def login(self, username, password):
- """
- Parameters:
- - username
- - password
- """
- self.send_login(username, password)
- return self.recv_login()
-
- def send_login(self, username, password):
- self._oprot.writeMessageBegin('login', TMessageType.CALL, self._seqid)
- args = login_args()
- args.username = username
- args.password = password
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_login(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = login_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "login failed: unknown result");
-
- def getUserData(self, username, password):
- """
- Parameters:
- - username
- - password
- """
- self.send_getUserData(username, password)
- return self.recv_getUserData()
-
- def send_getUserData(self, username, password):
- self._oprot.writeMessageBegin('getUserData', TMessageType.CALL, self._seqid)
- args = getUserData_args()
- args.username = username
- args.password = password
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getUserData(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getUserData_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getUserData failed: unknown result");
-
- def getAllUserData(self, ):
- self.send_getAllUserData()
- return self.recv_getAllUserData()
-
- def send_getAllUserData(self, ):
- self._oprot.writeMessageBegin('getAllUserData', TMessageType.CALL, self._seqid)
- args = getAllUserData_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getAllUserData(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getAllUserData_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getAllUserData failed: unknown result");
-
- def getServices(self, ):
- self.send_getServices()
- return self.recv_getServices()
-
- def send_getServices(self, ):
- self._oprot.writeMessageBegin('getServices', TMessageType.CALL, self._seqid)
- args = getServices_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getServices(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getServices_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getServices failed: unknown result");
-
- def hasService(self, plugin, func):
- """
- Parameters:
- - plugin
- - func
- """
- self.send_hasService(plugin, func)
- return self.recv_hasService()
-
- def send_hasService(self, plugin, func):
- self._oprot.writeMessageBegin('hasService', TMessageType.CALL, self._seqid)
- args = hasService_args()
- args.plugin = plugin
- args.func = func
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_hasService(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = hasService_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "hasService failed: unknown result");
-
- def call(self, info):
- """
- Parameters:
- - info
- """
- self.send_call(info)
- return self.recv_call()
-
- def send_call(self, info):
- self._oprot.writeMessageBegin('call', TMessageType.CALL, self._seqid)
- args = call_args()
- args.info = info
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_call(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = call_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- if result.ex is not None:
- raise result.ex
- if result.e is not None:
- raise result.e
- raise TApplicationException(TApplicationException.MISSING_RESULT, "call failed: unknown result");
-
- def getAllInfo(self, ):
- self.send_getAllInfo()
- return self.recv_getAllInfo()
-
- def send_getAllInfo(self, ):
- self._oprot.writeMessageBegin('getAllInfo', TMessageType.CALL, self._seqid)
- args = getAllInfo_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getAllInfo(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getAllInfo_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getAllInfo failed: unknown result");
-
- def getInfoByPlugin(self, plugin):
- """
- Parameters:
- - plugin
- """
- self.send_getInfoByPlugin(plugin)
- return self.recv_getInfoByPlugin()
-
- def send_getInfoByPlugin(self, plugin):
- self._oprot.writeMessageBegin('getInfoByPlugin', TMessageType.CALL, self._seqid)
- args = getInfoByPlugin_args()
- args.plugin = plugin
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getInfoByPlugin(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getInfoByPlugin_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getInfoByPlugin failed: unknown result");
-
- def isCaptchaWaiting(self, ):
- self.send_isCaptchaWaiting()
- return self.recv_isCaptchaWaiting()
-
- def send_isCaptchaWaiting(self, ):
- self._oprot.writeMessageBegin('isCaptchaWaiting', TMessageType.CALL, self._seqid)
- args = isCaptchaWaiting_args()
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_isCaptchaWaiting(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = isCaptchaWaiting_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "isCaptchaWaiting failed: unknown result");
-
- def getCaptchaTask(self, exclusive):
- """
- Parameters:
- - exclusive
- """
- self.send_getCaptchaTask(exclusive)
- return self.recv_getCaptchaTask()
-
- def send_getCaptchaTask(self, exclusive):
- self._oprot.writeMessageBegin('getCaptchaTask', TMessageType.CALL, self._seqid)
- args = getCaptchaTask_args()
- args.exclusive = exclusive
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getCaptchaTask(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getCaptchaTask_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getCaptchaTask failed: unknown result");
-
- def getCaptchaTaskStatus(self, tid):
- """
- Parameters:
- - tid
- """
- self.send_getCaptchaTaskStatus(tid)
- return self.recv_getCaptchaTaskStatus()
-
- def send_getCaptchaTaskStatus(self, tid):
- self._oprot.writeMessageBegin('getCaptchaTaskStatus', TMessageType.CALL, self._seqid)
- args = getCaptchaTaskStatus_args()
- args.tid = tid
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_getCaptchaTaskStatus(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = getCaptchaTaskStatus_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- if result.success is not None:
- return result.success
- raise TApplicationException(TApplicationException.MISSING_RESULT, "getCaptchaTaskStatus failed: unknown result");
-
- def setCaptchaResult(self, tid, result):
- """
- Parameters:
- - tid
- - result
- """
- self.send_setCaptchaResult(tid, result)
- self.recv_setCaptchaResult()
-
- def send_setCaptchaResult(self, tid, result):
- self._oprot.writeMessageBegin('setCaptchaResult', TMessageType.CALL, self._seqid)
- args = setCaptchaResult_args()
- args.tid = tid
- args.result = result
- args.write(self._oprot)
- self._oprot.writeMessageEnd()
- self._oprot.trans.flush()
-
- def recv_setCaptchaResult(self, ):
- (fname, mtype, rseqid) = self._iprot.readMessageBegin()
- if mtype == TMessageType.EXCEPTION:
- x = TApplicationException()
- x.read(self._iprot)
- self._iprot.readMessageEnd()
- raise x
- result = setCaptchaResult_result()
- result.read(self._iprot)
- self._iprot.readMessageEnd()
- return
-
-
-class Processor(Iface, TProcessor):
- def __init__(self, handler):
- self._handler = handler
- self._processMap = {}
- self._processMap["getConfigValue"] = Processor.process_getConfigValue
- self._processMap["setConfigValue"] = Processor.process_setConfigValue
- self._processMap["getConfig"] = Processor.process_getConfig
- self._processMap["getPluginConfig"] = Processor.process_getPluginConfig
- self._processMap["pauseServer"] = Processor.process_pauseServer
- self._processMap["unpauseServer"] = Processor.process_unpauseServer
- self._processMap["togglePause"] = Processor.process_togglePause
- self._processMap["statusServer"] = Processor.process_statusServer
- self._processMap["freeSpace"] = Processor.process_freeSpace
- self._processMap["getServerVersion"] = Processor.process_getServerVersion
- self._processMap["kill"] = Processor.process_kill
- self._processMap["restart"] = Processor.process_restart
- self._processMap["getLog"] = Processor.process_getLog
- self._processMap["isTimeDownload"] = Processor.process_isTimeDownload
- self._processMap["isTimeReconnect"] = Processor.process_isTimeReconnect
- self._processMap["toggleReconnect"] = Processor.process_toggleReconnect
- self._processMap["generatePackages"] = Processor.process_generatePackages
- self._processMap["checkURLs"] = Processor.process_checkURLs
- self._processMap["parseURLs"] = Processor.process_parseURLs
- self._processMap["checkOnlineStatus"] = Processor.process_checkOnlineStatus
- self._processMap["checkOnlineStatusContainer"] = Processor.process_checkOnlineStatusContainer
- self._processMap["pollResults"] = Processor.process_pollResults
- self._processMap["statusDownloads"] = Processor.process_statusDownloads
- self._processMap["getPackageData"] = Processor.process_getPackageData
- self._processMap["getPackageInfo"] = Processor.process_getPackageInfo
- self._processMap["getFileData"] = Processor.process_getFileData
- self._processMap["getQueue"] = Processor.process_getQueue
- self._processMap["getCollector"] = Processor.process_getCollector
- self._processMap["getQueueData"] = Processor.process_getQueueData
- self._processMap["getCollectorData"] = Processor.process_getCollectorData
- self._processMap["getPackageOrder"] = Processor.process_getPackageOrder
- self._processMap["getFileOrder"] = Processor.process_getFileOrder
- self._processMap["generateAndAddPackages"] = Processor.process_generateAndAddPackages
- self._processMap["addPackage"] = Processor.process_addPackage
- self._processMap["addFiles"] = Processor.process_addFiles
- self._processMap["uploadContainer"] = Processor.process_uploadContainer
- self._processMap["deleteFiles"] = Processor.process_deleteFiles
- self._processMap["deletePackages"] = Processor.process_deletePackages
- self._processMap["pushToQueue"] = Processor.process_pushToQueue
- self._processMap["pullFromQueue"] = Processor.process_pullFromQueue
- self._processMap["restartPackage"] = Processor.process_restartPackage
- self._processMap["restartFile"] = Processor.process_restartFile
- self._processMap["recheckPackage"] = Processor.process_recheckPackage
- self._processMap["stopAllDownloads"] = Processor.process_stopAllDownloads
- self._processMap["stopDownloads"] = Processor.process_stopDownloads
- self._processMap["setPackageName"] = Processor.process_setPackageName
- self._processMap["movePackage"] = Processor.process_movePackage
- self._processMap["moveFiles"] = Processor.process_moveFiles
- self._processMap["orderPackage"] = Processor.process_orderPackage
- self._processMap["orderFile"] = Processor.process_orderFile
- self._processMap["setPackageData"] = Processor.process_setPackageData
- self._processMap["deleteFinished"] = Processor.process_deleteFinished
- self._processMap["restartFailed"] = Processor.process_restartFailed
- self._processMap["getEvents"] = Processor.process_getEvents
- self._processMap["getAccounts"] = Processor.process_getAccounts
- self._processMap["getAccountTypes"] = Processor.process_getAccountTypes
- self._processMap["updateAccount"] = Processor.process_updateAccount
- self._processMap["removeAccount"] = Processor.process_removeAccount
- self._processMap["login"] = Processor.process_login
- self._processMap["getUserData"] = Processor.process_getUserData
- self._processMap["getAllUserData"] = Processor.process_getAllUserData
- self._processMap["getServices"] = Processor.process_getServices
- self._processMap["hasService"] = Processor.process_hasService
- self._processMap["call"] = Processor.process_call
- self._processMap["getAllInfo"] = Processor.process_getAllInfo
- self._processMap["getInfoByPlugin"] = Processor.process_getInfoByPlugin
- self._processMap["isCaptchaWaiting"] = Processor.process_isCaptchaWaiting
- self._processMap["getCaptchaTask"] = Processor.process_getCaptchaTask
- self._processMap["getCaptchaTaskStatus"] = Processor.process_getCaptchaTaskStatus
- self._processMap["setCaptchaResult"] = Processor.process_setCaptchaResult
-
- def process(self, iprot, oprot):
- (name, type, seqid) = iprot.readMessageBegin()
- if name not in self._processMap:
- iprot.skip(TType.STRUCT)
- iprot.readMessageEnd()
- x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name))
- oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid)
- x.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
- return
- else:
- self._processMap[name](self, seqid, iprot, oprot)
- return True
-
- def process_getConfigValue(self, seqid, iprot, oprot):
- args = getConfigValue_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getConfigValue_result()
- result.success = self._handler.getConfigValue(args.category, args.option, args.section)
- oprot.writeMessageBegin("getConfigValue", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_setConfigValue(self, seqid, iprot, oprot):
- args = setConfigValue_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = setConfigValue_result()
- self._handler.setConfigValue(args.category, args.option, args.value, args.section)
- oprot.writeMessageBegin("setConfigValue", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getConfig(self, seqid, iprot, oprot):
- args = getConfig_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getConfig_result()
- result.success = self._handler.getConfig()
- oprot.writeMessageBegin("getConfig", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getPluginConfig(self, seqid, iprot, oprot):
- args = getPluginConfig_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getPluginConfig_result()
- result.success = self._handler.getPluginConfig()
- oprot.writeMessageBegin("getPluginConfig", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_pauseServer(self, seqid, iprot, oprot):
- args = pauseServer_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = pauseServer_result()
- self._handler.pauseServer()
- oprot.writeMessageBegin("pauseServer", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_unpauseServer(self, seqid, iprot, oprot):
- args = unpauseServer_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = unpauseServer_result()
- self._handler.unpauseServer()
- oprot.writeMessageBegin("unpauseServer", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_togglePause(self, seqid, iprot, oprot):
- args = togglePause_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = togglePause_result()
- result.success = self._handler.togglePause()
- oprot.writeMessageBegin("togglePause", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_statusServer(self, seqid, iprot, oprot):
- args = statusServer_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = statusServer_result()
- result.success = self._handler.statusServer()
- oprot.writeMessageBegin("statusServer", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_freeSpace(self, seqid, iprot, oprot):
- args = freeSpace_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = freeSpace_result()
- result.success = self._handler.freeSpace()
- oprot.writeMessageBegin("freeSpace", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getServerVersion(self, seqid, iprot, oprot):
- args = getServerVersion_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getServerVersion_result()
- result.success = self._handler.getServerVersion()
- oprot.writeMessageBegin("getServerVersion", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_kill(self, seqid, iprot, oprot):
- args = kill_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = kill_result()
- self._handler.kill()
- oprot.writeMessageBegin("kill", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_restart(self, seqid, iprot, oprot):
- args = restart_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = restart_result()
- self._handler.restart()
- oprot.writeMessageBegin("restart", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getLog(self, seqid, iprot, oprot):
- args = getLog_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getLog_result()
- result.success = self._handler.getLog(args.offset)
- oprot.writeMessageBegin("getLog", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_isTimeDownload(self, seqid, iprot, oprot):
- args = isTimeDownload_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = isTimeDownload_result()
- result.success = self._handler.isTimeDownload()
- oprot.writeMessageBegin("isTimeDownload", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_isTimeReconnect(self, seqid, iprot, oprot):
- args = isTimeReconnect_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = isTimeReconnect_result()
- result.success = self._handler.isTimeReconnect()
- oprot.writeMessageBegin("isTimeReconnect", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_toggleReconnect(self, seqid, iprot, oprot):
- args = toggleReconnect_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = toggleReconnect_result()
- result.success = self._handler.toggleReconnect()
- oprot.writeMessageBegin("toggleReconnect", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_generatePackages(self, seqid, iprot, oprot):
- args = generatePackages_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = generatePackages_result()
- result.success = self._handler.generatePackages(args.links)
- oprot.writeMessageBegin("generatePackages", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_checkURLs(self, seqid, iprot, oprot):
- args = checkURLs_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = checkURLs_result()
- result.success = self._handler.checkURLs(args.urls)
- oprot.writeMessageBegin("checkURLs", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_parseURLs(self, seqid, iprot, oprot):
- args = parseURLs_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = parseURLs_result()
- result.success = self._handler.parseURLs(args.html, args.url)
- oprot.writeMessageBegin("parseURLs", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_checkOnlineStatus(self, seqid, iprot, oprot):
- args = checkOnlineStatus_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = checkOnlineStatus_result()
- result.success = self._handler.checkOnlineStatus(args.urls)
- oprot.writeMessageBegin("checkOnlineStatus", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_checkOnlineStatusContainer(self, seqid, iprot, oprot):
- args = checkOnlineStatusContainer_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = checkOnlineStatusContainer_result()
- result.success = self._handler.checkOnlineStatusContainer(args.urls, args.filename, args.data)
- oprot.writeMessageBegin("checkOnlineStatusContainer", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_pollResults(self, seqid, iprot, oprot):
- args = pollResults_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = pollResults_result()
- result.success = self._handler.pollResults(args.rid)
- oprot.writeMessageBegin("pollResults", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_statusDownloads(self, seqid, iprot, oprot):
- args = statusDownloads_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = statusDownloads_result()
- result.success = self._handler.statusDownloads()
- oprot.writeMessageBegin("statusDownloads", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getPackageData(self, seqid, iprot, oprot):
- args = getPackageData_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getPackageData_result()
- try:
- result.success = self._handler.getPackageData(args.pid)
- except PackageDoesNotExists, e:
- result.e = e
- oprot.writeMessageBegin("getPackageData", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getPackageInfo(self, seqid, iprot, oprot):
- args = getPackageInfo_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getPackageInfo_result()
- try:
- result.success = self._handler.getPackageInfo(args.pid)
- except PackageDoesNotExists, e:
- result.e = e
- oprot.writeMessageBegin("getPackageInfo", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getFileData(self, seqid, iprot, oprot):
- args = getFileData_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getFileData_result()
- try:
- result.success = self._handler.getFileData(args.fid)
- except FileDoesNotExists, e:
- result.e = e
- oprot.writeMessageBegin("getFileData", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getQueue(self, seqid, iprot, oprot):
- args = getQueue_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getQueue_result()
- result.success = self._handler.getQueue()
- oprot.writeMessageBegin("getQueue", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getCollector(self, seqid, iprot, oprot):
- args = getCollector_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getCollector_result()
- result.success = self._handler.getCollector()
- oprot.writeMessageBegin("getCollector", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getQueueData(self, seqid, iprot, oprot):
- args = getQueueData_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getQueueData_result()
- result.success = self._handler.getQueueData()
- oprot.writeMessageBegin("getQueueData", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getCollectorData(self, seqid, iprot, oprot):
- args = getCollectorData_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getCollectorData_result()
- result.success = self._handler.getCollectorData()
- oprot.writeMessageBegin("getCollectorData", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getPackageOrder(self, seqid, iprot, oprot):
- args = getPackageOrder_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getPackageOrder_result()
- result.success = self._handler.getPackageOrder(args.destination)
- oprot.writeMessageBegin("getPackageOrder", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getFileOrder(self, seqid, iprot, oprot):
- args = getFileOrder_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getFileOrder_result()
- result.success = self._handler.getFileOrder(args.pid)
- oprot.writeMessageBegin("getFileOrder", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_generateAndAddPackages(self, seqid, iprot, oprot):
- args = generateAndAddPackages_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = generateAndAddPackages_result()
- result.success = self._handler.generateAndAddPackages(args.links, args.dest)
- oprot.writeMessageBegin("generateAndAddPackages", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_addPackage(self, seqid, iprot, oprot):
- args = addPackage_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = addPackage_result()
- result.success = self._handler.addPackage(args.name, args.links, args.dest)
- oprot.writeMessageBegin("addPackage", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_addFiles(self, seqid, iprot, oprot):
- args = addFiles_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = addFiles_result()
- self._handler.addFiles(args.pid, args.links)
- oprot.writeMessageBegin("addFiles", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_uploadContainer(self, seqid, iprot, oprot):
- args = uploadContainer_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = uploadContainer_result()
- self._handler.uploadContainer(args.filename, args.data)
- oprot.writeMessageBegin("uploadContainer", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_deleteFiles(self, seqid, iprot, oprot):
- args = deleteFiles_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = deleteFiles_result()
- self._handler.deleteFiles(args.fids)
- oprot.writeMessageBegin("deleteFiles", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_deletePackages(self, seqid, iprot, oprot):
- args = deletePackages_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = deletePackages_result()
- self._handler.deletePackages(args.pids)
- oprot.writeMessageBegin("deletePackages", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_pushToQueue(self, seqid, iprot, oprot):
- args = pushToQueue_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = pushToQueue_result()
- self._handler.pushToQueue(args.pid)
- oprot.writeMessageBegin("pushToQueue", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_pullFromQueue(self, seqid, iprot, oprot):
- args = pullFromQueue_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = pullFromQueue_result()
- self._handler.pullFromQueue(args.pid)
- oprot.writeMessageBegin("pullFromQueue", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_restartPackage(self, seqid, iprot, oprot):
- args = restartPackage_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = restartPackage_result()
- self._handler.restartPackage(args.pid)
- oprot.writeMessageBegin("restartPackage", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_restartFile(self, seqid, iprot, oprot):
- args = restartFile_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = restartFile_result()
- self._handler.restartFile(args.fid)
- oprot.writeMessageBegin("restartFile", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_recheckPackage(self, seqid, iprot, oprot):
- args = recheckPackage_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = recheckPackage_result()
- self._handler.recheckPackage(args.pid)
- oprot.writeMessageBegin("recheckPackage", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_stopAllDownloads(self, seqid, iprot, oprot):
- args = stopAllDownloads_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = stopAllDownloads_result()
- self._handler.stopAllDownloads()
- oprot.writeMessageBegin("stopAllDownloads", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_stopDownloads(self, seqid, iprot, oprot):
- args = stopDownloads_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = stopDownloads_result()
- self._handler.stopDownloads(args.fids)
- oprot.writeMessageBegin("stopDownloads", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_setPackageName(self, seqid, iprot, oprot):
- args = setPackageName_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = setPackageName_result()
- self._handler.setPackageName(args.pid, args.name)
- oprot.writeMessageBegin("setPackageName", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_movePackage(self, seqid, iprot, oprot):
- args = movePackage_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = movePackage_result()
- self._handler.movePackage(args.destination, args.pid)
- oprot.writeMessageBegin("movePackage", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_moveFiles(self, seqid, iprot, oprot):
- args = moveFiles_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = moveFiles_result()
- self._handler.moveFiles(args.fids, args.pid)
- oprot.writeMessageBegin("moveFiles", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_orderPackage(self, seqid, iprot, oprot):
- args = orderPackage_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = orderPackage_result()
- self._handler.orderPackage(args.pid, args.position)
- oprot.writeMessageBegin("orderPackage", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_orderFile(self, seqid, iprot, oprot):
- args = orderFile_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = orderFile_result()
- self._handler.orderFile(args.fid, args.position)
- oprot.writeMessageBegin("orderFile", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_setPackageData(self, seqid, iprot, oprot):
- args = setPackageData_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = setPackageData_result()
- try:
- self._handler.setPackageData(args.pid, args.data)
- except PackageDoesNotExists, e:
- result.e = e
- oprot.writeMessageBegin("setPackageData", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_deleteFinished(self, seqid, iprot, oprot):
- args = deleteFinished_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = deleteFinished_result()
- result.success = self._handler.deleteFinished()
- oprot.writeMessageBegin("deleteFinished", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_restartFailed(self, seqid, iprot, oprot):
- args = restartFailed_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = restartFailed_result()
- self._handler.restartFailed()
- oprot.writeMessageBegin("restartFailed", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getEvents(self, seqid, iprot, oprot):
- args = getEvents_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getEvents_result()
- result.success = self._handler.getEvents(args.uuid)
- oprot.writeMessageBegin("getEvents", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getAccounts(self, seqid, iprot, oprot):
- args = getAccounts_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getAccounts_result()
- result.success = self._handler.getAccounts(args.refresh)
- oprot.writeMessageBegin("getAccounts", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getAccountTypes(self, seqid, iprot, oprot):
- args = getAccountTypes_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getAccountTypes_result()
- result.success = self._handler.getAccountTypes()
- oprot.writeMessageBegin("getAccountTypes", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_updateAccount(self, seqid, iprot, oprot):
- args = updateAccount_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = updateAccount_result()
- self._handler.updateAccount(args.plugin, args.account, args.password, args.options)
- oprot.writeMessageBegin("updateAccount", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_removeAccount(self, seqid, iprot, oprot):
- args = removeAccount_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = removeAccount_result()
- self._handler.removeAccount(args.plugin, args.account)
- oprot.writeMessageBegin("removeAccount", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_login(self, seqid, iprot, oprot):
- args = login_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = login_result()
- result.success = self._handler.login(args.username, args.password)
- oprot.writeMessageBegin("login", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getUserData(self, seqid, iprot, oprot):
- args = getUserData_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getUserData_result()
- result.success = self._handler.getUserData(args.username, args.password)
- oprot.writeMessageBegin("getUserData", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getAllUserData(self, seqid, iprot, oprot):
- args = getAllUserData_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getAllUserData_result()
- result.success = self._handler.getAllUserData()
- oprot.writeMessageBegin("getAllUserData", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getServices(self, seqid, iprot, oprot):
- args = getServices_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getServices_result()
- result.success = self._handler.getServices()
- oprot.writeMessageBegin("getServices", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_hasService(self, seqid, iprot, oprot):
- args = hasService_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = hasService_result()
- result.success = self._handler.hasService(args.plugin, args.func)
- oprot.writeMessageBegin("hasService", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_call(self, seqid, iprot, oprot):
- args = call_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = call_result()
- try:
- result.success = self._handler.call(args.info)
- except ServiceDoesNotExists, ex:
- result.ex = ex
- except ServiceException, e:
- result.e = e
- oprot.writeMessageBegin("call", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getAllInfo(self, seqid, iprot, oprot):
- args = getAllInfo_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getAllInfo_result()
- result.success = self._handler.getAllInfo()
- oprot.writeMessageBegin("getAllInfo", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getInfoByPlugin(self, seqid, iprot, oprot):
- args = getInfoByPlugin_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getInfoByPlugin_result()
- result.success = self._handler.getInfoByPlugin(args.plugin)
- oprot.writeMessageBegin("getInfoByPlugin", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_isCaptchaWaiting(self, seqid, iprot, oprot):
- args = isCaptchaWaiting_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = isCaptchaWaiting_result()
- result.success = self._handler.isCaptchaWaiting()
- oprot.writeMessageBegin("isCaptchaWaiting", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getCaptchaTask(self, seqid, iprot, oprot):
- args = getCaptchaTask_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getCaptchaTask_result()
- result.success = self._handler.getCaptchaTask(args.exclusive)
- oprot.writeMessageBegin("getCaptchaTask", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_getCaptchaTaskStatus(self, seqid, iprot, oprot):
- args = getCaptchaTaskStatus_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = getCaptchaTaskStatus_result()
- result.success = self._handler.getCaptchaTaskStatus(args.tid)
- oprot.writeMessageBegin("getCaptchaTaskStatus", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
- def process_setCaptchaResult(self, seqid, iprot, oprot):
- args = setCaptchaResult_args()
- args.read(iprot)
- iprot.readMessageEnd()
- result = setCaptchaResult_result()
- self._handler.setCaptchaResult(args.tid, args.result)
- oprot.writeMessageBegin("setCaptchaResult", TMessageType.REPLY, seqid)
- result.write(oprot)
- oprot.writeMessageEnd()
- oprot.trans.flush()
-
-
-# HELPER FUNCTIONS AND STRUCTURES
-
-class getConfigValue_args(TBase):
- """
- Attributes:
- - category
- - option
- - section
- """
-
- __slots__ = [
- 'category',
- 'option',
- 'section',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'category', None, None, ), # 1
- (2, TType.STRING, 'option', None, None, ), # 2
- (3, TType.STRING, 'section', None, None, ), # 3
- )
-
- def __init__(self, category=None, option=None, section=None,):
- self.category = category
- self.option = option
- self.section = section
-
-
-class getConfigValue_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.STRING, 'success', None, None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class setConfigValue_args(TBase):
- """
- Attributes:
- - category
- - option
- - value
- - section
- """
-
- __slots__ = [
- 'category',
- 'option',
- 'value',
- 'section',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'category', None, None, ), # 1
- (2, TType.STRING, 'option', None, None, ), # 2
- (3, TType.STRING, 'value', None, None, ), # 3
- (4, TType.STRING, 'section', None, None, ), # 4
- )
-
- def __init__(self, category=None, option=None, value=None, section=None,):
- self.category = category
- self.option = option
- self.value = value
- self.section = section
-
-
-class setConfigValue_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getConfig_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getConfig_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.STRUCT,(ConfigSection, ConfigSection.thrift_spec)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getPluginConfig_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getPluginConfig_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.STRUCT,(ConfigSection, ConfigSection.thrift_spec)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class pauseServer_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class pauseServer_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class unpauseServer_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class unpauseServer_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class togglePause_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class togglePause_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class statusServer_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class statusServer_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.STRUCT, 'success', (ServerStatus, ServerStatus.thrift_spec), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class freeSpace_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class freeSpace_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.I64, 'success', None, None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getServerVersion_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getServerVersion_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.STRING, 'success', None, None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class kill_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class kill_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class restart_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class restart_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getLog_args(TBase):
- """
- Attributes:
- - offset
- """
-
- __slots__ = [
- 'offset',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'offset', None, None, ), # 1
- )
-
- def __init__(self, offset=None,):
- self.offset = offset
-
-
-class getLog_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRING,None), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class isTimeDownload_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class isTimeDownload_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class isTimeReconnect_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class isTimeReconnect_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class toggleReconnect_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class toggleReconnect_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class generatePackages_args(TBase):
- """
- Attributes:
- - links
- """
-
- __slots__ = [
- 'links',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.LIST, 'links', (TType.STRING,None), None, ), # 1
- )
-
- def __init__(self, links=None,):
- self.links = links
-
-
-class generatePackages_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.LIST,(TType.STRING,None)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class checkURLs_args(TBase):
- """
- Attributes:
- - urls
- """
-
- __slots__ = [
- 'urls',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.LIST, 'urls', (TType.STRING,None), None, ), # 1
- )
-
- def __init__(self, urls=None,):
- self.urls = urls
-
-
-class checkURLs_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.LIST,(TType.STRING,None)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class parseURLs_args(TBase):
- """
- Attributes:
- - html
- - url
- """
-
- __slots__ = [
- 'html',
- 'url',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'html', None, None, ), # 1
- (2, TType.STRING, 'url', None, None, ), # 2
- )
-
- def __init__(self, html=None, url=None,):
- self.html = html
- self.url = url
-
-
-class parseURLs_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.LIST,(TType.STRING,None)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class checkOnlineStatus_args(TBase):
- """
- Attributes:
- - urls
- """
-
- __slots__ = [
- 'urls',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.LIST, 'urls', (TType.STRING,None), None, ), # 1
- )
-
- def __init__(self, urls=None,):
- self.urls = urls
-
-
-class checkOnlineStatus_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.STRUCT, 'success', (OnlineCheck, OnlineCheck.thrift_spec), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class checkOnlineStatusContainer_args(TBase):
- """
- Attributes:
- - urls
- - filename
- - data
- """
-
- __slots__ = [
- 'urls',
- 'filename',
- 'data',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.LIST, 'urls', (TType.STRING,None), None, ), # 1
- (2, TType.STRING, 'filename', None, None, ), # 2
- (3, TType.STRING, 'data', None, None, ), # 3
- )
-
- def __init__(self, urls=None, filename=None, data=None,):
- self.urls = urls
- self.filename = filename
- self.data = data
-
-
-class checkOnlineStatusContainer_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.STRUCT, 'success', (OnlineCheck, OnlineCheck.thrift_spec), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class pollResults_args(TBase):
- """
- Attributes:
- - rid
- """
-
- __slots__ = [
- 'rid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'rid', None, None, ), # 1
- )
-
- def __init__(self, rid=None,):
- self.rid = rid
-
-
-class pollResults_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.STRUCT, 'success', (OnlineCheck, OnlineCheck.thrift_spec), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class statusDownloads_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class statusDownloads_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(DownloadInfo, DownloadInfo.thrift_spec)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getPackageData_args(TBase):
- """
- Attributes:
- - pid
- """
-
- __slots__ = [
- 'pid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- )
-
- def __init__(self, pid=None,):
- self.pid = pid
-
-
-class getPackageData_result(TBase):
- """
- Attributes:
- - success
- - e
- """
-
- __slots__ = [
- 'success',
- 'e',
- ]
-
- thrift_spec = (
- (0, TType.STRUCT, 'success', (PackageData, PackageData.thrift_spec), None, ), # 0
- (1, TType.STRUCT, 'e', (PackageDoesNotExists, PackageDoesNotExists.thrift_spec), None, ), # 1
- )
-
- def __init__(self, success=None, e=None,):
- self.success = success
- self.e = e
-
-
-class getPackageInfo_args(TBase):
- """
- Attributes:
- - pid
- """
-
- __slots__ = [
- 'pid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- )
-
- def __init__(self, pid=None,):
- self.pid = pid
-
-
-class getPackageInfo_result(TBase):
- """
- Attributes:
- - success
- - e
- """
-
- __slots__ = [
- 'success',
- 'e',
- ]
-
- thrift_spec = (
- (0, TType.STRUCT, 'success', (PackageData, PackageData.thrift_spec), None, ), # 0
- (1, TType.STRUCT, 'e', (PackageDoesNotExists, PackageDoesNotExists.thrift_spec), None, ), # 1
- )
-
- def __init__(self, success=None, e=None,):
- self.success = success
- self.e = e
-
-
-class getFileData_args(TBase):
- """
- Attributes:
- - fid
- """
-
- __slots__ = [
- 'fid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'fid', None, None, ), # 1
- )
-
- def __init__(self, fid=None,):
- self.fid = fid
-
-
-class getFileData_result(TBase):
- """
- Attributes:
- - success
- - e
- """
-
- __slots__ = [
- 'success',
- 'e',
- ]
-
- thrift_spec = (
- (0, TType.STRUCT, 'success', (FileData, FileData.thrift_spec), None, ), # 0
- (1, TType.STRUCT, 'e', (FileDoesNotExists, FileDoesNotExists.thrift_spec), None, ), # 1
- )
-
- def __init__(self, success=None, e=None,):
- self.success = success
- self.e = e
-
-
-class getQueue_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getQueue_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(PackageData, PackageData.thrift_spec)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getCollector_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getCollector_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(PackageData, PackageData.thrift_spec)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getQueueData_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getQueueData_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(PackageData, PackageData.thrift_spec)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getCollectorData_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getCollectorData_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(PackageData, PackageData.thrift_spec)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getPackageOrder_args(TBase):
- """
- Attributes:
- - destination
- """
-
- __slots__ = [
- 'destination',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'destination', None, None, ), # 1
- )
-
- def __init__(self, destination=None,):
- self.destination = destination
-
-
-class getPackageOrder_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.MAP, 'success', (TType.I16,None,TType.I32,None), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getFileOrder_args(TBase):
- """
- Attributes:
- - pid
- """
-
- __slots__ = [
- 'pid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- )
-
- def __init__(self, pid=None,):
- self.pid = pid
-
-
-class getFileOrder_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.MAP, 'success', (TType.I16,None,TType.I32,None), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class generateAndAddPackages_args(TBase):
- """
- Attributes:
- - links
- - dest
- """
-
- __slots__ = [
- 'links',
- 'dest',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.LIST, 'links', (TType.STRING,None), None, ), # 1
- (2, TType.I32, 'dest', None, None, ), # 2
- )
-
- def __init__(self, links=None, dest=None,):
- self.links = links
- self.dest = dest
-
-
-class generateAndAddPackages_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.LIST, 'success', (TType.I32,None), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class addPackage_args(TBase):
- """
- Attributes:
- - name
- - links
- - dest
- """
-
- __slots__ = [
- 'name',
- 'links',
- 'dest',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'name', None, None, ), # 1
- (2, TType.LIST, 'links', (TType.STRING,None), None, ), # 2
- (3, TType.I32, 'dest', None, None, ), # 3
- )
-
- def __init__(self, name=None, links=None, dest=None,):
- self.name = name
- self.links = links
- self.dest = dest
-
-
-class addPackage_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.I32, 'success', None, None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class addFiles_args(TBase):
- """
- Attributes:
- - pid
- - links
- """
-
- __slots__ = [
- 'pid',
- 'links',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- (2, TType.LIST, 'links', (TType.STRING,None), None, ), # 2
- )
-
- def __init__(self, pid=None, links=None,):
- self.pid = pid
- self.links = links
-
-
-class addFiles_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class uploadContainer_args(TBase):
- """
- Attributes:
- - filename
- - data
- """
-
- __slots__ = [
- 'filename',
- 'data',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'filename', None, None, ), # 1
- (2, TType.STRING, 'data', None, None, ), # 2
- )
-
- def __init__(self, filename=None, data=None,):
- self.filename = filename
- self.data = data
-
-
-class uploadContainer_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class deleteFiles_args(TBase):
- """
- Attributes:
- - fids
- """
-
- __slots__ = [
- 'fids',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.LIST, 'fids', (TType.I32,None), None, ), # 1
- )
-
- def __init__(self, fids=None,):
- self.fids = fids
-
-
-class deleteFiles_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class deletePackages_args(TBase):
- """
- Attributes:
- - pids
- """
-
- __slots__ = [
- 'pids',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.LIST, 'pids', (TType.I32,None), None, ), # 1
- )
-
- def __init__(self, pids=None,):
- self.pids = pids
-
-
-class deletePackages_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class pushToQueue_args(TBase):
- """
- Attributes:
- - pid
- """
-
- __slots__ = [
- 'pid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- )
-
- def __init__(self, pid=None,):
- self.pid = pid
-
-
-class pushToQueue_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class pullFromQueue_args(TBase):
- """
- Attributes:
- - pid
- """
-
- __slots__ = [
- 'pid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- )
-
- def __init__(self, pid=None,):
- self.pid = pid
-
-
-class pullFromQueue_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class restartPackage_args(TBase):
- """
- Attributes:
- - pid
- """
-
- __slots__ = [
- 'pid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- )
-
- def __init__(self, pid=None,):
- self.pid = pid
-
-
-class restartPackage_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class restartFile_args(TBase):
- """
- Attributes:
- - fid
- """
-
- __slots__ = [
- 'fid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'fid', None, None, ), # 1
- )
-
- def __init__(self, fid=None,):
- self.fid = fid
-
-
-class restartFile_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class recheckPackage_args(TBase):
- """
- Attributes:
- - pid
- """
-
- __slots__ = [
- 'pid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- )
-
- def __init__(self, pid=None,):
- self.pid = pid
-
-
-class recheckPackage_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class stopAllDownloads_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class stopAllDownloads_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class stopDownloads_args(TBase):
- """
- Attributes:
- - fids
- """
-
- __slots__ = [
- 'fids',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.LIST, 'fids', (TType.I32,None), None, ), # 1
- )
-
- def __init__(self, fids=None,):
- self.fids = fids
-
-
-class stopDownloads_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class setPackageName_args(TBase):
- """
- Attributes:
- - pid
- - name
- """
-
- __slots__ = [
- 'pid',
- 'name',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- (2, TType.STRING, 'name', None, None, ), # 2
- )
-
- def __init__(self, pid=None, name=None,):
- self.pid = pid
- self.name = name
-
-
-class setPackageName_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class movePackage_args(TBase):
- """
- Attributes:
- - destination
- - pid
- """
-
- __slots__ = [
- 'destination',
- 'pid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'destination', None, None, ), # 1
- (2, TType.I32, 'pid', None, None, ), # 2
- )
-
- def __init__(self, destination=None, pid=None,):
- self.destination = destination
- self.pid = pid
-
-
-class movePackage_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class moveFiles_args(TBase):
- """
- Attributes:
- - fids
- - pid
- """
-
- __slots__ = [
- 'fids',
- 'pid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.LIST, 'fids', (TType.I32,None), None, ), # 1
- (2, TType.I32, 'pid', None, None, ), # 2
- )
-
- def __init__(self, fids=None, pid=None,):
- self.fids = fids
- self.pid = pid
-
-
-class moveFiles_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class orderPackage_args(TBase):
- """
- Attributes:
- - pid
- - position
- """
-
- __slots__ = [
- 'pid',
- 'position',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- (2, TType.I16, 'position', None, None, ), # 2
- )
-
- def __init__(self, pid=None, position=None,):
- self.pid = pid
- self.position = position
-
-
-class orderPackage_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class orderFile_args(TBase):
- """
- Attributes:
- - fid
- - position
- """
-
- __slots__ = [
- 'fid',
- 'position',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'fid', None, None, ), # 1
- (2, TType.I16, 'position', None, None, ), # 2
- )
-
- def __init__(self, fid=None, position=None,):
- self.fid = fid
- self.position = position
-
-
-class orderFile_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class setPackageData_args(TBase):
- """
- Attributes:
- - pid
- - data
- """
-
- __slots__ = [
- 'pid',
- 'data',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- (2, TType.MAP, 'data', (TType.STRING,None,TType.STRING,None), None, ), # 2
- )
-
- def __init__(self, pid=None, data=None,):
- self.pid = pid
- self.data = data
-
-
-class setPackageData_result(TBase):
- """
- Attributes:
- - e
- """
-
- __slots__ = [
- 'e',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRUCT, 'e', (PackageDoesNotExists, PackageDoesNotExists.thrift_spec), None, ), # 1
- )
-
- def __init__(self, e=None,):
- self.e = e
-
-
-class deleteFinished_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class deleteFinished_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.LIST, 'success', (TType.I32,None), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class restartFailed_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class restartFailed_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getEvents_args(TBase):
- """
- Attributes:
- - uuid
- """
-
- __slots__ = [
- 'uuid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'uuid', None, None, ), # 1
- )
-
- def __init__(self, uuid=None,):
- self.uuid = uuid
-
-
-class getEvents_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(EventInfo, EventInfo.thrift_spec)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getAccounts_args(TBase):
- """
- Attributes:
- - refresh
- """
-
- __slots__ = [
- 'refresh',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.BOOL, 'refresh', None, None, ), # 1
- )
-
- def __init__(self, refresh=None,):
- self.refresh = refresh
-
-
-class getAccounts_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRUCT,(AccountInfo, AccountInfo.thrift_spec)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getAccountTypes_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getAccountTypes_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.LIST, 'success', (TType.STRING,None), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class updateAccount_args(TBase):
- """
- Attributes:
- - plugin
- - account
- - password
- - options
- """
-
- __slots__ = [
- 'plugin',
- 'account',
- 'password',
- 'options',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'plugin', None, None, ), # 1
- (2, TType.STRING, 'account', None, None, ), # 2
- (3, TType.STRING, 'password', None, None, ), # 3
- (4, TType.MAP, 'options', (TType.STRING,None,TType.STRING,None), None, ), # 4
- )
-
- def __init__(self, plugin=None, account=None, password=None, options=None,):
- self.plugin = plugin
- self.account = account
- self.password = password
- self.options = options
-
-
-class updateAccount_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class removeAccount_args(TBase):
- """
- Attributes:
- - plugin
- - account
- """
-
- __slots__ = [
- 'plugin',
- 'account',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'plugin', None, None, ), # 1
- (2, TType.STRING, 'account', None, None, ), # 2
- )
-
- def __init__(self, plugin=None, account=None,):
- self.plugin = plugin
- self.account = account
-
-
-class removeAccount_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class login_args(TBase):
- """
- Attributes:
- - username
- - password
- """
-
- __slots__ = [
- 'username',
- 'password',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'username', None, None, ), # 1
- (2, TType.STRING, 'password', None, None, ), # 2
- )
-
- def __init__(self, username=None, password=None,):
- self.username = username
- self.password = password
-
-
-class login_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getUserData_args(TBase):
- """
- Attributes:
- - username
- - password
- """
-
- __slots__ = [
- 'username',
- 'password',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'username', None, None, ), # 1
- (2, TType.STRING, 'password', None, None, ), # 2
- )
-
- def __init__(self, username=None, password=None,):
- self.username = username
- self.password = password
-
-
-class getUserData_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.STRUCT, 'success', (UserData, UserData.thrift_spec), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getAllUserData_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getAllUserData_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.STRUCT,(UserData, UserData.thrift_spec)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getServices_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getServices_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.MAP,(TType.STRING,None,TType.STRING,None)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class hasService_args(TBase):
- """
- Attributes:
- - plugin
- - func
- """
-
- __slots__ = [
- 'plugin',
- 'func',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'plugin', None, None, ), # 1
- (2, TType.STRING, 'func', None, None, ), # 2
- )
-
- def __init__(self, plugin=None, func=None,):
- self.plugin = plugin
- self.func = func
-
-
-class hasService_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class call_args(TBase):
- """
- Attributes:
- - info
- """
-
- __slots__ = [
- 'info',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRUCT, 'info', (ServiceCall, ServiceCall.thrift_spec), None, ), # 1
- )
-
- def __init__(self, info=None,):
- self.info = info
-
-
-class call_result(TBase):
- """
- Attributes:
- - success
- - ex
- - e
- """
-
- __slots__ = [
- 'success',
- 'ex',
- 'e',
- ]
-
- thrift_spec = (
- (0, TType.STRING, 'success', None, None, ), # 0
- (1, TType.STRUCT, 'ex', (ServiceDoesNotExists, ServiceDoesNotExists.thrift_spec), None, ), # 1
- (2, TType.STRUCT, 'e', (ServiceException, ServiceException.thrift_spec), None, ), # 2
- )
-
- def __init__(self, success=None, ex=None, e=None,):
- self.success = success
- self.ex = ex
- self.e = e
-
-
-class getAllInfo_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class getAllInfo_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.MAP,(TType.STRING,None,TType.STRING,None)), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getInfoByPlugin_args(TBase):
- """
- Attributes:
- - plugin
- """
-
- __slots__ = [
- 'plugin',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'plugin', None, None, ), # 1
- )
-
- def __init__(self, plugin=None,):
- self.plugin = plugin
-
-
-class getInfoByPlugin_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.MAP, 'success', (TType.STRING,None,TType.STRING,None), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class isCaptchaWaiting_args(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
-
-class isCaptchaWaiting_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.BOOL, 'success', None, None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getCaptchaTask_args(TBase):
- """
- Attributes:
- - exclusive
- """
-
- __slots__ = [
- 'exclusive',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.BOOL, 'exclusive', None, None, ), # 1
- )
-
- def __init__(self, exclusive=None,):
- self.exclusive = exclusive
-
-
-class getCaptchaTask_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.STRUCT, 'success', (CaptchaTask, CaptchaTask.thrift_spec), None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class getCaptchaTaskStatus_args(TBase):
- """
- Attributes:
- - tid
- """
-
- __slots__ = [
- 'tid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'tid', None, None, ), # 1
- )
-
- def __init__(self, tid=None,):
- self.tid = tid
-
-
-class getCaptchaTaskStatus_result(TBase):
- """
- Attributes:
- - success
- """
-
- __slots__ = [
- 'success',
- ]
-
- thrift_spec = (
- (0, TType.STRING, 'success', None, None, ), # 0
- )
-
- def __init__(self, success=None,):
- self.success = success
-
-
-class setCaptchaResult_args(TBase):
- """
- Attributes:
- - tid
- - result
- """
-
- __slots__ = [
- 'tid',
- 'result',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'tid', None, None, ), # 1
- (2, TType.STRING, 'result', None, None, ), # 2
- )
-
- def __init__(self, tid=None, result=None,):
- self.tid = tid
- self.result = result
-
-
-class setCaptchaResult_result(TBase):
-
- __slots__ = [
- ]
-
- thrift_spec = (
- )
-
diff --git a/module/remote/thriftbackend/thriftgen/pyload/__init__.py b/module/remote/thriftbackend/thriftgen/pyload/__init__.py
deleted file mode 100644
index ce7f52598..000000000
--- a/module/remote/thriftbackend/thriftgen/pyload/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__all__ = ['ttypes', 'constants', 'Pyload']
diff --git a/module/remote/thriftbackend/thriftgen/pyload/constants.py b/module/remote/thriftbackend/thriftgen/pyload/constants.py
deleted file mode 100644
index f8960dc63..000000000
--- a/module/remote/thriftbackend/thriftgen/pyload/constants.py
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# Autogenerated by Thrift Compiler (0.9.0-dev)
-#
-# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
-#
-# options string: py:slots,dynamic
-#
-
-from thrift.Thrift import TType, TMessageType, TException
-from ttypes import *
-
diff --git a/module/remote/thriftbackend/thriftgen/pyload/ttypes.py b/module/remote/thriftbackend/thriftgen/pyload/ttypes.py
deleted file mode 100644
index 1299b515d..000000000
--- a/module/remote/thriftbackend/thriftgen/pyload/ttypes.py
+++ /dev/null
@@ -1,835 +0,0 @@
-#
-# Autogenerated by Thrift Compiler (0.9.0-dev)
-#
-# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
-#
-# options string: py:slots,dynamic
-#
-
-from thrift.Thrift import TType, TMessageType, TException
-
-from thrift.protocol.TBase import TBase, TExceptionBase
-
-
-class DownloadStatus(TBase):
- Finished = 0
- Offline = 1
- Online = 2
- Queued = 3
- Skipped = 4
- Waiting = 5
- TempOffline = 6
- Starting = 7
- Failed = 8
- Aborted = 9
- Decrypting = 10
- Custom = 11
- Downloading = 12
- Processing = 13
- Unknown = 14
-
- _VALUES_TO_NAMES = {
- 0: "Finished",
- 1: "Offline",
- 2: "Online",
- 3: "Queued",
- 4: "Skipped",
- 5: "Waiting",
- 6: "TempOffline",
- 7: "Starting",
- 8: "Failed",
- 9: "Aborted",
- 10: "Decrypting",
- 11: "Custom",
- 12: "Downloading",
- 13: "Processing",
- 14: "Unknown",
- }
-
- _NAMES_TO_VALUES = {
- "Finished": 0,
- "Offline": 1,
- "Online": 2,
- "Queued": 3,
- "Skipped": 4,
- "Waiting": 5,
- "TempOffline": 6,
- "Starting": 7,
- "Failed": 8,
- "Aborted": 9,
- "Decrypting": 10,
- "Custom": 11,
- "Downloading": 12,
- "Processing": 13,
- "Unknown": 14,
- }
-
-class Destination(TBase):
- Collector = 0
- Queue = 1
-
- _VALUES_TO_NAMES = {
- 0: "Collector",
- 1: "Queue",
- }
-
- _NAMES_TO_VALUES = {
- "Collector": 0,
- "Queue": 1,
- }
-
-class ElementType(TBase):
- Package = 0
- File = 1
-
- _VALUES_TO_NAMES = {
- 0: "Package",
- 1: "File",
- }
-
- _NAMES_TO_VALUES = {
- "Package": 0,
- "File": 1,
- }
-
-class Input(TBase):
- NONE = 0
- TEXT = 1
- TEXTBOX = 2
- PASSWORD = 3
- BOOL = 4
- CLICK = 5
- CHOICE = 6
- MULTIPLE = 7
- LIST = 8
- TABLE = 9
-
- _VALUES_TO_NAMES = {
- 0: "NONE",
- 1: "TEXT",
- 2: "TEXTBOX",
- 3: "PASSWORD",
- 4: "BOOL",
- 5: "CLICK",
- 6: "CHOICE",
- 7: "MULTIPLE",
- 8: "LIST",
- 9: "TABLE",
- }
-
- _NAMES_TO_VALUES = {
- "NONE": 0,
- "TEXT": 1,
- "TEXTBOX": 2,
- "PASSWORD": 3,
- "BOOL": 4,
- "CLICK": 5,
- "CHOICE": 6,
- "MULTIPLE": 7,
- "LIST": 8,
- "TABLE": 9,
- }
-
-class Output(TBase):
- CAPTCHA = 1
- QUESTION = 2
- NOTIFICATION = 4
-
- _VALUES_TO_NAMES = {
- 1: "CAPTCHA",
- 2: "QUESTION",
- 4: "NOTIFICATION",
- }
-
- _NAMES_TO_VALUES = {
- "CAPTCHA": 1,
- "QUESTION": 2,
- "NOTIFICATION": 4,
- }
-
-
-class DownloadInfo(TBase):
- """
- Attributes:
- - fid
- - name
- - speed
- - eta
- - format_eta
- - bleft
- - size
- - format_size
- - percent
- - status
- - statusmsg
- - format_wait
- - wait_until
- - packageID
- - packageName
- - plugin
- """
-
- __slots__ = [
- 'fid',
- 'name',
- 'speed',
- 'eta',
- 'format_eta',
- 'bleft',
- 'size',
- 'format_size',
- 'percent',
- 'status',
- 'statusmsg',
- 'format_wait',
- 'wait_until',
- 'packageID',
- 'packageName',
- 'plugin',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'fid', None, None, ), # 1
- (2, TType.STRING, 'name', None, None, ), # 2
- (3, TType.I64, 'speed', None, None, ), # 3
- (4, TType.I32, 'eta', None, None, ), # 4
- (5, TType.STRING, 'format_eta', None, None, ), # 5
- (6, TType.I64, 'bleft', None, None, ), # 6
- (7, TType.I64, 'size', None, None, ), # 7
- (8, TType.STRING, 'format_size', None, None, ), # 8
- (9, TType.BYTE, 'percent', None, None, ), # 9
- (10, TType.I32, 'status', None, None, ), # 10
- (11, TType.STRING, 'statusmsg', None, None, ), # 11
- (12, TType.STRING, 'format_wait', None, None, ), # 12
- (13, TType.I64, 'wait_until', None, None, ), # 13
- (14, TType.I32, 'packageID', None, None, ), # 14
- (15, TType.STRING, 'packageName', None, None, ), # 15
- (16, TType.STRING, 'plugin', None, None, ), # 16
- )
-
- def __init__(self, fid=None, name=None, speed=None, eta=None, format_eta=None, bleft=None, size=None, format_size=None, percent=None, status=None, statusmsg=None, format_wait=None, wait_until=None, packageID=None, packageName=None, plugin=None,):
- self.fid = fid
- self.name = name
- self.speed = speed
- self.eta = eta
- self.format_eta = format_eta
- self.bleft = bleft
- self.size = size
- self.format_size = format_size
- self.percent = percent
- self.status = status
- self.statusmsg = statusmsg
- self.format_wait = format_wait
- self.wait_until = wait_until
- self.packageID = packageID
- self.packageName = packageName
- self.plugin = plugin
-
-
-class ServerStatus(TBase):
- """
- Attributes:
- - pause
- - active
- - queue
- - total
- - speed
- - download
- - reconnect
- """
-
- __slots__ = [
- 'pause',
- 'active',
- 'queue',
- 'total',
- 'speed',
- 'download',
- 'reconnect',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.BOOL, 'pause', None, None, ), # 1
- (2, TType.I16, 'active', None, None, ), # 2
- (3, TType.I16, 'queue', None, None, ), # 3
- (4, TType.I16, 'total', None, None, ), # 4
- (5, TType.I64, 'speed', None, None, ), # 5
- (6, TType.BOOL, 'download', None, None, ), # 6
- (7, TType.BOOL, 'reconnect', None, None, ), # 7
- )
-
- def __init__(self, pause=None, active=None, queue=None, total=None, speed=None, download=None, reconnect=None,):
- self.pause = pause
- self.active = active
- self.queue = queue
- self.total = total
- self.speed = speed
- self.download = download
- self.reconnect = reconnect
-
-
-class ConfigItem(TBase):
- """
- Attributes:
- - name
- - description
- - value
- - type
- """
-
- __slots__ = [
- 'name',
- 'description',
- 'value',
- 'type',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'name', None, None, ), # 1
- (2, TType.STRING, 'description', None, None, ), # 2
- (3, TType.STRING, 'value', None, None, ), # 3
- (4, TType.STRING, 'type', None, None, ), # 4
- )
-
- def __init__(self, name=None, description=None, value=None, type=None,):
- self.name = name
- self.description = description
- self.value = value
- self.type = type
-
-
-class ConfigSection(TBase):
- """
- Attributes:
- - name
- - description
- - items
- - outline
- """
-
- __slots__ = [
- 'name',
- 'description',
- 'items',
- 'outline',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'name', None, None, ), # 1
- (2, TType.STRING, 'description', None, None, ), # 2
- (3, TType.LIST, 'items', (TType.STRUCT,(ConfigItem, ConfigItem.thrift_spec)), None, ), # 3
- (4, TType.STRING, 'outline', None, None, ), # 4
- )
-
- def __init__(self, name=None, description=None, items=None, outline=None,):
- self.name = name
- self.description = description
- self.items = items
- self.outline = outline
-
-
-class FileData(TBase):
- """
- Attributes:
- - fid
- - url
- - name
- - plugin
- - size
- - format_size
- - status
- - statusmsg
- - packageID
- - error
- - order
- """
-
- __slots__ = [
- 'fid',
- 'url',
- 'name',
- 'plugin',
- 'size',
- 'format_size',
- 'status',
- 'statusmsg',
- 'packageID',
- 'error',
- 'order',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'fid', None, None, ), # 1
- (2, TType.STRING, 'url', None, None, ), # 2
- (3, TType.STRING, 'name', None, None, ), # 3
- (4, TType.STRING, 'plugin', None, None, ), # 4
- (5, TType.I64, 'size', None, None, ), # 5
- (6, TType.STRING, 'format_size', None, None, ), # 6
- (7, TType.I32, 'status', None, None, ), # 7
- (8, TType.STRING, 'statusmsg', None, None, ), # 8
- (9, TType.I32, 'packageID', None, None, ), # 9
- (10, TType.STRING, 'error', None, None, ), # 10
- (11, TType.I16, 'order', None, None, ), # 11
- )
-
- def __init__(self, fid=None, url=None, name=None, plugin=None, size=None, format_size=None, status=None, statusmsg=None, packageID=None, error=None, order=None,):
- self.fid = fid
- self.url = url
- self.name = name
- self.plugin = plugin
- self.size = size
- self.format_size = format_size
- self.status = status
- self.statusmsg = statusmsg
- self.packageID = packageID
- self.error = error
- self.order = order
-
-
-class PackageData(TBase):
- """
- Attributes:
- - pid
- - name
- - folder
- - site
- - password
- - dest
- - order
- - linksdone
- - sizedone
- - sizetotal
- - linkstotal
- - links
- - fids
- """
-
- __slots__ = [
- 'pid',
- 'name',
- 'folder',
- 'site',
- 'password',
- 'dest',
- 'order',
- 'linksdone',
- 'sizedone',
- 'sizetotal',
- 'linkstotal',
- 'links',
- 'fids',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- (2, TType.STRING, 'name', None, None, ), # 2
- (3, TType.STRING, 'folder', None, None, ), # 3
- (4, TType.STRING, 'site', None, None, ), # 4
- (5, TType.STRING, 'password', None, None, ), # 5
- (6, TType.I32, 'dest', None, None, ), # 6
- (7, TType.I16, 'order', None, None, ), # 7
- (8, TType.I16, 'linksdone', None, None, ), # 8
- (9, TType.I64, 'sizedone', None, None, ), # 9
- (10, TType.I64, 'sizetotal', None, None, ), # 10
- (11, TType.I16, 'linkstotal', None, None, ), # 11
- (12, TType.LIST, 'links', (TType.STRUCT,(FileData, FileData.thrift_spec)), None, ), # 12
- (13, TType.LIST, 'fids', (TType.I32,None), None, ), # 13
- )
-
- def __init__(self, pid=None, name=None, folder=None, site=None, password=None, dest=None, order=None, linksdone=None, sizedone=None, sizetotal=None, linkstotal=None, links=None, fids=None,):
- self.pid = pid
- self.name = name
- self.folder = folder
- self.site = site
- self.password = password
- self.dest = dest
- self.order = order
- self.linksdone = linksdone
- self.sizedone = sizedone
- self.sizetotal = sizetotal
- self.linkstotal = linkstotal
- self.links = links
- self.fids = fids
-
-
-class InteractionTask(TBase):
- """
- Attributes:
- - iid
- - input
- - structure
- - preset
- - output
- - data
- - title
- - description
- - plugin
- """
-
- __slots__ = [
- 'iid',
- 'input',
- 'structure',
- 'preset',
- 'output',
- 'data',
- 'title',
- 'description',
- 'plugin',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'iid', None, None, ), # 1
- (2, TType.I32, 'input', None, None, ), # 2
- (3, TType.LIST, 'structure', (TType.STRING,None), None, ), # 3
- (4, TType.LIST, 'preset', (TType.STRING,None), None, ), # 4
- (5, TType.I32, 'output', None, None, ), # 5
- (6, TType.LIST, 'data', (TType.STRING,None), None, ), # 6
- (7, TType.STRING, 'title', None, None, ), # 7
- (8, TType.STRING, 'description', None, None, ), # 8
- (9, TType.STRING, 'plugin', None, None, ), # 9
- )
-
- def __init__(self, iid=None, input=None, structure=None, preset=None, output=None, data=None, title=None, description=None, plugin=None,):
- self.iid = iid
- self.input = input
- self.structure = structure
- self.preset = preset
- self.output = output
- self.data = data
- self.title = title
- self.description = description
- self.plugin = plugin
-
-
-class CaptchaTask(TBase):
- """
- Attributes:
- - tid
- - data
- - type
- - resultType
- """
-
- __slots__ = [
- 'tid',
- 'data',
- 'type',
- 'resultType',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I16, 'tid', None, None, ), # 1
- (2, TType.STRING, 'data', None, None, ), # 2
- (3, TType.STRING, 'type', None, None, ), # 3
- (4, TType.STRING, 'resultType', None, None, ), # 4
- )
-
- def __init__(self, tid=None, data=None, type=None, resultType=None,):
- self.tid = tid
- self.data = data
- self.type = type
- self.resultType = resultType
-
-
-class EventInfo(TBase):
- """
- Attributes:
- - eventname
- - id
- - type
- - destination
- """
-
- __slots__ = [
- 'eventname',
- 'id',
- 'type',
- 'destination',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'eventname', None, None, ), # 1
- (2, TType.I32, 'id', None, None, ), # 2
- (3, TType.I32, 'type', None, None, ), # 3
- (4, TType.I32, 'destination', None, None, ), # 4
- )
-
- def __init__(self, eventname=None, id=None, type=None, destination=None,):
- self.eventname = eventname
- self.id = id
- self.type = type
- self.destination = destination
-
-
-class UserData(TBase):
- """
- Attributes:
- - name
- - email
- - role
- - permission
- - templateName
- """
-
- __slots__ = [
- 'name',
- 'email',
- 'role',
- 'permission',
- 'templateName',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'name', None, None, ), # 1
- (2, TType.STRING, 'email', None, None, ), # 2
- (3, TType.I32, 'role', None, None, ), # 3
- (4, TType.I32, 'permission', None, None, ), # 4
- (5, TType.STRING, 'templateName', None, None, ), # 5
- )
-
- def __init__(self, name=None, email=None, role=None, permission=None, templateName=None,):
- self.name = name
- self.email = email
- self.role = role
- self.permission = permission
- self.templateName = templateName
-
-
-class AccountInfo(TBase):
- """
- Attributes:
- - validuntil
- - login
- - options
- - valid
- - trafficleft
- - maxtraffic
- - premium
- - type
- """
-
- __slots__ = [
- 'validuntil',
- 'login',
- 'options',
- 'valid',
- 'trafficleft',
- 'maxtraffic',
- 'premium',
- 'type',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I64, 'validuntil', None, None, ), # 1
- (2, TType.STRING, 'login', None, None, ), # 2
- (3, TType.MAP, 'options', (TType.STRING,None,TType.LIST,(TType.STRING,None)), None, ), # 3
- (4, TType.BOOL, 'valid', None, None, ), # 4
- (5, TType.I64, 'trafficleft', None, None, ), # 5
- (6, TType.I64, 'maxtraffic', None, None, ), # 6
- (7, TType.BOOL, 'premium', None, None, ), # 7
- (8, TType.STRING, 'type', None, None, ), # 8
- )
-
- def __init__(self, validuntil=None, login=None, options=None, valid=None, trafficleft=None, maxtraffic=None, premium=None, type=None,):
- self.validuntil = validuntil
- self.login = login
- self.options = options
- self.valid = valid
- self.trafficleft = trafficleft
- self.maxtraffic = maxtraffic
- self.premium = premium
- self.type = type
-
-
-class ServiceCall(TBase):
- """
- Attributes:
- - plugin
- - func
- - arguments
- - parseArguments
- """
-
- __slots__ = [
- 'plugin',
- 'func',
- 'arguments',
- 'parseArguments',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'plugin', None, None, ), # 1
- (2, TType.STRING, 'func', None, None, ), # 2
- (3, TType.LIST, 'arguments', (TType.STRING,None), None, ), # 3
- (4, TType.BOOL, 'parseArguments', None, None, ), # 4
- )
-
- def __init__(self, plugin=None, func=None, arguments=None, parseArguments=None,):
- self.plugin = plugin
- self.func = func
- self.arguments = arguments
- self.parseArguments = parseArguments
-
-
-class OnlineStatus(TBase):
- """
- Attributes:
- - name
- - plugin
- - packagename
- - status
- - size
- """
-
- __slots__ = [
- 'name',
- 'plugin',
- 'packagename',
- 'status',
- 'size',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'name', None, None, ), # 1
- (2, TType.STRING, 'plugin', None, None, ), # 2
- (3, TType.STRING, 'packagename', None, None, ), # 3
- (4, TType.I32, 'status', None, None, ), # 4
- (5, TType.I64, 'size', None, None, ), # 5
- )
-
- def __init__(self, name=None, plugin=None, packagename=None, status=None, size=None,):
- self.name = name
- self.plugin = plugin
- self.packagename = packagename
- self.status = status
- self.size = size
-
-
-class OnlineCheck(TBase):
- """
- Attributes:
- - rid
- - data
- """
-
- __slots__ = [
- 'rid',
- 'data',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'rid', None, None, ), # 1
- (2, TType.MAP, 'data', (TType.STRING,None,TType.STRUCT,(OnlineStatus, OnlineStatus.thrift_spec)), None, ), # 2
- )
-
- def __init__(self, rid=None, data=None,):
- self.rid = rid
- self.data = data
-
-
-class PackageDoesNotExists(TExceptionBase):
- """
- Attributes:
- - pid
- """
-
- __slots__ = [
- 'pid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'pid', None, None, ), # 1
- )
-
- def __init__(self, pid=None,):
- self.pid = pid
-
- def __str__(self):
- return repr(self)
-
-
-class FileDoesNotExists(TExceptionBase):
- """
- Attributes:
- - fid
- """
-
- __slots__ = [
- 'fid',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.I32, 'fid', None, None, ), # 1
- )
-
- def __init__(self, fid=None,):
- self.fid = fid
-
- def __str__(self):
- return repr(self)
-
-
-class ServiceDoesNotExists(TExceptionBase):
- """
- Attributes:
- - plugin
- - func
- """
-
- __slots__ = [
- 'plugin',
- 'func',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'plugin', None, None, ), # 1
- (2, TType.STRING, 'func', None, None, ), # 2
- )
-
- def __init__(self, plugin=None, func=None,):
- self.plugin = plugin
- self.func = func
-
- def __str__(self):
- return repr(self)
-
-
-class ServiceException(TExceptionBase):
- """
- Attributes:
- - msg
- """
-
- __slots__ = [
- 'msg',
- ]
-
- thrift_spec = (
- None, # 0
- (1, TType.STRING, 'msg', None, None, ), # 1
- )
-
- def __init__(self, msg=None,):
- self.msg = msg
-
- def __str__(self):
- return repr(self)
-
diff --git a/module/setup.py b/module/setup.py
deleted file mode 100644
index 42b24859f..000000000
--- a/module/setup.py
+++ /dev/null
@@ -1,514 +0,0 @@
-#!/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
-"""
-from getpass import getpass
-import module.common.pylgettext as gettext
-import os
-from os import makedirs
-from os.path import abspath
-from os.path import dirname
-from os.path import exists
-from os.path import join
-from subprocess import PIPE
-from subprocess import call
-import sys
-from sys import exit
-from module.utils import get_console_encoding
-
-class Setup():
- """
- pyLoads initial setup configuration assistent
- """
-
- def __init__(self, path, config):
- self.path = path
- self.config = config
- self.stdin_encoding = get_console_encoding(sys.stdin.encoding)
-
- def start(self):
- langs = self.config.getMetaData("general", "language")["type"].split(";")
- lang = self.ask(u"Choose your Language / WÀhle deine Sprache", "en", langs)
- gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None])
- translation = gettext.translation("setup", join(self.path, "locale"), languages=[lang, "en"], fallback=True)
- translation.install(True)
-
- #Input shorthand for yes
- self.yes = _("y")
- #Input shorthand for no
- self.no = _("n")
-
- # print ""
- # print _("Would you like to configure pyLoad via Webinterface?")
- # print _("You need a Browser and a connection to this PC for it.")
- # viaweb = self.ask(_("Start initial webinterface for configuration?"), "y", bool=True)
- # if viaweb:
- # try:
- # from module.web import ServerThread
- # ServerThread.setup = self
- # from module.web import webinterface
- # webinterface.run_simple()
- # return False
- # except Exception, e:
- # print "Setup failed with this error: ", e
- # print "Falling back to commandline setup."
-
-
- print ""
- print _("Welcome to the pyLoad Configuration Assistent.")
- print _("It will check your system and make a basic setup in order to run pyLoad.")
- print ""
- print _("The value in brackets [] always is the default value,")
- print _("in case you don't want to change it or you are unsure what to choose, just hit enter.")
- print _(
- "Don't forget: You can always rerun this assistent with --setup or -s parameter, when you start pyLoadCore.")
- print _("If you have any problems with this assistent hit STRG-C,")
- print _("to abort and don't let him start with pyLoadCore automatically anymore.")
- print ""
- print _("When you are ready for system check, hit enter.")
- raw_input()
-
- basic, ssl, captcha, gui, web, js = self.system_check()
- print ""
-
- if not basic:
- print _("You need pycurl, sqlite and python 2.5, 2.6 or 2.7 to run pyLoad.")
- print _("Please correct this and re-run pyLoad.")
- print _("Setup will now close.")
- raw_input()
- return False
-
- raw_input(_("System check finished, hit enter to see your status report."))
- print ""
- print _("## Status ##")
- print ""
-
- avail = []
- if self.check_module("Crypto"): avail.append(_("container decrypting"))
- if ssl: avail.append(_("ssl connection"))
- if captcha: avail.append(_("automatic captcha decryption"))
- if gui: avail.append(_("GUI"))
- if web: avail.append(_("Webinterface"))
- if js: avail.append(_("extended Click'N'Load"))
-
- string = ""
-
- for av in avail:
- string += ", " + av
-
- print _("Features available:") + string[1:]
- print ""
-
- if len(avail) < 5:
- print _("Featues missing: ")
- print
-
- if not self.check_module("Crypto"):
- print _("no py-crypto available")
- print _("You need this if you want to decrypt container files.")
- print ""
-
- if not ssl:
- print _("no SSL available")
- print _("This is needed if you want to establish a secure connection to core or webinterface.")
- print _("If you only want to access locally to pyLoad ssl is not usefull.")
- print ""
-
- if not captcha:
- print _("no Captcha Recognition available")
- print _("Only needed for some hosters and as freeuser.")
- print ""
-
- if not gui:
- print _("Gui not available")
- print _("The Graphical User Interface.")
- print ""
-
- if not js:
- print _("no JavaScript engine found")
- print _("You will need this for some Click'N'Load links. Install Spidermonkey, ossp-js, pyv8 or rhino")
-
- print _("You can abort the setup now and fix some dependicies if you want.")
-
- con = self.ask(_("Continue with setup?"), self.yes, bool=True)
-
- if not con:
- return False
-
- print ""
- print _("Do you want to change the config path? Current is %s") % abspath("")
- print _(
- "If you use pyLoad on a server or the home partition lives on an iternal flash it may be a good idea to change it.")
- path = self.ask(_("Change config path?"), self.no, bool=True)
- if path:
- self.conf_path()
- #calls exit when changed
-
- print ""
- print _("Do you want to configure login data and basic settings?")
- print _("This is recommend for first run.")
- con = self.ask(_("Make basic setup?"), self.yes, bool=True)
-
- if con:
- self.conf_basic()
-
- if ssl:
- print ""
- print _("Do you want to configure ssl?")
- ssl = self.ask(_("Configure ssl?"), self.no, bool=True)
- if ssl:
- self.conf_ssl()
-
- if web:
- print ""
- print _("Do you want to configure webinterface?")
- web = self.ask(_("Configure webinterface?"), self.yes, bool=True)
- if web:
- self.conf_web()
-
- print ""
- print _("Setup finished successfully.")
- print _("Hit enter to exit and restart pyLoad")
- raw_input()
- return True
-
- def system_check(self):
- """ make a systemcheck and return the results"""
- print _("## System Check ##")
-
- if sys.version_info[:2] > (2, 7):
- print _("Your python version is to new, Please use Python 2.6/2.7")
- python = False
- elif sys.version_info[:2] < (2, 5):
- print _("Your python version is to old, Please use at least Python 2.5")
- python = False
- else:
- print _("Python Version: OK")
- python = True
-
- curl = self.check_module("pycurl")
- self.print_dep("pycurl", curl)
-
- sqlite = self.check_module("sqlite3")
- self.print_dep("sqlite3", sqlite)
-
- basic = python and curl and sqlite
-
- print ""
-
- crypto = self.check_module("Crypto")
- self.print_dep("pycrypto", crypto)
-
- ssl = self.check_module("OpenSSL")
- self.print_dep("py-OpenSSL", ssl)
-
- print ""
-
- pil = self.check_module("Image")
- self.print_dep("py-imaging", pil)
-
- if os.name == "nt":
- tesser = self.check_prog([join(pypath, "tesseract", "tesseract.exe"), "-v"])
- else:
- tesser = self.check_prog(["tesseract", "-v"])
-
- self.print_dep("tesseract", tesser)
-
- captcha = pil and tesser
-
- print ""
-
- gui = self.check_module("PyQt4")
- self.print_dep("PyQt4", gui)
-
- print ""
- jinja = True
-
- try:
- import jinja2
-
- v = jinja2.__version__
- if v and "unknown" not in v:
- if not v.startswith("2.5") and not v.startswith("2.6"):
- print _("Your installed jinja2 version %s seems too old.") % jinja2.__version__
- print _("You can safely continue but if the webinterface is not working,")
- print _("please upgrade or deinstall it, pyLoad includes a sufficient jinja2 libary.")
- print
- jinja = False
- except:
- pass
-
- self.print_dep("jinja2", jinja)
- beaker = self.check_module("beaker")
- self.print_dep("beaker", beaker)
-
- web = sqlite and beaker
-
- from module.common import JsEngine
-
- js = True if JsEngine.ENGINE else False
- self.print_dep(_("JS engine"), js)
-
- return basic, ssl, captcha, gui, web, js
-
- def conf_basic(self):
- print ""
- print _("## Basic Setup ##")
-
- print ""
- print _("The following logindata is valid for CLI, GUI and webinterface.")
-
- from module.database import DatabaseBackend
-
- db = DatabaseBackend(None)
- db.setup()
- username = self.ask(_("Username"), "User")
- password = self.ask("", "", password=True)
- db.addUser(username, password)
- db.shutdown()
-
- print ""
- print _("External clients (GUI, CLI or other) need remote access to work over the network.")
- print _("However, if you only want to use the webinterface you may disable it to save ram.")
- self.config["remote"]["activated"] = self.ask(_("Enable remote access"), self.yes, bool=True)
-
- print ""
- langs = self.config.getMetaData("general", "language")
- self.config["general"]["language"] = self.ask(_("Language"), "en", langs["type"].split(";"))
-
- self.config["general"]["download_folder"] = self.ask(_("Downloadfolder"), "Downloads")
- self.config["download"]["max_downloads"] = self.ask(_("Max parallel downloads"), "3")
- #print _("You should disable checksum proofing, if you have low hardware requirements.")
- #self.config["general"]["checksum"] = self.ask(_("Proof checksum?"), "y", bool=True)
-
- reconnect = self.ask(_("Use Reconnect?"), self.no, bool=True)
- self.config["reconnect"]["activated"] = reconnect
- if reconnect:
- self.config["reconnect"]["method"] = self.ask(_("Reconnect script location"), "./reconnect.sh")
-
-
- def conf_web(self):
- print ""
- print _("## Webinterface Setup ##")
-
- print ""
- self.config["webinterface"]["activated"] = self.ask(_("Activate webinterface?"), self.yes, bool=True)
- print ""
- print _("Listen address, if you use 127.0.0.1 or localhost, the webinterface will only accessible locally.")
- self.config["webinterface"]["host"] = self.ask(_("Address"), "0.0.0.0")
- self.config["webinterface"]["port"] = self.ask(_("Port"), "8000")
- print ""
- print _("pyLoad offers several server backends, now following a short explanation.")
- print "builtin:", _("Default server, best choice if you dont know which one to choose.")
- print "threaded:", _("This server offers SSL and is a good alternative to builtin.")
- print "fastcgi:", _(
- "Can be used by apache, lighttpd, requires you to configure them, which is not too easy job.")
- print "lightweight:", _("Very fast alternative written in C, requires libev and linux knowlegde.")
- print "\t", _("Get it from here: https://github.com/jonashaag/bjoern, compile it")
- print "\t", _("and copy bjoern.so to module/lib")
-
- print
- print _(
- "Attention: In some rare cases the builtin server is not working, if you notice problems with the webinterface")
- print _("come back here and change the builtin server to the threaded one here.")
-
- self.config["webinterface"]["server"] = self.ask(_("Server"), "builtin",
- ["builtin", "threaded", "fastcgi", "lightweight"])
-
- def conf_ssl(self):
- print ""
- print _("## SSL Setup ##")
- print ""
- print _("Execute these commands from pyLoad config folder to make ssl certificates:")
- print ""
- print "openssl genrsa -out ssl.key 1024"
- print "openssl req -new -key ssl.key -out ssl.csr"
- print "openssl req -days 36500 -x509 -key ssl.key -in ssl.csr > ssl.crt "
- print ""
- print _("If you're done and everything went fine, you can activate ssl now.")
-
- self.config["ssl"]["activated"] = self.ask(_("Activate SSL?"), self.yes, bool=True)
-
- def set_user(self):
- gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None])
- translation = gettext.translation("setup", join(self.path, "locale"),
- languages=[self.config["general"]["language"], "en"], fallback=True)
- translation.install(True)
-
- from module.database import DatabaseBackend
-
- db = DatabaseBackend(None)
- db.setup()
-
- noaction = True
- try:
- while True:
- print _("Select action")
- print _("1 - Create/Edit user")
- print _("2 - List users")
- print _("3 - Remove user")
- print _("4 - Quit")
- action = raw_input("[1]/2/3/4: ")
- if not action in ("1", "2", "3", "4"):
- continue
- elif action == "1":
- print ""
- username = self.ask(_("Username"), "User")
- password = self.ask("", "", password=True)
- db.addUser(username, password)
- noaction = False
- elif action == "2":
- print ""
- print _("Users")
- print "-----"
- users = db.listUsers()
- noaction = False
- for user in users:
- print user
- print "-----"
- print ""
- elif action == "3":
- print ""
- username = self.ask(_("Username"), "")
- if username:
- db.removeUser(username)
- noaction = False
- elif action == "4":
- break
- finally:
- if not noaction:
- db.shutdown()
-
- def conf_path(self, trans=False):
- if trans:
- gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None])
- translation = gettext.translation("setup", join(self.path, "locale"),
- languages=[self.config["general"]["language"], "en"], fallback=True)
- translation.install(True)
-
- print _("Setting new configpath, current configuration will not be transfered!")
- path = self.ask(_("Configpath"), abspath(""))
- try:
- path = join(pypath, path)
- if not exists(path):
- makedirs(path)
- f = open(join(pypath, "module", "config", "configdir"), "wb")
- f.write(path)
- f.close()
- print _("Configpath changed, setup will now close, please restart to go on.")
- print _("Press Enter to exit.")
- raw_input()
- exit()
- except Exception, e:
- print _("Setting config path failed: %s") % str(e)
-
- def print_dep(self, name, value):
- """Print Status of dependency"""
- if value:
- print _("%s: OK") % name
- else:
- print _("%s: missing") % name
-
-
- def check_module(self, module):
- try:
- __import__(module)
- return True
- except:
- return False
-
- def check_prog(self, command):
- pipe = PIPE
- try:
- call(command, stdout=pipe, stderr=pipe)
- return True
- except:
- return False
-
- def ask(self, qst, default, answers=[], bool=False, password=False):
- """produce one line to asking for input"""
- if answers:
- info = "("
-
- for i, answer in enumerate(answers):
- info += (", " if i != 0 else "") + str((answer == default and "[%s]" % answer) or answer)
-
- info += ")"
- elif bool:
- if default == self.yes:
- info = "([%s]/%s)" % (self.yes, self.no)
- else:
- info = "(%s/[%s])" % (self.yes, self.no)
- else:
- info = "[%s]" % default
-
- if password:
- p1 = True
- p2 = False
- while p1 != p2:
- # getpass(_("Password: ")) will crash on systems with broken locales (Win, NAS)
- sys.stdout.write(_("Password: "))
- p1 = getpass("")
-
- if len(p1) < 4:
- print _("Password too short. Use at least 4 symbols.")
- continue
-
- sys.stdout.write(_("Password (again): "))
- p2 = getpass("")
-
- if p1 == p2:
- return p1
- else:
- print _("Passwords did not match.")
-
- while True:
- try:
- input = raw_input(qst + " %s: " % info)
- except KeyboardInterrupt:
- print "\nSetup interrupted"
- exit()
-
- input = input.decode(self.stdin_encoding)
-
- if input.strip() == "":
- input = default
-
- if bool:
- # yes, true,t are inputs for booleans with value true
- if input.lower().strip() in [self.yes, _("yes"), _("true"), _("t"), "yes"]:
- return True
- # no, false,f are inputs for booleans with value false
- elif input.lower().strip() in [self.no, _("no"), _("false"), _("f"), "no"]:
- return False
- else:
- print _("Invalid Input")
- continue
-
- if not answers:
- return input
-
- else:
- if input in answers:
- return input
- else:
- print _("Invalid Input")
-
-
-if __name__ == "__main__":
- test = Setup(join(abspath(dirname(__file__)), ".."), None)
- test.start()
diff --git a/module/unescape.py b/module/unescape.py
deleted file mode 100644
index d8999e077..000000000
--- a/module/unescape.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from module.utils import html_unescape
-#deprecated
-unescape = html_unescape \ No newline at end of file
diff --git a/module/utils.py b/module/utils.py
deleted file mode 100644
index 8748b7693..000000000
--- a/module/utils.py
+++ /dev/null
@@ -1,201 +0,0 @@
-# -*- coding: utf-8 -*-
-
-""" Store all usefull functions here """
-
-import os
-import sys
-import time
-import re
-from os.path import join
-from string import maketrans
-from htmlentitydefs import name2codepoint
-
-def chmod(*args):
- try:
- os.chmod(*args)
- except:
- pass
-
-
-def decode(string):
- """ decode string with utf if possible """
- try:
- return string.decode("utf8", "replace")
- except:
- return string
-
-
-def remove_chars(string, repl):
- """ removes all chars in repl from string"""
- if type(string) == str:
- return string.translate(maketrans("", ""), repl)
- elif type(string) == unicode:
- return string.translate(dict([(ord(s), None) for s in repl]))
-
-
-def save_path(name):
- #remove some chars
- if os.name == 'nt':
- return remove_chars(name, '/\\?%*:|"<>')
- else:
- return remove_chars(name, '/\\"')
-
-
-def save_join(*args):
- """ joins a path, encoding aware """
- return fs_encode(join(*[x if type(x) == unicode else decode(x) for x in args]))
-
-
-# File System Encoding functions:
-# Use fs_encode before accesing files on disk, it will encode the string properly
-
-if sys.getfilesystemencoding().startswith('ANSI'):
- def fs_encode(string):
- try:
- string = string.encode('utf-8')
- finally:
- return string
-
- fs_decode = decode #decode utf8
-
-else:
- fs_encode = fs_decode = lambda x: x # do nothing
-
-def get_console_encoding(enc):
- if os.name == "nt":
- if enc == "cp65001": # aka UTF-8
- print "WARNING: Windows codepage 65001 is not supported."
- enc = "cp850"
- else:
- enc = "utf8"
-
- return enc
-
-def compare_time(start, end):
- start = map(int, start)
- end = map(int, end)
-
- if start == end: return True
-
- now = list(time.localtime()[3:5])
- if start < now < end: return True
- elif start > end and (now > start or now < end): return True
- elif start < now > end < start: return True
- else: return False
-
-
-def formatSize(size):
- """formats size of bytes"""
- size = int(size)
- steps = 0
- sizes = ["B", "KiB", "MiB", "GiB", "TiB"]
- while size > 1000:
- size /= 1024.0
- steps += 1
- return "%.2f %s" % (size, sizes[steps])
-
-
-def formatSpeed(speed):
- return formatSize(speed) + "/s"
-
-
-def freeSpace(folder):
- if os.name == "nt":
- import ctypes
-
- free_bytes = ctypes.c_ulonglong(0)
- ctypes.windll.kernel32.GetDiskFreeSpaceExW(ctypes.c_wchar_p(folder), None, None, ctypes.pointer(free_bytes))
- return free_bytes.value
- else:
- from os import statvfs
-
- s = statvfs(folder)
- return s.f_bsize * s.f_bavail
-
-
-def uniqify(seq, idfun=None):
-# order preserving
- if idfun is None:
- def idfun(x): return x
- seen = {}
- result = []
- for item in seq:
- marker = idfun(item)
- # in old Python versions:
- # if seen.has_key(marker)
- # but in new ones:
- if marker in seen: continue
- seen[marker] = 1
- result.append(item)
- return result
-
-
-def parseFileSize(string, unit=None): #returns bytes
- if not unit:
- m = re.match(r"(\d*[\.,]?\d+)(.*)", string.strip().lower())
- if m:
- traffic = float(m.group(1).replace(",", "."))
- unit = m.group(2)
- else:
- return 0
- else:
- if isinstance(string, basestring):
- traffic = float(string.replace(",", "."))
- else:
- traffic = string
-
- #ignore case
- unit = unit.lower().strip()
-
- if unit in ("gb", "gig", "gbyte", "gigabyte", "gib", "g"):
- traffic *= 1 << 30
- elif unit in ("mb", "mbyte", "megabyte", "mib", "m"):
- traffic *= 1 << 20
- elif unit in ("kb", "kib", "kilobyte", "kbyte", "k"):
- traffic *= 1 << 10
-
- return traffic
-
-
-def lock(func):
- def new(*args):
- #print "Handler: %s args: %s" % (func,args[1:])
- args[0].lock.acquire()
- try:
- return func(*args)
- finally:
- args[0].lock.release()
-
- return new
-
-
-def fixup(m):
- text = m.group(0)
- if text[:2] == "&#":
- # character reference
- try:
- if text[:3] == "&#x":
- return unichr(int(text[3:-1], 16))
- else:
- return unichr(int(text[2:-1]))
- except ValueError:
- pass
- else:
- # named entity
- try:
- name = text[1:-1]
- text = unichr(name2codepoint[name])
- except KeyError:
- pass
-
- return text # leave as is
-
-
-def html_unescape(text):
- """Removes HTML or XML character references and entities from a text string"""
- return re.sub("&#?\w+;", fixup, text)
-
-if __name__ == "__main__":
- print freeSpace(".")
-
- print remove_chars("ab'cdgdsf''ds'", "'ghd")
diff --git a/module/web/ServerThread.py b/module/web/ServerThread.py
deleted file mode 100644
index 84667e5f6..000000000
--- a/module/web/ServerThread.py
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/env python
-from __future__ import with_statement
-from os.path import exists
-
-import os
-import threading
-import logging
-
-core = None
-setup = None
-log = logging.getLogger("log")
-
-class WebServer(threading.Thread):
- def __init__(self, pycore):
- global core
- threading.Thread.__init__(self)
- self.core = pycore
- core = pycore
- self.running = True
- self.server = pycore.config['webinterface']['server']
- self.https = pycore.config['webinterface']['https']
- self.cert = pycore.config["ssl"]["cert"]
- self.key = pycore.config["ssl"]["key"]
- self.host = pycore.config['webinterface']['host']
- self.port = pycore.config['webinterface']['port']
-
- self.setDaemon(True)
-
- def run(self):
- import webinterface
- global webinterface
-
- if self.https:
- if not exists(self.cert) or not exists(self.key):
- log.warning(_("SSL certificates not found."))
- self.https = False
-
- if self.server in ("lighttpd", "nginx"):
- log.warning(_("Sorry, we dropped support for starting %s directly within pyLoad") % self.server)
- log.warning(_("You can use the threaded server which offers good performance and ssl,"))
- log.warning(_("of course you can still use your existing %s with pyLoads fastcgi server") % self.server)
- log.warning(_("sample configs are located in the module/web/servers directory"))
- self.server = "builtin"
-
- if self.server == "fastcgi":
- try:
- import flup
- except:
- log.warning(_("Can't use %(server)s, python-flup is not installed!") % {
- "server": self.server})
- self.server = "builtin"
- elif self.server == "lightweight":
- try:
- import bjoern
- except Exception, e:
- log.error(_("Error importing lightweight server: %s") % e)
- log.warning(_("You need to download and compile bjoern, https://github.com/jonashaag/bjoern"))
- log.warning(_("Copy the boern.so to module/lib folder or use setup.py install"))
- log.warning(_("Of course you need to be familiar with linux and know how to compile software"))
- self.server = "builtin"
-
- if os.name == "nt":
- self.core.log.info(_("Server set to threaded, due to known performance problems on windows."))
- self.core.config['webinterface']['server'] = "threaded"
- self.server = "threaded"
-
-
- if self.server == "fastcgi":
- self.start_fcgi()
- elif self.server == "threaded":
- self.start_threaded()
- elif self.server == "lightweight":
- self.start_lightweight()
- else:
- self.start_builtin()
-
- def start_builtin(self):
-
- if self.https:
- log.warning(_("This server offers no SSL, please consider using threaded instead"))
-
- self.core.log.info(_("Starting builtin webserver: %(host)s:%(port)d") % {"host": self.host, "port": self.port})
- webinterface.run_simple(host=self.host, port=self.port)
-
- def start_threaded(self):
- if self.https:
- self.core.log.info(_("Starting threaded SSL webserver: %(host)s:%(port)d") % {"host": self.host, "port": self.port})
- else:
- self.cert = ""
- self.key = ""
- self.core.log.info(_("Starting threaded webserver: %(host)s:%(port)d") % {"host": self.host, "port": self.port})
-
- webinterface.run_threaded(host=self.host, port=self.port, cert=self.cert, key=self.key)
-
- def start_fcgi(self):
-
- self.core.log.info(_("Starting fastcgi server: %(host)s:%(port)d") % {"host": self.host, "port": self.port})
- webinterface.run_fcgi(host=self.host, port=self.port)
-
-
- def start_lightweight(self):
- if self.https:
- log.warning(_("This server offers no SSL, please consider using threaded instead"))
-
- self.core.log.info(_("Starting lightweight webserver (bjoern): %(host)s:%(port)d") % {"host": self.host, "port": self.port})
- webinterface.run_lightweight(host=self.host, port=self.port)
-
- def quit(self):
- self.running = False
diff --git a/module/web/__init__.py b/module/web/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/module/web/__init__.py
+++ /dev/null
diff --git a/module/web/api_app.py b/module/web/api_app.py
deleted file mode 100644
index 1629c1677..000000000
--- a/module/web/api_app.py
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from urllib import unquote
-from itertools import chain
-from traceback import format_exc, print_exc
-
-from bottle import route, request, response, HTTPError
-
-from utils import toDict, set_session
-from webinterface import PYLOAD
-
-from module.common.json_layer import json
-from module.lib.SafeEval import const_eval as literal_eval
-from module.Api import BaseObject
-
-# json encoder that accepts TBase objects
-class TBaseEncoder(json.JSONEncoder):
-
- def default(self, o):
- if isinstance(o, BaseObject):
- return toDict(o)
- return json.JSONEncoder.default(self, o)
-
-
-# accepting positional arguments, as well as kwargs via post and get
-
-@route("/api/:func:args#[a-zA-Z0-9\-_/\"'\[\]%{}]*#")
-@route("/api/:func:args#[a-zA-Z0-9\-_/\"'\[\]%{}]*#", method="POST")
-def call_api(func, args=""):
- response.headers.replace("Content-type", "application/json")
- response.headers.append("Cache-Control", "no-cache, must-revalidate")
-
- s = request.environ.get('beaker.session')
- if 'session' in request.POST:
- s = s.get_by_id(request.POST['session'])
-
- if not s or not s.get("authenticated", False):
- return HTTPError(403, json.dumps("Forbidden"))
-
- if not PYLOAD.isAuthorized(func, {"role": s["role"], "permission": s["perms"]}):
- return HTTPError(401, json.dumps("Unauthorized"))
-
- args = args.split("/")[1:]
- kwargs = {}
-
- for x, y in chain(request.GET.iteritems(), request.POST.iteritems()):
- if x == "session": continue
- kwargs[x] = unquote(y)
-
- try:
- return callApi(func, *args, **kwargs)
- except Exception, e:
- print_exc()
- return HTTPError(500, json.dumps({"error": e.message, "traceback": format_exc()}))
-
-
-def callApi(func, *args, **kwargs):
- if not hasattr(PYLOAD.EXTERNAL, func) or func.startswith("_"):
- print "Invalid API call", func
- return HTTPError(404, json.dumps("Not Found"))
-
- result = getattr(PYLOAD, func)(*[literal_eval(x) for x in args],
- **dict([(x, literal_eval(y)) for x, y in kwargs.iteritems()]))
-
- # null is invalid json response
- if result is None: result = True
-
- return json.dumps(result, cls=TBaseEncoder)
-
-
-#post -> username, password
-@route("/api/login", method="POST")
-def login():
- response.headers.replace("Content-type", "application/json")
- response.headers.append("Cache-Control", "no-cache, must-revalidate")
-
- user = request.forms.get("username")
- password = request.forms.get("password")
-
- info = PYLOAD.checkAuth(user, password)
-
- if not info:
- return json.dumps(False)
-
- s = set_session(request, info)
-
- # get the session id by dirty way, documentations seems wrong
- try:
- sid = s._headers["cookie_out"].split("=")[1].split(";")[0]
- return json.dumps(sid)
- except:
- return json.dumps(True)
-
-
-@route("/api/logout")
-def logout():
- response.headers.replace("Content-type", "application/json")
- response.headers.append("Cache-Control", "no-cache, must-revalidate")
-
- s = request.environ.get('beaker.session')
- s.delete()
diff --git a/module/web/cnl_app.py b/module/web/cnl_app.py
deleted file mode 100644
index d8f7c1180..000000000
--- a/module/web/cnl_app.py
+++ /dev/null
@@ -1,164 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-from os.path import join
-import re
-from urllib import unquote
-from base64 import standard_b64decode
-from binascii import unhexlify
-
-from bottle import route, request, HTTPError
-from webinterface import PYLOAD, DL_ROOT, JS
-
-try:
- from Crypto.Cipher import AES
-except:
- pass
-
-
-def local_check(function):
- def _view(*args, **kwargs):
- if request.environ.get('REMOTE_ADDR', "0") in ('127.0.0.1', 'localhost') \
- or request.environ.get('HTTP_HOST','0') == '127.0.0.1:9666':
- return function(*args, **kwargs)
- else:
- return HTTPError(403, "Forbidden")
-
- return _view
-
-
-@route("/flash")
-@route("/flash/:id")
-@route("/flash", method="POST")
-@local_check
-def flash(id="0"):
- return "JDownloader\r\n"
-
-@route("/flash/add", method="POST")
-@local_check
-def add(request):
- package = request.POST.get('referer', None)
- urls = filter(lambda x: x != "", request.POST['urls'].split("\n"))
-
- if package:
- PYLOAD.addPackage(package, urls, 0)
- else:
- PYLOAD.generateAndAddPackages(urls, 0)
-
- return ""
-
-@route("/flash/addcrypted", method="POST")
-@local_check
-def addcrypted():
-
- package = request.forms.get('referer', 'ClickAndLoad Package')
- dlc = request.forms['crypted'].replace(" ", "+")
-
- dlc_path = join(DL_ROOT, package.replace("/", "").replace("\\", "").replace(":", "") + ".dlc")
- dlc_file = open(dlc_path, "wb")
- dlc_file.write(dlc)
- dlc_file.close()
-
- try:
- PYLOAD.addPackage(package, [dlc_path], 0)
- except:
- return HTTPError()
- else:
- return "success\r\n"
-
-@route("/flash/addcrypted2", method="POST")
-@local_check
-def addcrypted2():
-
- package = request.forms.get("source", None)
- crypted = request.forms["crypted"]
- jk = request.forms["jk"]
-
- crypted = standard_b64decode(unquote(crypted.replace(" ", "+")))
- if JS:
- jk = "%s f()" % jk
- jk = JS.eval(jk)
-
- else:
- try:
- jk = re.findall(r"return ('|\")(.+)('|\")", jk)[0][1]
- except:
- ## Test for some known js functions to decode
- if jk.find("dec") > -1 and jk.find("org") > -1:
- org = re.findall(r"var org = ('|\")([^\"']+)", jk)[0][1]
- jk = list(org)
- jk.reverse()
- jk = "".join(jk)
- else:
- print "Could not decrypt key, please install py-spidermonkey or ossp-js"
-
- try:
- Key = unhexlify(jk)
- except:
- print "Could not decrypt key, please install py-spidermonkey or ossp-js"
- return "failed"
-
- IV = Key
-
- obj = AES.new(Key, AES.MODE_CBC, IV)
- result = obj.decrypt(crypted).replace("\x00", "").replace("\r","").split("\n")
-
- result = filter(lambda x: x != "", result)
-
- try:
- if package:
- PYLOAD.addPackage(package, result, 0)
- else:
- PYLOAD.generateAndAddPackages(result, 0)
- except:
- return "failed can't add"
- else:
- return "success\r\n"
-
-@route("/flashgot_pyload")
-@route("/flashgot_pyload", method="POST")
-@route("/flashgot")
-@route("/flashgot", method="POST")
-@local_check
-def flashgot():
- if request.environ['HTTP_REFERER'] != "http://localhost:9666/flashgot" and request.environ['HTTP_REFERER'] != "http://127.0.0.1:9666/flashgot":
- return HTTPError()
-
- autostart = int(request.forms.get('autostart', 0))
- package = request.forms.get('package', None)
- urls = filter(lambda x: x != "", request.forms['urls'].split("\n"))
- folder = request.forms.get('dir', None)
-
- if package:
- PYLOAD.addPackage(package, urls, autostart)
- else:
- PYLOAD.generateAndAddPackages(urls, autostart)
-
- return ""
-
-@route("/crossdomain.xml")
-@local_check
-def crossdomain():
- rep = "<?xml version=\"1.0\"?>\n"
- rep += "<!DOCTYPE cross-domain-policy SYSTEM \"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd\">\n"
- rep += "<cross-domain-policy>\n"
- rep += "<allow-access-from domain=\"*\" />\n"
- rep += "</cross-domain-policy>"
- return rep
-
-
-@route("/flash/checkSupportForUrl")
-@local_check
-def checksupport():
-
- url = request.GET.get("url")
- res = PYLOAD.checkURLs([url])
- supported = (not res[0][1] is None)
-
- return str(supported).lower()
-
-@route("/jdcheck.js")
-@local_check
-def jdcheck():
- rep = "jdownloader=true;\n"
- rep += "var version='9.581;'"
- return rep
diff --git a/module/web/filters.py b/module/web/filters.py
deleted file mode 100644
index 13b8345fc..000000000
--- a/module/web/filters.py
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-import os
-from os.path import abspath, commonprefix, join
-
-quotechar = "::/"
-
-try:
- from os.path import relpath
-except:
- from posixpath import curdir, sep, pardir
- def relpath(path, start=curdir):
- """Return a relative version of a path"""
- if not path:
- raise ValueError("no path specified")
- start_list = abspath(start).split(sep)
- path_list = abspath(path).split(sep)
- # Work out how much of the filepath is shared by start and path.
- i = len(commonprefix([start_list, path_list]))
- rel_list = [pardir] * (len(start_list)-i) + path_list[i:]
- if not rel_list:
- return curdir
- return join(*rel_list)
-
-
-def quotepath(path):
- try:
- return path.replace("../", quotechar)
- except AttributeError:
- return path
- except:
- return ""
-
-def unquotepath(path):
- try:
- return path.replace(quotechar, "../")
- except AttributeError:
- return path
- except:
- return ""
-
-def path_make_absolute(path):
- p = os.path.abspath(path)
- if p[-1] == os.path.sep:
- return p
- else:
- return p + os.path.sep
-
-def path_make_relative(path):
- p = relpath(path)
- if p[-1] == os.path.sep:
- return p
- else:
- return p + os.path.sep
-
-def truncate(value, n):
- if (n - len(value)) < 3:
- return value[:n]+"..."
- return value
-
-def date(date, format):
- return date \ No newline at end of file
diff --git a/module/web/json_app.py b/module/web/json_app.py
deleted file mode 100644
index f3626405c..000000000
--- a/module/web/json_app.py
+++ /dev/null
@@ -1,312 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from os.path import join
-from traceback import print_exc
-from shutil import copyfileobj
-
-from bottle import route, request, HTTPError
-
-from webinterface import PYLOAD
-
-from utils import login_required, render_to_response, toDict
-
-from module.utils import decode, formatSize
-
-
-def format_time(seconds):
- seconds = int(seconds)
-
- hours, seconds = divmod(seconds, 3600)
- minutes, seconds = divmod(seconds, 60)
- return "%.2i:%.2i:%.2i" % (hours, minutes, seconds)
-
-
-def get_sort_key(item):
- return item["order"]
-
-
-@route("/json/status")
-@route("/json/status", method="POST")
-@login_required('LIST')
-def status():
- try:
- status = toDict(PYLOAD.statusServer())
- status['captcha'] = PYLOAD.isCaptchaWaiting()
- return status
- except:
- return HTTPError()
-
-
-@route("/json/links")
-@route("/json/links", method="POST")
-@login_required('LIST')
-def links():
- try:
- links = [toDict(x) for x in PYLOAD.statusDownloads()]
- ids = []
- for link in links:
- ids.append(link['fid'])
-
- if link['status'] == 12:
- link['info'] = "%s @ %s/s" % (link['format_eta'], formatSize(link['speed']))
- elif link['status'] == 5:
- link['percent'] = 0
- link['size'] = 0
- link['bleft'] = 0
- link['info'] = _("waiting %s") % link['format_wait']
- else:
- link['info'] = ""
-
- data = {'links': links, 'ids': ids}
- return data
- except Exception, e:
- print_exc()
- return HTTPError()
-
-
-@route("/json/packages")
-@login_required('LIST')
-def packages():
- print "/json/packages"
- try:
- data = PYLOAD.getQueue()
-
- for package in data:
- package['links'] = []
- for file in PYLOAD.get_package_files(package['id']):
- package['links'].append(PYLOAD.get_file_info(file))
-
- return data
-
- except:
- return HTTPError()
-
-
-@route("/json/package/<id:int>")
-@login_required('LIST')
-def package(id):
- try:
- data = toDict(PYLOAD.getPackageData(id))
- data["links"] = [toDict(x) for x in data["links"]]
-
- for pyfile in data["links"]:
- if pyfile["status"] == 0:
- pyfile["icon"] = "status_finished.png"
- elif pyfile["status"] in (2, 3):
- pyfile["icon"] = "status_queue.png"
- elif pyfile["status"] in (9, 1):
- pyfile["icon"] = "status_offline.png"
- elif pyfile["status"] == 5:
- pyfile["icon"] = "status_waiting.png"
- elif pyfile["status"] == 8:
- pyfile["icon"] = "status_failed.png"
- elif pyfile["status"] == 4:
- pyfile["icon"] = "arrow_right.png"
- elif pyfile["status"] in (11, 13):
- pyfile["icon"] = "status_proc.png"
- else:
- pyfile["icon"] = "status_downloading.png"
-
- tmp = data["links"]
- tmp.sort(key=get_sort_key)
- data["links"] = tmp
- return data
-
- except:
- print_exc()
- return HTTPError()
-
-
-@route("/json/package_order/:ids")
-@login_required('ADD')
-def package_order(ids):
- try:
- pid, pos = ids.split("|")
- PYLOAD.orderPackage(int(pid), int(pos))
- return {"response": "success"}
- except:
- return HTTPError()
-
-
-@route("/json/abort_link/<id:int>")
-@login_required('DELETE')
-def abort_link(id):
- try:
- PYLOAD.stopDownloads([id])
- return {"response": "success"}
- except:
- return HTTPError()
-
-
-@route("/json/link_order/:ids")
-@login_required('ADD')
-def link_order(ids):
- try:
- pid, pos = ids.split("|")
- PYLOAD.orderFile(int(pid), int(pos))
- return {"response": "success"}
- except:
- return HTTPError()
-
-
-@route("/json/add_package")
-@route("/json/add_package", method="POST")
-@login_required('ADD')
-def add_package():
- name = request.forms.get("add_name", "New Package").strip()
- queue = int(request.forms['add_dest'])
- links = decode(request.forms['add_links'])
- links = links.split("\n")
- pw = request.forms.get("add_password", "").strip("\n\r")
-
- try:
- f = request.files['add_file']
-
- if not name or name == "New Package":
- name = f.name
-
- fpath = join(PYLOAD.getConfigValue("general", "download_folder"), "tmp_" + f.filename)
- destination = open(fpath, 'wb')
- copyfileobj(f.file, destination)
- destination.close()
- links.insert(0, fpath)
- except:
- pass
-
- name = name.decode("utf8", "ignore")
-
- links = map(lambda x: x.strip(), links)
- links = filter(lambda x: x != "", links)
-
- pack = PYLOAD.addPackage(name, links, queue)
- if pw:
- pw = pw.decode("utf8", "ignore")
- data = {"password": pw}
- PYLOAD.setPackageData(pack, data)
-
-
-@route("/json/move_package/<dest:int>/<id:int>")
-@login_required('MODIFY')
-def move_package(dest, id):
- try:
- PYLOAD.movePackage(dest, id)
- return {"response": "success"}
- except:
- return HTTPError()
-
-
-@route("/json/edit_package", method="POST")
-@login_required('MODIFY')
-def edit_package():
- try:
- id = int(request.forms.get("pack_id"))
- data = {"name": request.forms.get("pack_name").decode("utf8", "ignore"),
- "folder": request.forms.get("pack_folder").decode("utf8", "ignore"),
- "password": request.forms.get("pack_pws").decode("utf8", "ignore")}
-
- PYLOAD.setPackageData(id, data)
- return {"response": "success"}
-
- except:
- return HTTPError()
-
-
-@route("/json/set_captcha")
-@route("/json/set_captcha", method="POST")
-@login_required('ADD')
-def set_captcha():
- if request.environ.get('REQUEST_METHOD', "GET") == "POST":
- try:
- PYLOAD.setCaptchaResult(request.forms["cap_id"], request.forms["cap_result"])
- except:
- pass
-
- task = PYLOAD.getCaptchaTask()
-
- if task.tid >= 0:
- src = "data:image/%s;base64,%s" % (task.type, task.data)
-
- return {'captcha': True, 'id': task.tid, 'src': src, 'result_type' : task.resultType}
- else:
- return {'captcha': False}
-
-
-@route("/json/load_config/:category/:section")
-@login_required("SETTINGS")
-def load_config(category, section):
- conf = None
- if category == "general":
- conf = PYLOAD.getConfigDict()
- elif category == "plugin":
- conf = PYLOAD.getPluginConfigDict()
-
- for key, option in conf[section].iteritems():
- if key in ("desc","outline"): continue
-
- if ";" in option["type"]:
- option["list"] = option["type"].split(";")
-
- option["value"] = decode(option["value"])
-
- return render_to_response("settings_item.html", {"skey": section, "section": conf[section]})
-
-
-@route("/json/save_config/:category", method="POST")
-@login_required("SETTINGS")
-def save_config(category):
- for key, value in request.POST.iteritems():
- try:
- section, option = key.split("|")
- except:
- continue
-
- if category == "general": category = "core"
-
- PYLOAD.setConfigValue(section, option, decode(value), category)
-
-
-@route("/json/add_account", method="POST")
-@login_required("ACCOUNTS")
-def add_account():
- login = request.POST["account_login"]
- password = request.POST["account_password"]
- type = request.POST["account_type"]
-
- PYLOAD.updateAccount(type, login, password)
-
-
-@route("/json/update_accounts", method="POST")
-@login_required("ACCOUNTS")
-def update_accounts():
- deleted = [] #dont update deleted accs or they will be created again
-
- for name, value in request.POST.iteritems():
- value = value.strip()
- if not value: continue
-
- tmp, user = name.split(";")
- plugin, action = tmp.split("|")
-
- if (plugin, user) in deleted: continue
-
- if action == "password":
- PYLOAD.updateAccount(plugin, user, value)
- elif action == "time" and "-" in value:
- PYLOAD.updateAccount(plugin, user, options={"time": [value]})
- elif action == "limitdl" and value.isdigit():
- PYLOAD.updateAccount(plugin, user, options={"limitDL": [value]})
- elif action == "delete":
- deleted.append((plugin,user))
- PYLOAD.removeAccount(plugin, user)
-
-@route("/json/change_password", method="POST")
-def change_password():
-
- user = request.POST["user_login"]
- oldpw = request.POST["login_current_password"]
- newpw = request.POST["login_new_password"]
-
- if not PYLOAD.changePassword(user, oldpw, newpw):
- print "Wrong password"
- return HTTPError()
diff --git a/module/web/media/default/css/MooDialog.css b/module/web/media/default/css/MooDialog.css
deleted file mode 100644
index 48c9166ad..000000000
--- a/module/web/media/default/css/MooDialog.css
+++ /dev/null
@@ -1,92 +0,0 @@
-/* Created by Arian Stolwijk <http://www.aryweb.nl> */
-
-.MooDialog {
-/* position: fixed;*/
- margin: 0 auto 0 -350px;
- width:600px;
- padding:14px;
- left:50%;
- top: 100px;
-
- position: absolute;
- left: 50%;
- z-index: 50000;
-
- background: #eef5f8;
- color: black;
- border-radius: 7px;
- -moz-border-radius: 7px;
- -webkit-border-radius: 7px;
- border-radius: 7px;
- -moz-box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
- -webkit-box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
- box-shadow: 1px 1px 5px rgba(0,0,0,0.8);
-}
-
-.MooDialogTitle {
- padding-top: 30px;
-}
-
-.MooDialog .title {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- padding: 3px 20px;
- background: #b7c4dc;
- border-bottom: 1px solid #a1aec5;
- font-weight: bold;
- text-shadow: 1px 1px 0 #fff;
- color: black;
- border-radius: 7px;
- -moz-border-radius: 7px;
- -webkit-border-radius: 7px;
-}
-
-.MooDialog .close {
- background: url(/media/img/dialog-close.png) no-repeat;
- width: 16px;
- height: 16px;
- display: block;
- cursor: pointer;
- top: -5px;
- left: -5px;
- position: absolute;
-}
-
-.MooDialog .buttons {
- text-align: right;
- margin: 0;
- padding: 0;
- border: 0;
- background: none;
-}
-
-.MooDialog .iframe {
- width: 100%;
- height: 100%;
-}
-
-.MooDialog .textInput {
- width: 200px;
- float: left;
-}
-
-.MooDialog .MooDialogAlert,
-.MooDialog .MooDialogConfirm,
-.MooDialog .MooDialogPrompt,
-.MooDialog .MooDialogError {
- background: url(/media/img/dialog-warning.png) no-repeat;
- padding-left: 40px;
- min-height: 40px;
-}
-
-.MooDialog .MooDialogConfirm,
-.MooDialog .MooDialogPromt {
- background: url(/media/img/dialog-question.png) no-repeat;
-}
-
-.MooDialog .MooDialogError {
- background: url(/media/img/dialog-error.png) no-repeat;
-}
-
diff --git a/module/web/media/default/css/default.css b/module/web/media/default/css/default.css
deleted file mode 100644
index 116f9725a..000000000
--- a/module/web/media/default/css/default.css
+++ /dev/null
@@ -1,908 +0,0 @@
-.hidden {
- display:none;
-}
-.leftalign {
- text-align:left;
-}
-.centeralign {
- text-align:center;
-}
-.rightalign {
- text-align:right;
-}
-
-
-.dokuwiki div.plugin_translation ul li a.wikilink1:link, .dokuwiki div.plugin_translation ul li a.wikilink1:hover, .dokuwiki div.plugin_translation ul li a.wikilink1:active, .dokuwiki div.plugin_translation ul li a.wikilink1:visited {
- background-color:#000080;
- color:#fff !important;
- text-decoration:none;
- padding:0 0.2em;
- margin:0.1em 0.2em;
- border:none !important;
-}
-.dokuwiki div.plugin_translation ul li a.wikilink2:link, .dokuwiki div.plugin_translation ul li a.wikilink2:hover, .dokuwiki div.plugin_translation ul li a.wikilink2:active, .dokuwiki div.plugin_translation ul li a.wikilink2:visited {
- background-color:#808080;
- color:#fff !important;
- text-decoration:none;
- padding:0 0.2em;
- margin:0.1em 0.2em;
- border:none !important;
-}
-
-.dokuwiki div.plugin_translation ul li a:hover img {
- opacity:1.0;
- height:15px;
-}
-
-body {
- margin:0;
- padding:0;
- background-color:white;
- color:black;
- font-size:12px;
- font-family:Verdana, Helvetica, "Lucida Grande", Lucida, Arial, sans-serif;
- font-family:sans-serif;
- font-size:99, 96%;
- font-size-adjust:none;
- font-style:normal;
- font-variant:normal;
- font-weight:normal;
- line-height:normal;
-}
-hr {
- border-width:0;
- border-bottom:1px #aaa dotted;
-}
-img {
- border:none;
-}
-form {
- margin:0px;
- padding:0px;
- border:none;
- display:inline;
- background:transparent;
-}
-ul li {
- margin:5px;
-}
-textarea {
- font-family:monospace;
-}
-table {
- margin:0.5em 0;
- border-collapse:collapse;
-}
-td {
- padding:0.25em;
- border:1pt solid #ADB9CC;
-}
-a {
- color:#3465a4;
- text-decoration:none;
-}
-a:hover {
- text-decoration:underline;
-}
-
-option {
- border:0 none #fff;
-}
-strong.highlight {
- background-color:#fc9;
- padding:1pt;
-}
-#pagebottom {
- clear:both;
-}
-hr {
- height:1px;
- color:#c0c0c0;
- background-color:#c0c0c0;
- border:none;
- margin:.2em 0 .2em 0;
-}
-
-.invisible {
- margin:0px;
- border:0px;
- padding:0px;
- height:0px;
- visibility:hidden;
-}
-.left {
- float:left !important;
-}
-.right {
- float:right !important;
-}
-.center {
- text-align:center;
-}
-div#body-wrapper {
- padding:40px 40px 10px 40px;
- font-size:127%;
-}
-div#content {
- margin-top:-20px;
- padding:0;
- font-size:14px;
- color:black;
- line-height:1.5em;
-}
-h1, h2, h3, h4, h5, h6 {
- background:transparent none repeat scroll 0 0;
- border-bottom:1px solid #aaa;
- color:black;
- font-weight:normal;
- margin:0;
- padding:0;
- padding-bottom:0.17em;
- padding-top:0.5em;
-}
-h1 {
- font-size:188%;
- line-height:1.2em;
- margin-bottom:0.1em;
- padding-bottom:0;
-}
-h2 {
- font-size:150%;
-}
-h3, h4, h5, h6 {
- border-bottom:none;
- font-weight:bold;
-}
-h3 {
- font-size:132%;
-}
-h4 {
- font-size:116%;
-}
-h5 {
- font-size:100%;
-}
-h6 {
- font-size:80%;
-}
-ul#page-actions, ul#page-actions-more {
- float:right;
- margin:10px 10px 0 10px;
- padding:6px;
- color:black;
- background-color:#ececec;
- list-style-type:none;
- white-space: nowrap;
- border-radius:5px;
- -moz-border-radius:5px;
-}
-ul#user-actions {
- padding:5px;
- margin:0;
- display:inline;
- color:black;
- background-color:#ececec;
- list-style-type:none;
- -moz-border-radius:3px;
- border-radius:3px;
-}
-ul#page-actions li, ul#user-actions li, ul#page-actions-more li {
- display:inline;
-}
-ul#page-actions a, ul#user-actions a, ul#page-actions-more a {
- text-decoration:none;
- color:black;
- display:inline;
- margin:0 3px;
- padding:2px 0px 2px 18px;
-}
-ul#page-actions a:hover, ul#page-actions a:focus, ul#user-actions a:hover, ul#user-actions a:focus {
- /*text-decoration:underline;*/
-}
-/***************************/
-ul#page-actions2 {
- float:left;
- margin:10px 10px 0 10px;
- padding:6px;
- color:black;
- background-color:#ececec;
- list-style-type:none;
- border-radius:5px;
- -moz-border-radius:5px;
-}
-ul#user-actions2 {
- padding:5px;
- margin:0;
- display:inline;
- color:black;
- background-color:#ececec;
- list-style-type:none;
- border-radius:3px;
- -moz-border-radius:3px;
-}
-ul#page-actions2 li, ul#user-actions2 li {
- display:inline;
-}
-ul#page-actions2 a, ul#user-actions2 a {
- text-decoration:none;
- color:black;
- display:inline;
- margin:0 3px;
- padding:2px 0px 2px 18px;
-}
-ul#page-actions2 a:hover, ul#page-actions2 a:focus, ul#user-actions2 a:hover, ul#user-actions2 a:focus,
-ul#page-actions-more a:hover, ul#page-actions-more a:focus{
- color: #4e7bb4;
-}
-/****************************/
-.hidden {
- display:none;
-}
-
-a.action.index {
- background:transparent url(/media/default/img/wiki-tools-index.png) 0px 1px no-repeat;
-}
-a.action.recent {
- background:transparent url(/media/default/img/wiki-tools-recent.png) 0px 1px no-repeat;
-}
-a.logout {
- background:transparent url(/media/default/img/user-actions-logout.png) 0px 1px no-repeat;
-}
-
-a.info {
- background:transparent url(/media/default/img/user-info.png) 0px 1px no-repeat;
-}
-
-a.admin {
- background:transparent url(/media/default/img/user-actions-admin.png) 0px 1px no-repeat;
-}
-a.profile {
- background:transparent url(/media/default/img/user-actions-profile.png) 0px 1px no-repeat;
-}
-a.create, a.edit {
- background:transparent url(/media/default/img/page-tools-edit.png) 0px 1px no-repeat;
-}
-a.source, a.show {
- background:transparent url(/media/default/img/page-tools-source.png) 0px 1px no-repeat;
-}
-a.revisions {
- background:transparent url(/media/default/img/page-tools-revisions.png) 0px 1px no-repeat;
-}
-a.subscribe, a.unsubscribe {
- background:transparent url(/media/default/img/page-tools-subscribe.png) 0px 1px no-repeat;
-}
-a.backlink {
- background:transparent url(/media/default/img/page-tools-backlinks.png) 0px 1px no-repeat;
-}
-a.play {
- background:transparent url(/media/default/img/control_play.png) 0px 1px no-repeat;
-}
-.time {
- background:transparent url(/media/default/img/status_None.png) 0px 1px no-repeat;
- padding: 2px 0px 2px 18px;
- margin: 0px 3px;
-}
-.reconnect {
- background:transparent url(/media/default/img/reconnect.png) 0px 1px no-repeat;
- padding: 2px 0px 2px 18px;
- margin: 0px 3px;
-}
-a.play:hover {
- background:transparent url(/media/default/img/control_play_blue.png) 0px 1px no-repeat;
-}
-a.cancel {
- background:transparent url(/media/default/img/control_cancel.png) 0px 1px no-repeat;
-}
-a.cancel:hover {
- background:transparent url(/media/default/img/control_cancel_blue.png) 0px 1px no-repeat;
-}
-a.pause {
- background:transparent url(/media/default/img/control_pause.png) 0px 1px no-repeat;
-}
-a.pause:hover {
- background:transparent url(/media/default/img/control_pause_blue.png) 0px 1px no-repeat;
- font-weight: bold;
-}
-a.stop {
- background:transparent url(/media/default/img/control_stop.png) 0px 1px no-repeat;
-}
-a.stop:hover {
- background:transparent url(/media/default/img/control_stop_blue.png) 0px 1px no-repeat;
-}
-a.add {
- background:transparent url(/media/default/img/control_add.png) 0px 1px no-repeat;
-}
-a.add:hover {
- background:transparent url(/media/default/img/control_add_blue.png) 0px 1px no-repeat;
-}
-a.cog {
- background:transparent url(/media/default/img/cog.png) 0px 1px no-repeat;
-}
-#head-panel {
- background:#525252 url(/media/default/img/head_bg1.png) bottom left repeat-x;
-}
-#head-panel h1 {
- display:none;
- margin:0;
- text-decoration:none;
- padding-top:0.8em;
- padding-left:3.3em;
- font-size:2.6em;
- color:#eeeeec;
-}
-#head-panel #head-logo {
- float:left;
- margin:5px 0 -15px 5px;
- padding:0;
- overflow:visible;
-}
-#head-menu {
- background:transparent url(/media/default/img/tabs-border-bottom.png) 0 100% repeat-x;
- width:100%;
- float:left;
- margin:0;
- padding:0;
- padding-top:0.8em;
-}
-#head-menu ul {
- list-style:none;
- margin:0 1em 0 2em;
-}
-#head-menu ul li {
- float:left;
- margin:0;
- margin-left:0.3em;
- font-size:14px;
- margin-bottom:4px;
-}
-#head-menu ul li.selected, #head-menu ul li:hover {
- margin-bottom:0px;
-}
-#head-menu ul li a img {
- height:22px;
- width:22px;
- vertical-align:middle;
-}
-#head-menu ul li a, #head-menu ul li a:link {
- float:left;
- text-decoration:none;
- color:#555;
- background:#eaeaea url(/media/default/img/tab-background.png) 0 100% repeat-x;
- padding:3px 7px 3px 7px;
- border:2px solid #ccc;
- border-bottom:0px solid transparent;
- padding-bottom:3px;
- -moz-border-radius:5px;
- border-radius:5px;
-}
-#head-menu ul li a:hover, #head-menu ul li a:focus {
- color:#111;
- padding-bottom:7px;
- border-bottom:0px none transparent;
- outline:none;
- border-bottom-left-radius: 0px;
- border-bottom-right-radius: 0px;
- -moz-border-radius-bottomright:0px;
- -moz-border-radius-bottomleft:0px;
-}
-#head-menu ul li a:focus {
- margin-bottom:-4px;
-}
-#head-menu ul li.selected a {
- color:#3566A5;
- background:#fff;
- padding-bottom:7px;
- border-bottom:0px none transparent;
- border-bottom-left-radius: 0px;
- border-bottom-right-radius: 0px;
- -moz-border-radius-bottomright:0px;
- -moz-border-radius-bottomleft:0px;
-}
-#head-menu ul li.selected a:hover, #head-menu ul li.selected a:focus {
- color:#111;
-}
-div#head-search-and-login {
- float:right;
- margin:0 1em 0 0;
- background-color:#222;
- padding:7px 7px 5px 5px;
- color:white;
- white-space: nowrap;
- border-bottom-left-radius: 6px;
- border-bottom-right-radius: 6px;
- -moz-border-radius-bottomright:6px;
- -moz-border-radius-bottomleft:6px;
-}
-div#head-search-and-login form {
- display:inline;
- padding:0 3px;
-}
-div#head-search-and-login form input {
- border:2px solid #888;
- background:#eee;
- font-size:14px;
- padding:2px;
- border-radius:3px;
- -moz-border-radius:3px;
-}
-div#head-search-and-login form input:focus {
- background:#fff;
-}
-#head-search {
- font-size:14px;
-}
-#head-username, #head-password {
- width:80px;
- font-size:14px;
-}
-#pageinfo {
- clear:both;
- color:#888;
- padding:0.6em 0;
- margin:0;
-}
-#foot {
- font-style:normal;
- color:#888;
- text-align:center;
-}
-#foot a {
- color:#aaf;
-}
-#foot img {
- vertical-align:middle;
-}
-div.toc {
- border:1px dotted #888;
- background:#f0f0f0;
- margin:1em 0 1em 1em;
- float:right;
- font-size:95%;
-}
-div.toc .tocheader {
- font-weight:bold;
- margin:0.5em 1em;
-}
-div.toc ol {
- margin:1em 0.5em 1em 1em;
- padding:0;
-}
-div.toc ol li {
- margin:0;
- padding:0;
- margin-left:1em;
-}
-div.toc ol ol {
- margin:0.5em 0.5em 0.5em 1em;
- padding:0;
-}
-div.recentchanges table {
- clear:both;
-}
-div#editor-help {
- font-size:90%;
- border:1px dotted #888;
- padding:0ex 1ex 1ex 1ex;
- background:#f7f6f2;
-}
-div#preview {
- margin-top:1em;
-}
-label.block {
- display:block;
- text-align:right;
- font-weight:bold;
-}
-label.simple {
- display:block;
- text-align:left;
- font-weight:normal;
-}
-label.block input.edit {
- width:50%;
-}
-/*fieldset {
- width:300px;
- text-align:center;
- padding:0.5em;
- margin:auto;
-}
-*/
-div.editor {
- margin:0 0 0 0;
-}
-table {
- margin:0.5em 0;
- border-collapse:collapse;
-}
-td {
- padding:0.25em;
- border:1pt solid #ADB9CC;
-}
-td p {
- margin:0;
- padding:0;
-}
-.u {
- text-decoration:underline;
-}
-.footnotes ul {
- padding:0 2em;
- margin:0 0 1em;
-}
-.footnotes li {
- list-style:none;
-}
-.userpref table, .userpref td {
- border:none;
-}
-#message {
- clear:both;
- padding:5px 10px;
- background-color:#eee;
- border-bottom:2px solid #ccc;
-}
-#message p {
- margin:5px 0;
- padding:0;
- font-weight:bold;
-}
-#message div.buttons {
- font-weight:normal;
-}
-.diff {
- width:99%;
-}
-.diff-title {
- background-color:#C0C0C0;
-}
-.searchresult dd span {
- font-weight:bold;
-}
-.boxtext {
- font-family:tahoma, arial, sans-serif;
- font-size:11px;
- color:#000;
- float:none;
- padding:3px 0 0 10px;
-}
-.statusbutton {
- width:32px;
- height:32px;
- float:left;
- margin-left:-32px;
- margin-right:5px;
- opacity:0;
- cursor:pointer
-}
-.dlsize {
- float:left;
- padding-right: 8px;
-}
-.dlspeed {
- float:left;
- padding-right: 8px;
-}
-.package {
- margin-bottom: 10px;
-}
-.packagename {
- font-weight: bold;
-}
-
-.child {
- margin-left: 20px;
-}
-.child_status {
- margin-right: 10px;
-}
-.child_secrow {
- font-size: 10px;
-}
-
-.header, .header th {
- text-align: left;
- font-weight: normal;
- background-color:#ececec;
- -moz-border-radius:5px;
- border-radius:5px;
-}
-.progress_bar {
- background: #0C0;
- height: 5px;
-
-}
-
-.queue {
- border: none
-}
-
-.queue tr td {
- border: none
-}
-
-.header, .header th{
- text-align: left;
- font-weight: normal;
-}
-
-
-.clearer
-{
- clear: both;
- height: 1px;
-}
-
-.left
-{
- float: left;
-}
-
-.right
-{
- float: right;
-}
-
-
-.setfield
-{
- display: table-cell;
-}
-
-ul.tabs li a
-{
- padding: 5px 16px 4px 15px;
- border: none;
- font-weight: bold;
-
- border-radius: 5px 5px 0 0;
- -moz-border-radius: 5px 5px 0 0;
-
-}
-
-
-#tabs span
-{
- display: none;
-}
-
-#tabs span.selected
-{
- display: inline;
-}
-
-#tabsback
-{
- background-color: #525252;
- margin: 2px 0 0;
- padding: 6px 4px 1px 4px;
-
- border-top-right-radius: 30px;
- border-top-left-radius: 3px;
- -moz-border-radius-topright: 30px;
- -moz-border-radius-topleft: 3px;
-}
-ul.tabs
-{
- list-style-type: none;
- margin:0;
- padding: 0 40px 0 0;
-}
-
-ul.tabs li
-{
- display: inline;
- margin-left: 8px;
-}
-
-
-ul.tabs li a
-{
- color: #42454a;
- background-color: #eaeaea;
- border: 1px none #c9c3ba;
- margin: 0;
- text-decoration: none;
-
- outline: 0;
-
- padding: 5px 16px 4px 15px;
- font-weight: bold;
-
- border-radius: 5px 5px 0 0;
- -moz-border-radius: 5px 5px 0 0;
-
-}
-
-ul.tabs li a.selected, ul.tabs li a:hover
-{
- color: #000;
- background-color: white;
-
- border-bottom-right-radius: 0;
- border-bottom-left-radius: 0;
- -moz-border-radius-bottomright: 0;
- -moz-border-radius-bottomleft: 0;
-}
-
-ul.tabs li a:hover
-{
- background-color: #f1f4ee;
-}
-
-ul.tabs li a.selected
-{
- font-weight: bold;
- background-color: #525252;
- padding-bottom: 5px;
- color: white;
-}
-
-
-#tabs-body {
- position: relative;
- overflow: hidden;
-}
-
-
-span.tabContent
-{
- border: 2px solid #525252;
- margin: 0;
- padding: 0;
- padding-bottom: 10px;
-}
-
-#tabs-body > span {
- display: none;
-}
-
-#tabs-body > span.active {
- display: block;
-}
-
-.hide
-{
- display: none;
-}
-
-.settable
-{
- margin: 20px;
- border: none;
-}
-.settable td
-{
- border: none;
- margin: 0;
- padding: 5px;
-}
-
-.settable th{
- padding-bottom: 8px;
-}
-
-.settable.wide td , .settable.wide th {
- padding-left: 15px;
- padding-right: 15px;
-}
-
-
-/*settings navbar*/
-ul.nav {
- margin: -30px 0 0;
- padding: 0;
- list-style: none;
- position: absolute;
-}
-
-
-ul.nav li {
- position: relative;
- float: left;
- padding: 5px;
-}
-
-ul.nav > li a {
- background: white;
- -moz-border-radius: 4px 4px 4px 4px;
- border: 1px solid #C9C3BA;
- border-bottom: medium none;
- color: black;
-}
-
-ul.nav ul {
- position: absolute;
- top: 26px;
- left: 10px;
- margin: 0;
- padding: 0;
- list-style: none;
- border: 1px solid #AAA;
- background: #f1f1f1;
- -webkit-box-shadow: 1px 1px 5px #AAA;
- -moz-box-shadow: 1px 1px 5px #AAA;
- box-shadow: 1px 1px 5px #AAA;
- cursor: pointer;
-}
-
-ul.nav .open {
- display: block;
-}
-
-ul.nav .close {
- display: none;
-}
-
-ul.nav ul li {
- float: none;
- padding: 0;
-}
-
-ul.nav ul li a {
- width: 130px;
- background: #f1f1f1;
- padding: 3px;
- display: block;
- font-weight: normal;
-}
-
-ul.nav ul li a:hover {
- background: #CDCDCD;
-}
-
-ul.nav ul ul {
- left: 137px;
- top: 0;
-}
-
-.purr-wrapper{
- margin:10px;
-}
-
-/*Purr alert styles*/
-
-.purr-alert{
- margin-bottom:10px;
- padding:10px;
- background:#000;
- font-size:13px;
- font-weight:bold;
- color:#FFF;
- -moz-border-radius:5px;
- -webkit-border-radius:5px;
- /*-moz-box-shadow: 0 0 10px rgba(255,255,0,.25);*/
- width:300px;
-}
-.purr-alert.error{
- color:#F55;
- padding-left:30px;
- background:url(/media/default/img/error.png) no-repeat #000 7px 10px;
- width:280px;
-}
-.purr-alert.success{
- color:#5F5;
- padding-left:30px;
- background:url(/media/default/img/success.png) no-repeat #000 7px 10px;
- width:280px;
-}
-.purr-alert.notice{
- color:#99F;
- padding-left:30px;
- background:url(/media/default/img//notice.png) no-repeat #000 7px 10px;
- width:280px;
-}
-
-table.system {
- border: none;
- margin-left: 10px;
-}
-
-table.system td {
- border: none
-}
-
-table.system tr > td:first-child {
- font-weight: bold;
- padding-right: 10px;
-} \ No newline at end of file
diff --git a/module/web/media/default/css/log.css b/module/web/media/default/css/log.css
deleted file mode 100644
index 73786bfb4..000000000
--- a/module/web/media/default/css/log.css
+++ /dev/null
@@ -1,72 +0,0 @@
-
-html, body, #content
-{
- height: 100%;
-}
-#body-wrapper
-{
- height: 70%;
-}
-.logdiv
-{
- height: 90%;
- width: 100%;
- overflow: auto;
- border: 2px solid #CCC;
- outline: 1px solid #666;
- background-color: #FFE;
- margin-right: auto;
- margin-left: auto;
-}
-.logform
-{
- display: table;
- margin: 0 auto 0 auto;
- padding-top: 5px;
-}
-.logtable
-{
-
- margin: 0px;
-}
-.logtable td
-{
- border: none;
- white-space: nowrap;
-
-
- font-family: monospace;
- font-size: 16px;
- margin: 0px;
- padding: 0px 10px 0px 10px;
- line-height: 110%;
-}
-td.logline
-{
- background-color: #EEE;
- text-align:right;
- padding: 0px 5px 0px 5px;
-}
-td.loglevel
-{
- text-align:right;
-}
-.logperpage
-{
- float: right;
- padding-bottom: 8px;
-}
-.logpaginator
-{
- float: left;
- padding-top: 5px;
-}
-.logpaginator a
-{
- padding: 0px 8px 0px 8px;
-}
-.logwarn
-{
- text-align: center;
- color: red;
-} \ No newline at end of file
diff --git a/module/web/media/default/css/pathchooser.css b/module/web/media/default/css/pathchooser.css
deleted file mode 100644
index 894cc335e..000000000
--- a/module/web/media/default/css/pathchooser.css
+++ /dev/null
@@ -1,68 +0,0 @@
-table {
- width: 90%;
- border: 1px dotted #888888;
- font-family: sans-serif;
- font-size: 10pt;
-}
-
-th {
- background-color: #525252;
- color: #E0E0E0;
-}
-
-table, tr, td {
- background-color: #F0F0F0;
-}
-
-a, a:visited {
- text-decoration: none;
- font-weight: bold;
-}
-
-#paths {
- width: 90%;
- text-align: left;
-}
-
-.file_directory {
- color: #c0c0c0;
-}
-.path_directory {
- color: #3c3c3c;
-}
-.file_file {
- color: #3c3c3c;
-}
-.path_file {
- color: #c0c0c0;
-}
-
-.parentdir {
- color: #000000;
- font-size: 10pt;
-}
-.name {
- text-align: left;
-}
-.size {
- text-align: right;
-}
-.type {
- text-align: left;
-}
-.mtime {
- text-align: center;
-}
-
-.path_abs_rel {
- color: #3c3c3c;
- text-decoration: none;
- font-weight: bold;
- font-family: sans-serif;
- font-size: 10pt;
-}
-
-.path_abs_rel a {
- color: #3c3c3c;
- font-style: italic;
-}
diff --git a/module/web/media/default/css/window.css b/module/web/media/default/css/window.css
deleted file mode 100644
index 12829868b..000000000
--- a/module/web/media/default/css/window.css
+++ /dev/null
@@ -1,73 +0,0 @@
-/* ----------- stylized ----------- */
-.window_box h1{
- font-size:14px;
- font-weight:bold;
- margin-bottom:8px;
-}
-.window_box p{
- font-size:11px;
- color:#666666;
- margin-bottom:20px;
- border-bottom:solid 1px #b7ddf2;
- padding-bottom:10px;
-}
-.window_box label{
- display:block;
- font-weight:bold;
- text-align:right;
- width:240px;
- float:left;
-}
-.window_box .small{
- color:#666666;
- display:block;
- font-size:11px;
- font-weight:normal;
- text-align:right;
- width:240px;
-}
-.window_box select, .window_box input{
- float:left;
- font-size:12px;
- padding:4px 2px;
- border:solid 1px #aacfe4;
- width:300px;
- margin:2px 0 20px 10px;
-}
-.window_box .cont{
- float:left;
- font-size:12px;
- padding: 0px 10px 15px 0px;
- width:300px;
- margin:0px 0px 0px 10px;
-}
-.window_box .cont input{
- float: none;
- margin: 0px 15px 0px 1px;
-}
-.window_box textarea{
- float:left;
- font-size:12px;
- padding:4px 2px;
- border:solid 1px #aacfe4;
- width:300px;
- margin:2px 0 20px 10px;
-}
-.window_box button, .styled_button{
- clear:both;
- margin-left:150px;
- width:125px;
- height:31px;
- background:#666666 url(../img/button.png) no-repeat;
- text-align:center;
- line-height:31px;
- color:#FFFFFF;
- font-size:11px;
- font-weight:bold;
- border: 0px;
-}
-
-.styled_button {
- margin-left: 15px;
- cursor: pointer;
-}
diff --git a/module/web/media/default/img/add_folder.png b/module/web/media/default/img/add_folder.png
deleted file mode 100644
index 8acbc411b..000000000
--- a/module/web/media/default/img/add_folder.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/ajax-loader.gif b/module/web/media/default/img/ajax-loader.gif
deleted file mode 100644
index 2fd8e0737..000000000
--- a/module/web/media/default/img/ajax-loader.gif
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/arrow_refresh.png b/module/web/media/default/img/arrow_refresh.png
deleted file mode 100644
index 0de26566d..000000000
--- a/module/web/media/default/img/arrow_refresh.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/arrow_right.png b/module/web/media/default/img/arrow_right.png
deleted file mode 100644
index b1a181923..000000000
--- a/module/web/media/default/img/arrow_right.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/big_button.gif b/module/web/media/default/img/big_button.gif
deleted file mode 100644
index 7680490ea..000000000
--- a/module/web/media/default/img/big_button.gif
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/big_button_over.gif b/module/web/media/default/img/big_button_over.gif
deleted file mode 100644
index 2e3ee10d2..000000000
--- a/module/web/media/default/img/big_button_over.gif
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/body.png b/module/web/media/default/img/body.png
deleted file mode 100644
index 7ff1043e0..000000000
--- a/module/web/media/default/img/body.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/button.png b/module/web/media/default/img/button.png
deleted file mode 100644
index 890160614..000000000
--- a/module/web/media/default/img/button.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/closebtn.gif b/module/web/media/default/img/closebtn.gif
deleted file mode 100644
index 3e27e6030..000000000
--- a/module/web/media/default/img/closebtn.gif
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/cog.png b/module/web/media/default/img/cog.png
deleted file mode 100644
index 67de2c6cc..000000000
--- a/module/web/media/default/img/cog.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/control_add.png b/module/web/media/default/img/control_add.png
deleted file mode 100644
index d39886893..000000000
--- a/module/web/media/default/img/control_add.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/control_add_blue.png b/module/web/media/default/img/control_add_blue.png
deleted file mode 100644
index d11b7f41d..000000000
--- a/module/web/media/default/img/control_add_blue.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/control_cancel.png b/module/web/media/default/img/control_cancel.png
deleted file mode 100644
index 7b9bc3fba..000000000
--- a/module/web/media/default/img/control_cancel.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/control_cancel_blue.png b/module/web/media/default/img/control_cancel_blue.png
deleted file mode 100644
index 0c5c96ce3..000000000
--- a/module/web/media/default/img/control_cancel_blue.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/control_pause.png b/module/web/media/default/img/control_pause.png
deleted file mode 100644
index 2d9ce9c4e..000000000
--- a/module/web/media/default/img/control_pause.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/control_pause_blue.png b/module/web/media/default/img/control_pause_blue.png
deleted file mode 100644
index ec61099b0..000000000
--- a/module/web/media/default/img/control_pause_blue.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/control_play.png b/module/web/media/default/img/control_play.png
deleted file mode 100644
index 0846555d0..000000000
--- a/module/web/media/default/img/control_play.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/control_play_blue.png b/module/web/media/default/img/control_play_blue.png
deleted file mode 100644
index f8c8ec683..000000000
--- a/module/web/media/default/img/control_play_blue.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/control_stop.png b/module/web/media/default/img/control_stop.png
deleted file mode 100644
index 893bb60e5..000000000
--- a/module/web/media/default/img/control_stop.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/control_stop_blue.png b/module/web/media/default/img/control_stop_blue.png
deleted file mode 100644
index e6f75d232..000000000
--- a/module/web/media/default/img/control_stop_blue.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/delete.png b/module/web/media/default/img/delete.png
deleted file mode 100644
index 08f249365..000000000
--- a/module/web/media/default/img/delete.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/drag_corner.gif b/module/web/media/default/img/drag_corner.gif
deleted file mode 100644
index befb1adf1..000000000
--- a/module/web/media/default/img/drag_corner.gif
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/error.png b/module/web/media/default/img/error.png
deleted file mode 100644
index c37bd062e..000000000
--- a/module/web/media/default/img/error.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/folder.png b/module/web/media/default/img/folder.png
deleted file mode 100644
index 784e8fa48..000000000
--- a/module/web/media/default/img/folder.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/full.png b/module/web/media/default/img/full.png
deleted file mode 100644
index fea52af76..000000000
--- a/module/web/media/default/img/full.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head-login.png b/module/web/media/default/img/head-login.png
deleted file mode 100644
index b59b7cbbf..000000000
--- a/module/web/media/default/img/head-login.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head-menu-collector.png b/module/web/media/default/img/head-menu-collector.png
deleted file mode 100644
index 861be40bc..000000000
--- a/module/web/media/default/img/head-menu-collector.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head-menu-config.png b/module/web/media/default/img/head-menu-config.png
deleted file mode 100644
index bbf43d4f3..000000000
--- a/module/web/media/default/img/head-menu-config.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head-menu-development.png b/module/web/media/default/img/head-menu-development.png
deleted file mode 100644
index fad150fe1..000000000
--- a/module/web/media/default/img/head-menu-development.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head-menu-download.png b/module/web/media/default/img/head-menu-download.png
deleted file mode 100644
index 98c5da9db..000000000
--- a/module/web/media/default/img/head-menu-download.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head-menu-home.png b/module/web/media/default/img/head-menu-home.png
deleted file mode 100644
index 9d62109aa..000000000
--- a/module/web/media/default/img/head-menu-home.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head-menu-index.png b/module/web/media/default/img/head-menu-index.png
deleted file mode 100644
index 44d631064..000000000
--- a/module/web/media/default/img/head-menu-index.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head-menu-news.png b/module/web/media/default/img/head-menu-news.png
deleted file mode 100644
index 43950ebc9..000000000
--- a/module/web/media/default/img/head-menu-news.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head-menu-queue.png b/module/web/media/default/img/head-menu-queue.png
deleted file mode 100644
index be98793ce..000000000
--- a/module/web/media/default/img/head-menu-queue.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head-menu-recent.png b/module/web/media/default/img/head-menu-recent.png
deleted file mode 100644
index fc9b0497f..000000000
--- a/module/web/media/default/img/head-menu-recent.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head-menu-wiki.png b/module/web/media/default/img/head-menu-wiki.png
deleted file mode 100644
index 07cf0102d..000000000
--- a/module/web/media/default/img/head-menu-wiki.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head-search-noshadow.png b/module/web/media/default/img/head-search-noshadow.png
deleted file mode 100644
index aafdae015..000000000
--- a/module/web/media/default/img/head-search-noshadow.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/head_bg1.png b/module/web/media/default/img/head_bg1.png
deleted file mode 100644
index f2848c3cc..000000000
--- a/module/web/media/default/img/head_bg1.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/images.png b/module/web/media/default/img/images.png
deleted file mode 100644
index 184860d1e..000000000
--- a/module/web/media/default/img/images.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/notice.png b/module/web/media/default/img/notice.png
deleted file mode 100644
index 12cd1aef9..000000000
--- a/module/web/media/default/img/notice.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/package_go.png b/module/web/media/default/img/package_go.png
deleted file mode 100644
index aace63ad6..000000000
--- a/module/web/media/default/img/package_go.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/page-tools-backlinks.png b/module/web/media/default/img/page-tools-backlinks.png
deleted file mode 100644
index 3eb6a9ce3..000000000
--- a/module/web/media/default/img/page-tools-backlinks.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/page-tools-edit.png b/module/web/media/default/img/page-tools-edit.png
deleted file mode 100644
index 188e1c12b..000000000
--- a/module/web/media/default/img/page-tools-edit.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/page-tools-revisions.png b/module/web/media/default/img/page-tools-revisions.png
deleted file mode 100644
index 5c3b8587f..000000000
--- a/module/web/media/default/img/page-tools-revisions.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/parseUri.png b/module/web/media/default/img/parseUri.png
deleted file mode 100644
index 937bded9d..000000000
--- a/module/web/media/default/img/parseUri.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/pencil.png b/module/web/media/default/img/pencil.png
deleted file mode 100644
index 0bfecd50e..000000000
--- a/module/web/media/default/img/pencil.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/pyload-logo-edited3.5-new-font-small.png b/module/web/media/default/img/pyload-logo-edited3.5-new-font-small.png
deleted file mode 100644
index 2443cd8b1..000000000
--- a/module/web/media/default/img/pyload-logo-edited3.5-new-font-small.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/reconnect.png b/module/web/media/default/img/reconnect.png
deleted file mode 100644
index 49b269145..000000000
--- a/module/web/media/default/img/reconnect.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/status_None.png b/module/web/media/default/img/status_None.png
deleted file mode 100644
index 293b13f77..000000000
--- a/module/web/media/default/img/status_None.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/status_downloading.png b/module/web/media/default/img/status_downloading.png
deleted file mode 100644
index fb4ebc850..000000000
--- a/module/web/media/default/img/status_downloading.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/status_failed.png b/module/web/media/default/img/status_failed.png
deleted file mode 100644
index c37bd062e..000000000
--- a/module/web/media/default/img/status_failed.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/status_finished.png b/module/web/media/default/img/status_finished.png
deleted file mode 100644
index 89c8129a4..000000000
--- a/module/web/media/default/img/status_finished.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/status_offline.png b/module/web/media/default/img/status_offline.png
deleted file mode 100644
index 0cfd58596..000000000
--- a/module/web/media/default/img/status_offline.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/status_proc.png b/module/web/media/default/img/status_proc.png
deleted file mode 100644
index 67de2c6cc..000000000
--- a/module/web/media/default/img/status_proc.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/status_queue.png b/module/web/media/default/img/status_queue.png
deleted file mode 100644
index 293b13f77..000000000
--- a/module/web/media/default/img/status_queue.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/status_waiting.png b/module/web/media/default/img/status_waiting.png
deleted file mode 100644
index 2842cc338..000000000
--- a/module/web/media/default/img/status_waiting.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/success.png b/module/web/media/default/img/success.png
deleted file mode 100644
index 89c8129a4..000000000
--- a/module/web/media/default/img/success.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/tab-background.png b/module/web/media/default/img/tab-background.png
deleted file mode 100644
index 29a5d1991..000000000
--- a/module/web/media/default/img/tab-background.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/tabs-border-bottom.png b/module/web/media/default/img/tabs-border-bottom.png
deleted file mode 100644
index 02440f428..000000000
--- a/module/web/media/default/img/tabs-border-bottom.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/user-actions-logout.png b/module/web/media/default/img/user-actions-logout.png
deleted file mode 100644
index 0010931e2..000000000
--- a/module/web/media/default/img/user-actions-logout.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/user-actions-profile.png b/module/web/media/default/img/user-actions-profile.png
deleted file mode 100644
index 46573fff6..000000000
--- a/module/web/media/default/img/user-actions-profile.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/default/img/user-info.png b/module/web/media/default/img/user-info.png
deleted file mode 100644
index 6e643100f..000000000
--- a/module/web/media/default/img/user-info.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/img/dialog-close.png b/module/web/media/img/dialog-close.png
deleted file mode 100644
index 81ebb88b2..000000000
--- a/module/web/media/img/dialog-close.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/img/dialog-question.png b/module/web/media/img/dialog-question.png
deleted file mode 100644
index b0af3db5b..000000000
--- a/module/web/media/img/dialog-question.png
+++ /dev/null
Binary files differ
diff --git a/module/web/media/img/favicon.ico b/module/web/media/img/favicon.ico
deleted file mode 100644
index 58b1f4b89..000000000
--- a/module/web/media/img/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/module/web/media/js/MooDialog_static.js b/module/web/media/js/MooDialog_static.js
deleted file mode 100644
index d497d3d57..000000000
--- a/module/web/media/js/MooDialog_static.js
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
----
-
-name: Overlay
-
-authors:
- - David Walsh (http://davidwalsh.name)
-
-license:
- - MIT-style license
-
-requires: [Core/Class, Core/Element.Style, Core/Element.Event, Core/Element.Dimensions, Core/Fx.Tween]
-
-provides:
- - Overlay
-...
-*/
-
-var Overlay = new Class({
-
- Implements: [Options, Events],
-
- options: {
- id: 'overlay',
- color: '#000',
- duration: 500,
- opacity: 0.5,
- zIndex: 5000/*,
- onClick: $empty,
- onClose: $empty,
- onHide: $empty,
- onOpen: $empty,
- onShow: $empty
- */
- },
-
- initialize: function(container, options){
- this.setOptions(options);
- this.container = document.id(container);
-
- this.bound = {
- 'window': {
- resize: this.resize.bind(this),
- scroll: this.scroll.bind(this)
- },
- overlayClick: this.overlayClick.bind(this),
- tweenStart: this.tweenStart.bind(this),
- tweenComplete: this.tweenComplete.bind(this)
- };
-
- this.build().attach();
- },
-
- build: function(){
- this.overlay = new Element('div', {
- id: this.options.id,
- opacity: 0,
- styles: {
- position: (Browser.ie6) ? 'absolute' : 'fixed',
- background: this.options.color,
- left: 0,
- top: 0,
- 'z-index': this.options.zIndex
- }
- }).inject(this.container);
- this.tween = new Fx.Tween(this.overlay, {
- duration: this.options.duration,
- link: 'cancel',
- property: 'opacity'
- });
- this.tween.set('opacity', 0)
- return this;
- }.protect(),
-
- attach: function(){
- window.addEvents(this.bound.window);
- this.overlay.addEvent('click', this.bound.overlayClick);
- this.tween.addEvents({
- onStart: this.bound.tweenStart,
- onComplete: this.bound.tweenComplete
- });
- return this;
- },
-
- detach: function(){
- var args = Array.prototype.slice.call(arguments);
- args.each(function(item){
- if(item == 'window') window.removeEvents(this.bound.window);
- if(item == 'overlay') this.overlay.removeEvent('click', this.bound.overlayClick);
- }, this);
- return this;
- },
-
- overlayClick: function(){
- this.fireEvent('click');
- return this;
- },
-
- tweenStart: function(){
- this.overlay.setStyles({
- width: '100%',
- height: this.container.getScrollSize().y
- });
- return this;
- },
-
- tweenComplete: function(){
- this.fireEvent(this.overlay.get('opacity') == this.options.opacity ? 'show' : 'hide');
- return this;
- },
-
- open: function(){
- this.fireEvent('open');
- this.tween.set('display', 'block');
- this.tween.start(this.options.opacity);
- return this;
- },
-
- close: function(){
- this.fireEvent('close');
- this.tween.start(0).chain(function(){
- this.tween.set('display', 'none');
- }.bind(this));
- return this;
- },
-
- resize: function(){
- this.fireEvent('resize');
- this.overlay.setStyle('height', this.container.getScrollSize().y);
- return this;
- },
-
- scroll: function(){
- this.fireEvent('scroll');
- if (Browser.ie6) this.overlay.setStyle('left', window.getScroll().x);
- return this;
- }
-
-});
-/*
----
-name: MooDialog
-description: The base class of MooDialog
-authors: Arian Stolwijk
-license: MIT-style license
-requires: [Core/Class, Core/Element, Core/Element.Styles, Core/Element.Event]
-provides: [MooDialog, Element.MooDialog]
-...
-*/
-
-
-var MooDialog = new Class({
-
- Implements: [Options, Events],
-
- options: {
- 'class': 'MooDialog',
- title: null,
- scroll: true, // IE
- forceScroll: false,
- useEscKey: true,
- destroyOnHide: true,
- autoOpen: true,
- closeButton: true,
- onInitialize: function(){
- this.wrapper.setStyle('display', 'none');
- },
- onBeforeOpen: function(){
- this.wrapper.setStyle('display', 'block');
- this.fireEvent('show');
- },
- onBeforeClose: function(){
- this.wrapper.setStyle('display', 'none');
- this.fireEvent('hide');
- }/*,
- onOpen: function(){},
- onClose: function(){},
- onShow: function(){},
- onHide: function(){}*/
- },
-
- initialize: function(options){
- this.setOptions(options);
- this.options.inject = this.options.inject || document.body;
- options = this.options;
-
- var wrapper = this.wrapper = new Element('div.' + options['class'].replace(' ', '.')).inject(options.inject);
- this.content = new Element('div.content').inject(wrapper);
- wrapper.setStyle('display', 'none');
-
- if (options.title){
- this.title = new Element('div.title').set('text', options.title).inject(wrapper);
- wrapper.addClass('MooDialogTitle');
- }
-
- if (options.closeButton){
- this.closeButton = new Element('a.close', {
- events: {click: this.close.bind(this)}
- }).inject(wrapper);
- }
-
-
- /*<ie6>*/// IE 6 scroll
- if ((options.scroll && Browser.ie6) || options.forceScroll){
- wrapper.setStyle('position', 'absolute');
- var position = wrapper.getPosition(options.inject);
- window.addEvent('scroll', function(){
- var scroll = document.getScroll();
- wrapper.setPosition({
- x: position.x + scroll.x,
- y: position.y + scroll.y
- });
- });
- }
- /*</ie6>*/
-
- if (options.useEscKey){
- // Add event for the esc key
- document.addEvent('keydown', function(e){
- if (e.key == 'esc') this.close();
- }.bind(this));
- }
-
- this.addEvent('hide', function(){
- if (options.destroyOnHide) this.destroy();
- }.bind(this));
-
- this.fireEvent('initialize', wrapper);
- },
-
- setContent: function(){
- var content = Array.from(arguments);
- if (content.length == 1) content = content[0];
-
- this.content.empty();
-
- var type = typeOf(content);
- if (['string', 'number'].contains(type)) this.content.set('text', content);
- else this.content.adopt(content);
-
- return this;
- },
-
- open: function(){
- this.fireEvent('beforeOpen', this.wrapper).fireEvent('open');
- this.opened = true;
- return this;
- },
-
- close: function(){
- this.fireEvent('beforeClose', this.wrapper).fireEvent('close');
- this.opened = false;
- return this;
- },
-
- destroy: function(){
- this.wrapper.destroy();
- },
-
- toElement: function(){
- return this.wrapper;
- }
-
-});
-
-
-Element.implement({
-
- MooDialog: function(options){
- this.store('MooDialog',
- new MooDialog(options).setContent(this).open()
- );
- return this;
- }
-
-});
-/*
----
-name: MooDialog.Fx
-description: Overwrite the default events so the Dialogs are using Fx on open and close
-authors: Arian Stolwijk
-license: MIT-style license
-requires: [Cores/Fx.Tween, Overlay]
-provides: MooDialog.Fx
-...
-*/
-
-
-MooDialog.implement('options', {
-
- duration: 400,
- closeOnOverlayClick: true,
-
- onInitialize: function(wrapper){
- this.fx = new Fx.Tween(wrapper, {
- property: 'opacity',
- duration: this.options.duration
- }).set(0);
- this.overlay = new Overlay(this.options.inject, {
- duration: this.options.duration
- });
- if (this.options.closeOnOverlayClick) this.overlay.addEvent('click', this.close.bind(this));
- },
-
- onBeforeOpen: function(wrapper){
- this.overlay.open();
- wrapper.setStyle('display', 'block');
- this.fx.start(1).chain(function(){
- this.fireEvent('show');
- }.bind(this));
- },
-
- onBeforeClose: function(wrapper){
- this.overlay.close();
- this.fx.start(0).chain(function(){
- this.fireEvent('hide');
- wrapper.setStyle('display', 'none');
- }.bind(this));
- }
-
-});
-/*
----
-name: MooDialog.Confirm
-description: Creates an Confirm Dialog
-authors: Arian Stolwijk
-license: MIT-style license
-requires: MooDialog
-provides: [MooDialog.Confirm, Element.confirmLinkClick, Element.confirmFormSubmit]
-...
-*/
-
-
-MooDialog.Confirm = new Class({
-
- Extends: MooDialog,
-
- options: {
- okText: 'Ok',
- cancelText: 'Cancel',
- focus: true,
- textPClass: 'MooDialogConfirm'
- },
-
- initialize: function(msg, fn, fn1, options){
- this.parent(options);
- var emptyFn = function(){},
- self = this;
-
- var buttons = [
- {fn: fn || emptyFn, txt: this.options.okText},
- {fn: fn1 || emptyFn, txt: this.options.cancelText}
- ].map(function(button){
- return new Element('input[type=button]', {
- events: {
- click: function(){
- button.fn();
- self.close();
- }
- },
- value: button.txt
- });
- });
-
- this.setContent(
- new Element('p.' + this.options.textPClass, {text: msg}),
- new Element('div.buttons').adopt(buttons)
- );
- if (this.options.autoOpen) this.open();
-
- if(this.options.focus) this.addEvent('show', function(){
- buttons[1].focus();
- });
-
- }
-});
-
-
-Element.implement({
-
- confirmLinkClick: function(msg, options){
- this.addEvent('click', function(e){
- e.stop();
- new MooDialog.Confirm(msg, function(){
- location.href = this.get('href');
- }.bind(this), null, options)
- });
- return this;
- },
-
- confirmFormSubmit: function(msg, options){
- this.addEvent('submit', function(e){
- e.stop();
- new MooDialog.Confirm(msg, function(){
- this.submit();
- }.bind(this), null, options)
- }.bind(this));
- return this;
- }
-
-});
diff --git a/module/web/media/js/MooDropMenu_static.js b/module/web/media/js/MooDropMenu_static.js
deleted file mode 100644
index b9cd8cc10..000000000
--- a/module/web/media/js/MooDropMenu_static.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
----
-description: This provides a simple Drop Down menu with infinit levels
-
-license: MIT-style
-
-authors:
-- Arian Stolwijk
-
-requires:
- - Core/Class.Extras
- - Core/Element.Event
- - Core/Selectors
-
-provides: [MooDropMenu, Element.MooDropMenu]
-
-...
-*/
-
-var MooDropMenu = new Class({
-
- Implements: [Options, Events],
-
- options: {
- onOpen: function(el){
- el.removeClass('close').addClass('open');
- },
- onClose: function(el){
- el.removeClass('open').addClass('close');
- },
- onInitialize: function(el){
- el.removeClass('open').addClass('close');
- },
- mouseoutDelay: 200,
- mouseoverDelay: 0,
- listSelector: 'ul',
- itemSelector: 'li'
- },
-
- initialize: function(menu, options, level){
- this.setOptions(options);
- options = this.options;
-
- var menu = this.menu = document.id(menu);
-
- menu.getElements(options.itemSelector + ' > ' + options.listSelector).each(function(el){
-
- this.fireEvent('initialize', el);
-
- var parent = el.getParent(options.itemSelector),
- timer;
-
- parent.addEvents({
-
- 'mouseenter': function(){
- parent.store('DropDownOpen', true);
-
- clearTimeout(timer);
- if (options.mouseoverDelay) timer = this.fireEvent.delay(options.mouseoverDelay, this, ['open', el]);
- else this.fireEvent('open', el);
-
- }.bind(this),
-
- 'mouseleave': function(){
- parent.store('DropDownOpen', false);
-
- clearTimeout(timer);
- timer = (function(){
- if (!parent.retrieve('DropDownOpen')) this.fireEvent('close', el);
- }).delay(options.mouseoutDelay, this);
-
- }.bind(this)
- });
-
- }, this);
- },
-
- toElement: function(){
- return this.menu
- }
-
-});
-
-/* So you can do like this $('nav').MooDropMenu(); or even $('nav').MooDropMenu().setStyle('border',1); */
-Element.implement({
- MooDropMenu: function(options){
- return this.store('MooDropMenu', new MooDropMenu(this, options));
- }
-});
diff --git a/module/web/media/js/admin.coffee b/module/web/media/js/admin.coffee
deleted file mode 100644
index 82b0dd3ec..000000000
--- a/module/web/media/js/admin.coffee
+++ /dev/null
@@ -1,58 +0,0 @@
-root = this
-
-window.addEvent "domready", ->
-
- root.passwordDialog = new MooDialog {destroyOnHide: false}
- root.passwordDialog.setContent $ 'password_box'
-
- $("login_password_reset").addEvent "click", (e) -> root.passwordDialog.close()
- $("login_password_button").addEvent "click", (e) ->
-
- newpw = $("login_new_password").get("value")
- newpw2 = $("login_new_password2").get("value")
-
- if newpw is newpw2
- form = $("password_form")
- form.set "send", {
- onSuccess: (data) ->
- root.notify.alert "Success", {
- 'className': 'success'
- }
- onFailure: (data) ->
- root.notify.alert "Error", {
- 'className': 'error'
- }
- }
-
- form.send()
-
- root.passwordDialog.close()
- else
- alert '{{_("Passwords did not match.")}}'
-
- e.stop()
-
- for item in $$(".change_password")
- id = item.get("id")
- user = id.split("|")[1]
- $("user_login").set("value", user)
- item.addEvent "click", (e) -> root.passwordDialog.open()
-
- $('quit-pyload').addEvent "click", (e) ->
- new MooDialog.Confirm "{{_('You are really sure you want to quit pyLoad?')}}", ->
- new Request.JSON({
- url: '/api/kill'
- method: 'get'
- }).send()
- , ->
- e.stop()
-
- $('restart-pyload').addEvent "click", (e) ->
- new MooDialog.Confirm "{{_('Are you sure you want to restart pyLoad?')}}", ->
- new Request.JSON({
- url: '/api/restart'
- method: 'get'
- onSuccess: (data) -> alert "{{_('pyLoad restarted')}}"
- }).send()
- , ->
- e.stop() \ No newline at end of file
diff --git a/module/web/media/js/admin.js b/module/web/media/js/admin.js
deleted file mode 100644
index d34d310a0..000000000
--- a/module/web/media/js/admin.js
+++ /dev/null
@@ -1,3 +0,0 @@
-{% autoescape true %}
-var root;root=this;window.addEvent("domready",function(){var f,c,b,e,a,d;root.passwordDialog=new MooDialog({destroyOnHide:false});root.passwordDialog.setContent($("password_box"));$("login_password_reset").addEvent("click",function(g){return root.passwordDialog.close()});$("login_password_button").addEvent("click",function(j){var h,i,g;i=$("login_new_password").get("value");g=$("login_new_password2").get("value");if(i===g){h=$("password_form");h.set("send",{onSuccess:function(k){return root.notify.alert("Success",{className:"success"})},onFailure:function(k){return root.notify.alert("Error",{className:"error"})}});h.send();root.passwordDialog.close()}else{alert('{{_("Passwords did not match.")}}')}return j.stop()});d=$$(".change_password");for(e=0,a=d.length;e<a;e++){c=d[e];f=c.get("id");b=f.split("|")[1];$("user_login").set("value",b);c.addEvent("click",function(g){return root.passwordDialog.open()})}$("quit-pyload").addEvent("click",function(g){new MooDialog.Confirm("{{_('You are really sure you want to quit pyLoad?')}}",function(){return new Request.JSON({url:"/api/kill",method:"get"}).send()},function(){});return g.stop()});return $("restart-pyload").addEvent("click",function(g){new MooDialog.Confirm("{{_('Are you sure you want to restart pyLoad?')}}",function(){return new Request.JSON({url:"/api/restart",method:"get",onSuccess:function(h){return alert("{{_('pyLoad restarted')}}")}}).send()},function(){});return g.stop()})});
-{% endautoescape %} \ No newline at end of file
diff --git a/module/web/media/js/base.coffee b/module/web/media/js/base.coffee
deleted file mode 100644
index 3b5d33e82..000000000
--- a/module/web/media/js/base.coffee
+++ /dev/null
@@ -1,173 +0,0 @@
-# External scope
-root = this
-
-# helper functions
-humanFileSize = (size) ->
- filesizename = new Array("B", "KiB", "MiB", "GiB", "TiB", "PiB")
- loga = Math.log(size) / Math.log(1024)
- i = Math.floor(loga)
- a = Math.pow(1024, i)
- if size is 0 then "0 B" else (Math.round(size * 100 / a) / 100 + " " + filesizename[i])
-
-parseUri = () ->
- oldString = $("add_links").value
- regxp = new RegExp('(ht|f)tp(s?):\/\/[a-zA-Z0-9\-\.\/\?=_&%#]+[<| |\"|\'|\r|\n|\t]{1}', 'g')
- resu = oldString.match regxp
- return if resu == null
- res = "";
-
- for part in resu
- if part.indexOf(" ") != -1
- res = res + part.replace(" ", " \n")
- else if part.indexOf("\t") != -1
- res = res + part.replace("\t", " \n")
- else if part.indexOf("\r") != -1
- res = res + part.replace("\r", " \n")
- else if part.indexOf("\"") != -1
- res = res + part.replace("\"", " \n")
- else if part.indexOf("<") != -1
- res = res + part.replace("<", " \n")
- else if part.indexOf("'") != -1
- res = res + part.replace("'", " \n")
- else
- res = res + part.replace("\n", " \n")
-
- $("add_links").value = res;
-
-
-Array::remove = (from, to) ->
- rest = this.slice((to || from) + 1 || this.length)
- this.length = from < 0 ? this.length + from : from
- return [] if this.length == 0
- return this.push.apply(this, rest)
-
-
-document.addEvent "domready", ->
-
- # global notification
- root.notify = new Purr {
- 'mode': 'top'
- 'position': 'center'
- }
-
- root.captchaBox = new MooDialog {destroyOnHide: false}
- root.captchaBox.setContent $ 'cap_box'
-
- root.addBox = new MooDialog {destroyOnHide: false}
- root.addBox.setContent $ 'add_box'
-
- $('add_form').onsubmit = ->
- $('add_form').target = 'upload_target'
- if $('add_name').value is "" and $('add_file').value is ""
- alert '{{_("Please Enter a packagename.")}}'
- return false
- else
- root.addBox.close()
- return true
-
- $('add_reset').addEvent 'click', -> root.addBox.close()
-
- $('action_add').addEvent 'click', -> $("add_form").reset(); root.addBox.open()
- $('action_play').addEvent 'click', -> new Request({method: 'get', url: '/api/unpauseServer'}).send()
- $('action_cancel').addEvent 'click', -> new Request({method: 'get', url: '/api/stopAllDownloads'}).send()
- $('action_stop').addEvent 'click', -> new Request({method: 'get', url: '/api/pauseServer'}).send()
-
-
- # captcha events
-
- $('cap_info').addEvent 'click', ->
- load_captcha "get", ""
- root.captchaBox.open()
- $('cap_reset').addEvent 'click', -> root.captchaBox.close()
- $('cap_form').addEvent 'submit', (e) ->
- submit_captcha()
- e.stop()
-
- $('cap_positional').addEvent 'click', on_captcha_click
-
- new Request.JSON({
- url: "/json/status"
- onSuccess: LoadJsonToContent
- secure: false
- async: true
- initialDelay: 0
- delay: 4000
- limit: 3000
- }).startTimer()
-
-LoadJsonToContent = (data) ->
- $("speed").set 'text', humanFileSize(data.speed)+"/s"
- $("aktiv").set 'text', data.active
- $("aktiv_from").set 'text', data.queue
- $("aktiv_total").set 'text', data.total
-
- if data.captcha
- if $("cap_info").getStyle("display") != "inline"
- $("cap_info").setStyle 'display', 'inline'
- root.notify.alert '{{_("New Captcha Request")}}', {
- 'className': 'notify'
- }
- else
- $("cap_info").setStyle 'display', 'none'
-
-
- if data.download
- $("time").set 'text', ' {{_("on")}}'
- $("time").setStyle 'background-color', "#8ffc25"
- else
- $("time").set 'text', ' {{_("off")}}'
- $("time").setStyle 'background-color', "#fc6e26"
-
- if data.reconnect
- $("reconnect").set 'text', ' {{_("on")}}'
- $("reconnect").setStyle 'background-color', "#8ffc25"
- else
- $("reconnect").set 'text', ' {{_("off")}}'
- $("reconnect").setStyle 'background-color', "#fc6e26"
-
- return null
-
-
-set_captcha = (data) ->
- $('cap_id').set 'value', data.id
- if (data.result_type is 'textual')
- $('cap_textual_img').set 'src', data.src
- $('cap_title').set 'text', '{{_("Please read the text on the captcha.")}}'
- $('cap_submit').setStyle 'display', 'inline'
- $('cap_textual').setStyle 'display', 'block'
- $('cap_positional').setStyle 'display', 'none'
-
- else if (data.result_type == 'positional')
- $('cap_positional_img').set('src', data.src)
- $('cap_title').set('text', '{{_("Please click on the right captcha position.")}}')
- $('cap_submit').setStyle('display', 'none')
- $('cap_textual').setStyle('display', 'none')
-
-
-load_captcha = (method, post) ->
- new Request.JSON({
- url: "/json/set_captcha"
- onSuccess: (data) -> set_captcha(data) if data.captcha else clear_captcha()
- secure: false
- async: true
- method: method
- }).send(post)
-
-clear_captcha = ->
- $('cap_textual').setStyle 'display', 'none'
- $('cap_textual_img').set 'src', ''
- $('cap_positional').setStyle 'display', 'none'
- $('cap_positional_img').set 'src', ''
- $('cap_title').set 'text', '{{_("No Captchas to read.")}}'
-
-submit_captcha = ->
- load_captcha("post", "cap_id=" + $('cap_id').get('value') + "&cap_result=" + $('cap_result').get('value') );
- $('cap_result').set('value', '')
- false
-
-on_captcha_click = (e) ->
- position = e.target.getPosition()
- x = e.page.x - position.x
- y = e.page.y - position.y
- $('cap_result').value = x + "," + y
- submit_captcha() \ No newline at end of file
diff --git a/module/web/media/js/base.js b/module/web/media/js/base.js
deleted file mode 100644
index c68b1047a..000000000
--- a/module/web/media/js/base.js
+++ /dev/null
@@ -1,3 +0,0 @@
-{% autoescape true %}
-var LoadJsonToContent,clear_captcha,humanFileSize,load_captcha,on_captcha_click,parseUri,root,set_captcha,submit_captcha;root=this;humanFileSize=function(f){var c,d,e,b;d=new Array("B","KiB","MiB","GiB","TiB","PiB");b=Math.log(f)/Math.log(1024);e=Math.floor(b);c=Math.pow(1024,e);if(f===0){return"0 B"}else{return Math.round(f*100/c)/100+" "+d[e]}};parseUri=function(){var b,c,g,e,d,f,a;b=$("add_links").value;g=new RegExp("(ht|f)tp(s?)://[a-zA-Z0-9-./?=_&%#]+[<| |\"|'|\r|\n|\t]{1}","g");d=b.match(g);if(d===null){return}e="";for(f=0,a=d.length;f<a;f++){c=d[f];if(c.indexOf(" ")!==-1){e=e+c.replace(" "," \n")}else{if(c.indexOf("\t")!==-1){e=e+c.replace("\t"," \n")}else{if(c.indexOf("\r")!==-1){e=e+c.replace("\r"," \n")}else{if(c.indexOf('"')!==-1){e=e+c.replace('"'," \n")}else{if(c.indexOf("<")!==-1){e=e+c.replace("<"," \n")}else{if(c.indexOf("'")!==-1){e=e+c.replace("'"," \n")}else{e=e+c.replace("\n"," \n")}}}}}}}return $("add_links").value=e};Array.prototype.remove=function(d,c){var a,b;a=this.slice((c||d)+1||this.length);this.length=(b=d<0)!=null?b:this.length+{from:d};if(this.length===0){return[]}return this.push.apply(this,a)};document.addEvent("domready",function(){root.notify=new Purr({mode:"top",position:"center"});root.captchaBox=new MooDialog({destroyOnHide:false});root.captchaBox.setContent($("cap_box"));root.addBox=new MooDialog({destroyOnHide:false});root.addBox.setContent($("add_box"));$("add_form").onsubmit=function(){$("add_form").target="upload_target";if($("add_name").value===""&&$("add_file").value===""){alert('{{_("Please Enter a packagename.")}}');return false}else{root.addBox.close();return true}};$("add_reset").addEvent("click",function(){return root.addBox.close()});$("action_add").addEvent("click",function(){$("add_form").reset();return root.addBox.open()});$("action_play").addEvent("click",function(){return new Request({method:"get",url:"/api/unpauseServer"}).send()});$("action_cancel").addEvent("click",function(){return new Request({method:"get",url:"/api/stopAllDownloads"}).send()});$("action_stop").addEvent("click",function(){return new Request({method:"get",url:"/api/pauseServer"}).send()});$("cap_info").addEvent("click",function(){load_captcha("get","");return root.captchaBox.open()});$("cap_reset").addEvent("click",function(){return root.captchaBox.close()});$("cap_form").addEvent("submit",function(a){submit_captcha();return a.stop()});$("cap_positional").addEvent("click",on_captcha_click);return new Request.JSON({url:"/json/status",onSuccess:LoadJsonToContent,secure:false,async:true,initialDelay:0,delay:4000,limit:3000}).startTimer()});LoadJsonToContent=function(a){$("speed").set("text",humanFileSize(a.speed)+"/s");$("aktiv").set("text",a.active);$("aktiv_from").set("text",a.queue);$("aktiv_total").set("text",a.total);if(a.captcha){if($("cap_info").getStyle("display")!=="inline"){$("cap_info").setStyle("display","inline");root.notify.alert('{{_("New Captcha Request")}}',{className:"notify"})}}else{$("cap_info").setStyle("display","none")}if(a.download){$("time").set("text",' {{_("on")}}');$("time").setStyle("background-color","#8ffc25")}else{$("time").set("text",' {{_("off")}}');$("time").setStyle("background-color","#fc6e26")}if(a.reconnect){$("reconnect").set("text",' {{_("on")}}');$("reconnect").setStyle("background-color","#8ffc25")}else{$("reconnect").set("text",' {{_("off")}}');$("reconnect").setStyle("background-color","#fc6e26")}return null};set_captcha=function(a){$("cap_id").set("value",a.id);if(a.result_type==="textual"){$("cap_textual_img").set("src",a.src);$("cap_title").set("text",'{{_("Please read the text on the captcha.")}}');$("cap_submit").setStyle("display","inline");$("cap_textual").setStyle("display","block");return $("cap_positional").setStyle("display","none")}else{if(a.result_type==="positional"){$("cap_positional_img").set("src",a.src);$("cap_title").set("text",'{{_("Please click on the right captcha position.")}}');$("cap_submit").setStyle("display","none");return $("cap_textual").setStyle("display","none")}}};load_captcha=function(b,a){return new Request.JSON({url:"/json/set_captcha",onSuccess:function(c){return set_captcha(c)(c.captcha?void 0:clear_captcha())},secure:false,async:true,method:b}).send(a)};clear_captcha=function(){$("cap_textual").setStyle("display","none");$("cap_textual_img").set("src","");$("cap_positional").setStyle("display","none");$("cap_positional_img").set("src","");return $("cap_title").set("text",'{{_("No Captchas to read.")}}')};submit_captcha=function(){load_captcha("post","cap_id="+$("cap_id").get("value")+"&cap_result="+$("cap_result").get("value"));$("cap_result").set("value","");return false};on_captcha_click=function(c){var b,a,d;b=c.target.getPosition();a=c.page.x-b.x;d=c.page.y-b.y;$("cap_result").value=a+","+d;return submit_captcha()};
-{% endautoescape %} \ No newline at end of file
diff --git a/module/web/media/js/mootools-core-1.4.1.js b/module/web/media/js/mootools-core-1.4.1.js
deleted file mode 100644
index 835b4bbe2..000000000
--- a/module/web/media/js/mootools-core-1.4.1.js
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
----
-MooTools: the javascript framework
-
-web build:
- - http://mootools.net/core/76bf47062d6c1983d66ce47ad66aa0e0
-
-packager build:
- - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff
-
-copyrights:
- - [MooTools](http://mootools.net)
-
-licenses:
- - [MIT License](http://mootools.net/license.txt)
-...
-*/
-(function(){this.MooTools={version:"1.4.1",build:"d1fb25710e3c5482a219ab9dc675a4e0ad2176b6"};var o=this.typeOf=function(i){if(i==null){return"null";}if(i.$family){return i.$family();
-}if(i.nodeName){if(i.nodeType==1){return"element";}if(i.nodeType==3){return(/\S/).test(i.nodeValue)?"textnode":"whitespace";}}else{if(typeof i.length=="number"){if(i.callee){return"arguments";
-}if("item" in i){return"collection";}}}return typeof i;};var j=this.instanceOf=function(t,i){if(t==null){return false;}var s=t.$constructor||t.constructor;
-while(s){if(s===i){return true;}s=s.parent;}return t instanceof i;};var f=this.Function;var p=true;for(var k in {toString:1}){p=null;}if(p){p=["hasOwnProperty","valueOf","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","constructor"];
-}f.prototype.overloadSetter=function(s){var i=this;return function(u,t){if(u==null){return this;}if(s||typeof u!="string"){for(var v in u){i.call(this,v,u[v]);
-}if(p){for(var w=p.length;w--;){v=p[w];if(u.hasOwnProperty(v)){i.call(this,v,u[v]);}}}}else{i.call(this,u,t);}return this;};};f.prototype.overloadGetter=function(s){var i=this;
-return function(u){var v,t;if(s||typeof u!="string"){v=u;}else{if(arguments.length>1){v=arguments;}}if(v){t={};for(var w=0;w<v.length;w++){t[v[w]]=i.call(this,v[w]);
-}}else{t=i.call(this,u);}return t;};};f.prototype.extend=function(i,s){this[i]=s;}.overloadSetter();f.prototype.implement=function(i,s){this.prototype[i]=s;
-}.overloadSetter();var n=Array.prototype.slice;f.from=function(i){return(o(i)=="function")?i:function(){return i;};};Array.from=function(i){if(i==null){return[];
-}return(a.isEnumerable(i)&&typeof i!="string")?(o(i)=="array")?i:n.call(i):[i];};Number.from=function(s){var i=parseFloat(s);return isFinite(i)?i:null;
-};String.from=function(i){return i+"";};f.implement({hide:function(){this.$hidden=true;return this;},protect:function(){this.$protected=true;return this;
-}});var a=this.Type=function(u,t){if(u){var s=u.toLowerCase();var i=function(v){return(o(v)==s);};a["is"+u]=i;if(t!=null){t.prototype.$family=(function(){return s;
-}).hide();}}if(t==null){return null;}t.extend(this);t.$constructor=a;t.prototype.$constructor=t;return t;};var e=Object.prototype.toString;a.isEnumerable=function(i){return(i!=null&&typeof i.length=="number"&&e.call(i)!="[object Function]");
-};var q={};var r=function(i){var s=o(i.prototype);return q[s]||(q[s]=[]);};var b=function(t,x){if(x&&x.$hidden){return;}var s=r(this);for(var u=0;u<s.length;
-u++){var w=s[u];if(o(w)=="type"){b.call(w,t,x);}else{w.call(this,t,x);}}var v=this.prototype[t];if(v==null||!v.$protected){this.prototype[t]=x;}if(this[t]==null&&o(x)=="function"){m.call(this,t,function(i){return x.apply(i,n.call(arguments,1));
-});}};var m=function(i,t){if(t&&t.$hidden){return;}var s=this[i];if(s==null||!s.$protected){this[i]=t;}};a.implement({implement:b.overloadSetter(),extend:m.overloadSetter(),alias:function(i,s){b.call(this,i,this.prototype[s]);
-}.overloadSetter(),mirror:function(i){r(this).push(i);return this;}});new a("Type",a);var d=function(s,w,u){var t=(w!=Object),A=w.prototype;if(t){w=new a(s,w);
-}for(var x=0,v=u.length;x<v;x++){var B=u[x],z=w[B],y=A[B];if(z){z.protect();}if(t&&y){delete A[B];A[B]=y.protect();}}if(t){w.implement(A);}return d;};d("String",String,["charAt","charCodeAt","concat","indexOf","lastIndexOf","match","quote","replace","search","slice","split","substr","substring","trim","toLowerCase","toUpperCase"])("Array",Array,["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice","indexOf","lastIndexOf","filter","forEach","every","map","some","reduce","reduceRight"])("Number",Number,["toExponential","toFixed","toLocaleString","toPrecision"])("Function",f,["apply","call","bind"])("RegExp",RegExp,["exec","test"])("Object",Object,["create","defineProperty","defineProperties","keys","getPrototypeOf","getOwnPropertyDescriptor","getOwnPropertyNames","preventExtensions","isExtensible","seal","isSealed","freeze","isFrozen"])("Date",Date,["now"]);
-Object.extend=m.overloadSetter();Date.extend("now",function(){return +(new Date);});new a("Boolean",Boolean);Number.prototype.$family=function(){return isFinite(this)?"number":"null";
-}.hide();Number.extend("random",function(s,i){return Math.floor(Math.random()*(i-s+1)+s);});var g=Object.prototype.hasOwnProperty;Object.extend("forEach",function(i,t,u){for(var s in i){if(g.call(i,s)){t.call(u,i[s],s,i);
-}}});Object.each=Object.forEach;Array.implement({forEach:function(u,v){for(var t=0,s=this.length;t<s;t++){if(t in this){u.call(v,this[t],t,this);}}},each:function(i,s){Array.forEach(this,i,s);
-return this;}});var l=function(i){switch(o(i)){case"array":return i.clone();case"object":return Object.clone(i);default:return i;}};Array.implement("clone",function(){var s=this.length,t=new Array(s);
-while(s--){t[s]=l(this[s]);}return t;});var h=function(s,i,t){switch(o(t)){case"object":if(o(s[i])=="object"){Object.merge(s[i],t);}else{s[i]=Object.clone(t);
-}break;case"array":s[i]=t.clone();break;default:s[i]=t;}return s;};Object.extend({merge:function(z,u,t){if(o(u)=="string"){return h(z,u,t);}for(var y=1,s=arguments.length;
-y<s;y++){var w=arguments[y];for(var x in w){h(z,x,w[x]);}}return z;},clone:function(i){var t={};for(var s in i){t[s]=l(i[s]);}return t;},append:function(w){for(var v=1,t=arguments.length;
-v<t;v++){var s=arguments[v]||{};for(var u in s){w[u]=s[u];}}return w;}});["Object","WhiteSpace","TextNode","Collection","Arguments"].each(function(i){new a(i);
-});var c=Date.now();String.extend("uniqueID",function(){return(c++).toString(36);});})();Array.implement({every:function(c,d){for(var b=0,a=this.length>>>0;
-b<a;b++){if((b in this)&&!c.call(d,this[b],b,this)){return false;}}return true;},filter:function(d,e){var c=[];for(var b=0,a=this.length>>>0;b<a;b++){if((b in this)&&d.call(e,this[b],b,this)){c.push(this[b]);
-}}return c;},indexOf:function(c,d){var b=this.length>>>0;for(var a=(d<0)?Math.max(0,b+d):d||0;a<b;a++){if(this[a]===c){return a;}}return -1;},map:function(c,e){var d=this.length>>>0,b=Array(d);
-for(var a=0;a<d;a++){if(a in this){b[a]=c.call(e,this[a],a,this);}}return b;},some:function(c,d){for(var b=0,a=this.length>>>0;b<a;b++){if((b in this)&&c.call(d,this[b],b,this)){return true;
-}}return false;},clean:function(){return this.filter(function(a){return a!=null;});},invoke:function(a){var b=Array.slice(arguments,1);return this.map(function(c){return c[a].apply(c,b);
-});},associate:function(c){var d={},b=Math.min(this.length,c.length);for(var a=0;a<b;a++){d[c[a]]=this[a];}return d;},link:function(c){var a={};for(var e=0,b=this.length;
-e<b;e++){for(var d in c){if(c[d](this[e])){a[d]=this[e];delete c[d];break;}}}return a;},contains:function(a,b){return this.indexOf(a,b)!=-1;},append:function(a){this.push.apply(this,a);
-return this;},getLast:function(){return(this.length)?this[this.length-1]:null;},getRandom:function(){return(this.length)?this[Number.random(0,this.length-1)]:null;
-},include:function(a){if(!this.contains(a)){this.push(a);}return this;},combine:function(c){for(var b=0,a=c.length;b<a;b++){this.include(c[b]);}return this;
-},erase:function(b){for(var a=this.length;a--;){if(this[a]===b){this.splice(a,1);}}return this;},empty:function(){this.length=0;return this;},flatten:function(){var d=[];
-for(var b=0,a=this.length;b<a;b++){var c=typeOf(this[b]);if(c=="null"){continue;}d=d.concat((c=="array"||c=="collection"||c=="arguments"||instanceOf(this[b],Array))?Array.flatten(this[b]):this[b]);
-}return d;},pick:function(){for(var b=0,a=this.length;b<a;b++){if(this[b]!=null){return this[b];}}return null;},hexToRgb:function(b){if(this.length!=3){return null;
-}var a=this.map(function(c){if(c.length==1){c+=c;}return c.toInt(16);});return(b)?a:"rgb("+a+")";},rgbToHex:function(d){if(this.length<3){return null;}if(this.length==4&&this[3]==0&&!d){return"transparent";
-}var b=[];for(var a=0;a<3;a++){var c=(this[a]-0).toString(16);b.push((c.length==1)?"0"+c:c);}return(d)?b:"#"+b.join("");}});String.implement({test:function(a,b){return((typeOf(a)=="regexp")?a:new RegExp(""+a,b)).test(this);
-},contains:function(a,b){return(b)?(b+this+b).indexOf(b+a+b)>-1:String(this).indexOf(a)>-1;},trim:function(){return String(this).replace(/^\s+|\s+$/g,"");
-},clean:function(){return String(this).replace(/\s+/g," ").trim();},camelCase:function(){return String(this).replace(/-\D/g,function(a){return a.charAt(1).toUpperCase();
-});},hyphenate:function(){return String(this).replace(/[A-Z]/g,function(a){return("-"+a.charAt(0).toLowerCase());});},capitalize:function(){return String(this).replace(/\b[a-z]/g,function(a){return a.toUpperCase();
-});},escapeRegExp:function(){return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g,"\\$1");},toInt:function(a){return parseInt(this,a||10);},toFloat:function(){return parseFloat(this);
-},hexToRgb:function(b){var a=String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/);return(a)?a.slice(1).hexToRgb(b):null;},rgbToHex:function(b){var a=String(this).match(/\d{1,3}/g);
-return(a)?a.rgbToHex(b):null;},substitute:function(a,b){return String(this).replace(b||(/\\?\{([^{}]+)\}/g),function(d,c){if(d.charAt(0)=="\\"){return d.slice(1);
-}return(a[c]!=null)?a[c]:"";});}});Number.implement({limit:function(b,a){return Math.min(a,Math.max(b,this));},round:function(a){a=Math.pow(10,a||0).toFixed(a<0?-a:0);
-return Math.round(this*a)/a;},times:function(b,c){for(var a=0;a<this;a++){b.call(c,a,this);}},toFloat:function(){return parseFloat(this);},toInt:function(a){return parseInt(this,a||10);
-}});Number.alias("each","times");(function(b){var a={};b.each(function(c){if(!Number[c]){a[c]=function(){return Math[c].apply(null,[this].concat(Array.from(arguments)));
-};}});Number.implement(a);})(["abs","acos","asin","atan","atan2","ceil","cos","exp","floor","log","max","min","pow","sin","sqrt","tan"]);Function.extend({attempt:function(){for(var b=0,a=arguments.length;
-b<a;b++){try{return arguments[b]();}catch(c){}}return null;}});Function.implement({attempt:function(a,c){try{return this.apply(c,Array.from(a));}catch(b){}return null;
-},bind:function(e){var a=this,b=arguments.length>1?Array.slice(arguments,1):null,d=function(){};var c=function(){var g=e,h=arguments.length;if(this instanceof c){d.prototype=a.prototype;
-g=new d;}var f=(!b&&!h)?a.call(g):a.apply(g,b&&h?b.concat(Array.slice(arguments)):b||arguments);return g==e?f:g;};return c;},pass:function(b,c){var a=this;
-if(b!=null){b=Array.from(b);}return function(){return a.apply(c,b||arguments);};},delay:function(b,c,a){return setTimeout(this.pass((a==null?[]:a),c),b);
-},periodical:function(c,b,a){return setInterval(this.pass((a==null?[]:a),b),c);}});(function(){var a=Object.prototype.hasOwnProperty;Object.extend({subset:function(d,g){var f={};
-for(var e=0,b=g.length;e<b;e++){var c=g[e];if(c in d){f[c]=d[c];}}return f;},map:function(b,e,f){var d={};for(var c in b){if(a.call(b,c)){d[c]=e.call(f,b[c],c,b);
-}}return d;},filter:function(b,e,g){var d={};for(var c in b){var f=b[c];if(a.call(b,c)&&e.call(g,f,c,b)){d[c]=f;}}return d;},every:function(b,d,e){for(var c in b){if(a.call(b,c)&&!d.call(e,b[c],c)){return false;
-}}return true;},some:function(b,d,e){for(var c in b){if(a.call(b,c)&&d.call(e,b[c],c)){return true;}}return false;},keys:function(b){var d=[];for(var c in b){if(a.call(b,c)){d.push(c);
-}}return d;},values:function(c){var b=[];for(var d in c){if(a.call(c,d)){b.push(c[d]);}}return b;},getLength:function(b){return Object.keys(b).length;},keyOf:function(b,d){for(var c in b){if(a.call(b,c)&&b[c]===d){return c;
-}}return null;},contains:function(b,c){return Object.keyOf(b,c)!=null;},toQueryString:function(b,c){var d=[];Object.each(b,function(h,g){if(c){g=c+"["+g+"]";
-}var f;switch(typeOf(h)){case"object":f=Object.toQueryString(h,g);break;case"array":var e={};h.each(function(k,j){e[j]=k;});f=Object.toQueryString(e,g);
-break;default:f=g+"="+encodeURIComponent(h);}if(h!=null){d.push(f);}});return d.join("&");}});})();(function(){var k=this.document;var i=k.window=this;
-var b=1;this.$uid=(i.ActiveXObject)?function(e){return(e.uid||(e.uid=[b++]))[0];}:function(e){return e.uid||(e.uid=b++);};$uid(i);$uid(k);var a=navigator.userAgent.toLowerCase(),c=navigator.platform.toLowerCase(),j=a.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,"unknown",0],f=j[1]=="ie"&&k.documentMode;
-var o=this.Browser={extend:Function.prototype.extend,name:(j[1]=="version")?j[3]:j[1],version:f||parseFloat((j[1]=="opera"&&j[4])?j[4]:j[2]),Platform:{name:a.match(/ip(?:ad|od|hone)/)?"ios":(a.match(/(?:webos|android)/)||c.match(/mac|win|linux/)||["other"])[0]},Features:{xpath:!!(k.evaluate),air:!!(i.runtime),query:!!(k.querySelector),json:!!(i.JSON)},Plugins:{}};
-o[o.name]=true;o[o.name+parseInt(o.version,10)]=true;o.Platform[o.Platform.name]=true;o.Request=(function(){var q=function(){return new XMLHttpRequest();
-};var p=function(){return new ActiveXObject("MSXML2.XMLHTTP");};var e=function(){return new ActiveXObject("Microsoft.XMLHTTP");};return Function.attempt(function(){q();
-return q;},function(){p();return p;},function(){e();return e;});})();o.Features.xhr=!!(o.Request);var h=(Function.attempt(function(){return navigator.plugins["Shockwave Flash"].description;
-},function(){return new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version");})||"0 r0").match(/\d+/g);o.Plugins.Flash={version:Number(h[0]||"0."+h[1])||0,build:Number(h[2])||0};
-o.exec=function(p){if(!p){return p;}if(i.execScript){i.execScript(p);}else{var e=k.createElement("script");e.setAttribute("type","text/javascript");e.text=p;
-k.head.appendChild(e);k.head.removeChild(e);}return p;};String.implement("stripScripts",function(p){var e="";var q=this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi,function(r,s){e+=s+"\n";
-return"";});if(p===true){o.exec(e);}else{if(typeOf(p)=="function"){p(e,q);}}return q;});o.extend({Document:this.Document,Window:this.Window,Element:this.Element,Event:this.Event});
-this.Window=this.$constructor=new Type("Window",function(){});this.$family=Function.from("window").hide();Window.mirror(function(e,p){i[e]=p;});this.Document=k.$constructor=new Type("Document",function(){});
-k.$family=Function.from("document").hide();Document.mirror(function(e,p){k[e]=p;});k.html=k.documentElement;if(!k.head){k.head=k.getElementsByTagName("head")[0];
-}if(k.execCommand){try{k.execCommand("BackgroundImageCache",false,true);}catch(g){}}if(this.attachEvent&&!this.addEventListener){var d=function(){this.detachEvent("onunload",d);
-k.head=k.html=k.window=null;};this.attachEvent("onunload",d);}var m=Array.from;try{m(k.html.childNodes);}catch(g){Array.from=function(p){if(typeof p!="string"&&Type.isEnumerable(p)&&typeOf(p)!="array"){var e=p.length,q=new Array(e);
-while(e--){q[e]=p[e];}return q;}return m(p);};var l=Array.prototype,n=l.slice;["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice"].each(function(e){var p=l[e];
-Array[e]=function(q){return p.apply(Array.from(q),n.call(arguments,1));};});}})();(function(){var b={};var a=this.DOMEvent=new Type("DOMEvent",function(c,g){if(!g){g=window;
-}c=c||g.event;if(c.$extended){return c;}this.event=c;this.$extended=true;this.shift=c.shiftKey;this.control=c.ctrlKey;this.alt=c.altKey;this.meta=c.metaKey;
-var i=this.type=c.type;var h=c.target||c.srcElement;while(h&&h.nodeType==3){h=h.parentNode;}this.target=document.id(h);if(i.indexOf("key")==0){var d=this.code=(c.which||c.keyCode);
-this.key=b[d];if(i=="keydown"){if(d>111&&d<124){this.key="f"+(d-111);}else{if(d>95&&d<106){this.key=d-96;}}}if(this.key==null){this.key=String.fromCharCode(d).toLowerCase();
-}}else{if(i=="click"||i=="dblclick"||i=="contextmenu"||i=="DOMMouseScroll"||i.indexOf("mouse")==0){var j=g.document;j=(!j.compatMode||j.compatMode=="CSS1Compat")?j.html:j.body;
-this.page={x:(c.pageX!=null)?c.pageX:c.clientX+j.scrollLeft,y:(c.pageY!=null)?c.pageY:c.clientY+j.scrollTop};this.client={x:(c.pageX!=null)?c.pageX-g.pageXOffset:c.clientX,y:(c.pageY!=null)?c.pageY-g.pageYOffset:c.clientY};
-if(i=="DOMMouseScroll"||i=="mousewheel"){this.wheel=(c.wheelDelta)?c.wheelDelta/120:-(c.detail||0)/3;}this.rightClick=(c.which==3||c.button==2);if(i=="mouseover"||i=="mouseout"){var k=c.relatedTarget||c[(i=="mouseover"?"from":"to")+"Element"];
-while(k&&k.nodeType==3){k=k.parentNode;}this.relatedTarget=document.id(k);}}else{if(i.indexOf("touch")==0||i.indexOf("gesture")==0){this.rotation=c.rotation;
-this.scale=c.scale;this.targetTouches=c.targetTouches;this.changedTouches=c.changedTouches;var f=this.touches=c.touches;if(f&&f[0]){var e=f[0];this.page={x:e.pageX,y:e.pageY};
-this.client={x:e.clientX,y:e.clientY};}}}}if(!this.client){this.client={};}if(!this.page){this.page={};}});a.implement({stop:function(){return this.preventDefault().stopPropagation();
-},stopPropagation:function(){if(this.event.stopPropagation){this.event.stopPropagation();}else{this.event.cancelBubble=true;}return this;},preventDefault:function(){if(this.event.preventDefault){this.event.preventDefault();
-}else{this.event.returnValue=false;}return this;}});a.defineKey=function(d,c){b[d]=c;return this;};a.defineKeys=a.defineKey.overloadSetter(true);a.defineKeys({"38":"up","40":"down","37":"left","39":"right","27":"esc","32":"space","8":"backspace","9":"tab","46":"delete","13":"enter"});
-})();(function(){var a=this.Class=new Type("Class",function(h){if(instanceOf(h,Function)){h={initialize:h};}var g=function(){e(this);if(g.$prototyping){return this;
-}this.$caller=null;var i=(this.initialize)?this.initialize.apply(this,arguments):this;this.$caller=this.caller=null;return i;}.extend(this).implement(h);
-g.$constructor=a;g.prototype.$constructor=g;g.prototype.parent=c;return g;});var c=function(){if(!this.$caller){throw new Error('The method "parent" cannot be called.');
-}var g=this.$caller.$name,h=this.$caller.$owner.parent,i=(h)?h.prototype[g]:null;if(!i){throw new Error('The method "'+g+'" has no parent.');}return i.apply(this,arguments);
-};var e=function(g){for(var h in g){var j=g[h];switch(typeOf(j)){case"object":var i=function(){};i.prototype=j;g[h]=e(new i);break;case"array":g[h]=j.clone();
-break;}}return g;};var b=function(g,h,j){if(j.$origin){j=j.$origin;}var i=function(){if(j.$protected&&this.$caller==null){throw new Error('The method "'+h+'" cannot be called.');
-}var l=this.caller,m=this.$caller;this.caller=m;this.$caller=i;var k=j.apply(this,arguments);this.$caller=m;this.caller=l;return k;}.extend({$owner:g,$origin:j,$name:h});
-return i;};var f=function(h,i,g){if(a.Mutators.hasOwnProperty(h)){i=a.Mutators[h].call(this,i);if(i==null){return this;}}if(typeOf(i)=="function"){if(i.$hidden){return this;
-}this.prototype[h]=(g)?i:b(this,h,i);}else{Object.merge(this.prototype,h,i);}return this;};var d=function(g){g.$prototyping=true;var h=new g;delete g.$prototyping;
-return h;};a.implement("implement",f.overloadSetter());a.Mutators={Extends:function(g){this.parent=g;this.prototype=d(g);},Implements:function(g){Array.from(g).each(function(j){var h=new j;
-for(var i in h){f.call(this,i,h[i],true);}},this);}};})();(function(){this.Chain=new Class({$chain:[],chain:function(){this.$chain.append(Array.flatten(arguments));
-return this;},callChain:function(){return(this.$chain.length)?this.$chain.shift().apply(this,arguments):false;},clearChain:function(){this.$chain.empty();
-return this;}});var a=function(b){return b.replace(/^on([A-Z])/,function(c,d){return d.toLowerCase();});};this.Events=new Class({$events:{},addEvent:function(d,c,b){d=a(d);
-this.$events[d]=(this.$events[d]||[]).include(c);if(b){c.internal=true;}return this;},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]);}return this;
-},fireEvent:function(e,c,b){e=a(e);var d=this.$events[e];if(!d){return this;}c=Array.from(c);d.each(function(f){if(b){f.delay(b,this,c);}else{f.apply(this,c);
-}},this);return this;},removeEvent:function(e,d){e=a(e);var c=this.$events[e];if(c&&!d.internal){var b=c.indexOf(d);if(b!=-1){delete c[b];}}return this;
-},removeEvents:function(d){var e;if(typeOf(d)=="object"){for(e in d){this.removeEvent(e,d[e]);}return this;}if(d){d=a(d);}for(e in this.$events){if(d&&d!=e){continue;
-}var c=this.$events[e];for(var b=c.length;b--;){if(b in c){this.removeEvent(e,c[b]);}}}return this;}});this.Options=new Class({setOptions:function(){var b=this.options=Object.merge.apply(null,[{},this.options].append(arguments));
-if(this.addEvent){for(var c in b){if(typeOf(b[c])!="function"||!(/^on[A-Z]/).test(c)){continue;}this.addEvent(c,b[c]);delete b[c];}}return this;}});})();
-(function(){var k,n,l,g,a={},c={},m=/\\/g;var e=function(q,p){if(q==null){return null;}if(q.Slick===true){return q;}q=(""+q).replace(/^\s+|\s+$/g,"");g=!!p;
-var o=(g)?c:a;if(o[q]){return o[q];}k={Slick:true,expressions:[],raw:q,reverse:function(){return e(this.raw,true);}};n=-1;while(q!=(q=q.replace(j,b))){}k.length=k.expressions.length;
-return o[k.raw]=(g)?h(k):k;};var i=function(o){if(o==="!"){return" ";}else{if(o===" "){return"!";}else{if((/^!/).test(o)){return o.replace(/^!/,"");}else{return"!"+o;
-}}}};var h=function(u){var r=u.expressions;for(var p=0;p<r.length;p++){var t=r[p];var q={parts:[],tag:"*",combinator:i(t[0].combinator)};for(var o=0;o<t.length;
-o++){var s=t[o];if(!s.reverseCombinator){s.reverseCombinator=" ";}s.combinator=s.reverseCombinator;delete s.reverseCombinator;}t.reverse().push(q);}return u;
-};var f=function(o){return o.replace(/[-[\]{}()*+?.\\^$|,#\s]/g,function(p){return"\\"+p;});};var j=new RegExp("^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)".replace(/<combinator>/,"["+f(">+~`!@$%^&={}\\;</")+"]").replace(/<unicode>/g,"(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])").replace(/<unicode1>/g,"(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])"));
-function b(x,s,D,z,r,C,q,B,A,y,u,F,G,v,p,w){if(s||n===-1){k.expressions[++n]=[];l=-1;if(s){return"";}}if(D||z||l===-1){D=D||" ";var t=k.expressions[n];
-if(g&&t[l]){t[l].reverseCombinator=i(D);}t[++l]={combinator:D,tag:"*"};}var o=k.expressions[n][l];if(r){o.tag=r.replace(m,"");}else{if(C){o.id=C.replace(m,"");
-}else{if(q){q=q.replace(m,"");if(!o.classList){o.classList=[];}if(!o.classes){o.classes=[];}o.classList.push(q);o.classes.push({value:q,regexp:new RegExp("(^|\\s)"+f(q)+"(\\s|$)")});
-}else{if(G){w=w||p;w=w?w.replace(m,""):null;if(!o.pseudos){o.pseudos=[];}o.pseudos.push({key:G.replace(m,""),value:w,type:F.length==1?"class":"element"});
-}else{if(B){B=B.replace(m,"");u=(u||"").replace(m,"");var E,H;switch(A){case"^=":H=new RegExp("^"+f(u));break;case"$=":H=new RegExp(f(u)+"$");break;case"~=":H=new RegExp("(^|\\s)"+f(u)+"(\\s|$)");
-break;case"|=":H=new RegExp("^"+f(u)+"(-|$)");break;case"=":E=function(I){return u==I;};break;case"*=":E=function(I){return I&&I.indexOf(u)>-1;};break;
-case"!=":E=function(I){return u!=I;};break;default:E=function(I){return !!I;};}if(u==""&&(/^[*$^]=$/).test(A)){E=function(){return false;};}if(!E){E=function(I){return I&&H.test(I);
-};}if(!o.attributes){o.attributes=[];}o.attributes.push({key:B,operator:A,value:u,test:E});}}}}}return"";}var d=(this.Slick||{});d.parse=function(o){return e(o);
-};d.escapeRegExp=f;if(!this.Slick){this.Slick=d;}}).apply((typeof exports!="undefined")?exports:this);(function(){var k={},m={},d=Object.prototype.toString;
-k.isNativeCode=function(c){return(/\{\s*\[native code\]\s*\}/).test(""+c);};k.isXML=function(c){return(!!c.xmlVersion)||(!!c.xml)||(d.call(c)=="[object XMLDocument]")||(c.nodeType==9&&c.documentElement.nodeName!="HTML");
-};k.setDocument=function(x){var u=x.nodeType;if(u==9){}else{if(u){x=x.ownerDocument;}else{if(x.navigator){x=x.document;}else{return;}}}if(this.document===x){return;
-}this.document=x;var z=x.documentElement,v=this.getUIDXML(z),p=m[v],B;if(p){for(B in p){this[B]=p[B];}return;}p=m[v]={};p.root=z;p.isXMLDocument=this.isXML(x);
-p.brokenStarGEBTN=p.starSelectsClosedQSA=p.idGetsName=p.brokenMixedCaseQSA=p.brokenGEBCN=p.brokenCheckedQSA=p.brokenEmptyAttributeQSA=p.isHTMLDocument=p.nativeMatchesSelector=false;
-var n,o,y,r,s;var t,c="slick_uniqueid";var A=x.createElement("div");var q=x.body||x.getElementsByTagName("body")[0]||z;q.appendChild(A);try{A.innerHTML='<a id="'+c+'"></a>';
-p.isHTMLDocument=!!x.getElementById(c);}catch(w){}if(p.isHTMLDocument){A.style.display="none";A.appendChild(x.createComment(""));o=(A.getElementsByTagName("*").length>1);
-try{A.innerHTML="foo</foo>";t=A.getElementsByTagName("*");n=(t&&!!t.length&&t[0].nodeName.charAt(0)=="/");}catch(w){}p.brokenStarGEBTN=o||n;try{A.innerHTML='<a name="'+c+'"></a><b id="'+c+'"></b>';
-p.idGetsName=x.getElementById(c)===A.firstChild;}catch(w){}if(A.getElementsByClassName){try{A.innerHTML='<a class="f"></a><a class="b"></a>';A.getElementsByClassName("b").length;
-A.firstChild.className="b";r=(A.getElementsByClassName("b").length!=2);}catch(w){}try{A.innerHTML='<a class="a"></a><a class="f b a"></a>';y=(A.getElementsByClassName("a").length!=2);
-}catch(w){}p.brokenGEBCN=r||y;}if(A.querySelectorAll){try{A.innerHTML="foo</foo>";t=A.querySelectorAll("*");p.starSelectsClosedQSA=(t&&!!t.length&&t[0].nodeName.charAt(0)=="/");
-}catch(w){}try{A.innerHTML='<a class="MiX"></a>';p.brokenMixedCaseQSA=!A.querySelectorAll(".MiX").length;}catch(w){}try{A.innerHTML='<select><option selected="selected">a</option></select>';
-p.brokenCheckedQSA=(A.querySelectorAll(":checked").length==0);}catch(w){}try{A.innerHTML='<a class=""></a>';p.brokenEmptyAttributeQSA=(A.querySelectorAll('[class*=""]').length!=0);
-}catch(w){}}try{A.innerHTML='<form action="s"><input id="action"/></form>';s=(A.firstChild.getAttribute("action")!="s");}catch(w){}p.nativeMatchesSelector=z.matchesSelector||z.mozMatchesSelector||z.webkitMatchesSelector;
-if(p.nativeMatchesSelector){try{p.nativeMatchesSelector.call(z,":slick");p.nativeMatchesSelector=null;}catch(w){}}}try{z.slick_expando=1;delete z.slick_expando;
-p.getUID=this.getUIDHTML;}catch(w){p.getUID=this.getUIDXML;}q.removeChild(A);A=t=q=null;p.getAttribute=(p.isHTMLDocument&&s)?function(E,C){var F=this.attributeGetters[C];
-if(F){return F.call(E);}var D=E.getAttributeNode(C);return(D)?D.nodeValue:null;}:function(D,C){var E=this.attributeGetters[C];return(E)?E.call(D):D.getAttribute(C);
-};p.hasAttribute=(z&&this.isNativeCode(z.hasAttribute))?function(D,C){return D.hasAttribute(C);}:function(D,C){D=D.getAttributeNode(C);return !!(D&&(D.specified||D.nodeValue));
-};p.contains=(z&&this.isNativeCode(z.contains))?function(C,D){return C.contains(D);}:(z&&z.compareDocumentPosition)?function(C,D){return C===D||!!(C.compareDocumentPosition(D)&16);
-}:function(C,D){if(D){do{if(D===C){return true;}}while((D=D.parentNode));}return false;};p.documentSorter=(z.compareDocumentPosition)?function(D,C){if(!D.compareDocumentPosition||!C.compareDocumentPosition){return 0;
-}return D.compareDocumentPosition(C)&4?-1:D===C?0:1;}:("sourceIndex" in z)?function(D,C){if(!D.sourceIndex||!C.sourceIndex){return 0;}return D.sourceIndex-C.sourceIndex;
-}:(x.createRange)?function(F,D){if(!F.ownerDocument||!D.ownerDocument){return 0;}var E=F.ownerDocument.createRange(),C=D.ownerDocument.createRange();E.setStart(F,0);
-E.setEnd(F,0);C.setStart(D,0);C.setEnd(D,0);return E.compareBoundaryPoints(Range.START_TO_END,C);}:null;z=null;for(B in p){this[B]=p[B];}};var f=/^([#.]?)((?:[\w-]+|\*))$/,h=/\[.+[*$^]=(?:""|'')?\]/,g={};
-k.search=function(U,z,H,s){var p=this.found=(s)?null:(H||[]);if(!U){return p;}else{if(U.navigator){U=U.document;}else{if(!U.nodeType){return p;}}}var F,O,V=this.uniques={},I=!!(H&&H.length),y=(U.nodeType==9);
-if(this.document!==(y?U:U.ownerDocument)){this.setDocument(U);}if(I){for(O=p.length;O--;){V[this.getUID(p[O])]=true;}}if(typeof z=="string"){var r=z.match(f);
-simpleSelectors:if(r){var u=r[1],v=r[2],A,E;if(!u){if(v=="*"&&this.brokenStarGEBTN){break simpleSelectors;}E=U.getElementsByTagName(v);if(s){return E[0]||null;
-}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{if(u=="#"){if(!this.isHTMLDocument||!y){break simpleSelectors;}A=U.getElementById(v);
-if(!A){return p;}if(this.idGetsName&&A.getAttributeNode("id").nodeValue!=v){break simpleSelectors;}if(s){return A||null;}if(!(I&&V[this.getUID(A)])){p.push(A);
-}}else{if(u=="."){if(!this.isHTMLDocument||((!U.getElementsByClassName||this.brokenGEBCN)&&U.querySelectorAll)){break simpleSelectors;}if(U.getElementsByClassName&&!this.brokenGEBCN){E=U.getElementsByClassName(v);
-if(s){return E[0]||null;}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{var T=new RegExp("(^|\\s)"+e.escapeRegExp(v)+"(\\s|$)");E=U.getElementsByTagName("*");
-for(O=0;A=E[O++];){className=A.className;if(!(className&&T.test(className))){continue;}if(s){return A;}if(!(I&&V[this.getUID(A)])){p.push(A);}}}}}}if(I){this.sort(p);
-}return(s)?null:p;}querySelector:if(U.querySelectorAll){if(!this.isHTMLDocument||g[z]||this.brokenMixedCaseQSA||(this.brokenCheckedQSA&&z.indexOf(":checked")>-1)||(this.brokenEmptyAttributeQSA&&h.test(z))||(!y&&z.indexOf(",")>-1)||e.disableQSA){break querySelector;
-}var S=z,x=U;if(!y){var C=x.getAttribute("id"),t="slickid__";x.setAttribute("id",t);S="#"+t+" "+S;U=x.parentNode;}try{if(s){return U.querySelector(S)||null;
-}else{E=U.querySelectorAll(S);}}catch(Q){g[z]=1;break querySelector;}finally{if(!y){if(C){x.setAttribute("id",C);}else{x.removeAttribute("id");}U=x;}}if(this.starSelectsClosedQSA){for(O=0;
-A=E[O++];){if(A.nodeName>"@"&&!(I&&V[this.getUID(A)])){p.push(A);}}}else{for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}if(I){this.sort(p);
-}return p;}F=this.Slick.parse(z);if(!F.length){return p;}}else{if(z==null){return p;}else{if(z.Slick){F=z;}else{if(this.contains(U.documentElement||U,z)){(p)?p.push(z):p=z;
-return p;}else{return p;}}}}this.posNTH={};this.posNTHLast={};this.posNTHType={};this.posNTHTypeLast={};this.push=(!I&&(s||(F.length==1&&F.expressions[0].length==1)))?this.pushArray:this.pushUID;
-if(p==null){p=[];}var M,L,K;var B,J,D,c,q,G,W;var N,P,o,w,R=F.expressions;search:for(O=0;(P=R[O]);O++){for(M=0;(o=P[M]);M++){B="combinator:"+o.combinator;
-if(!this[B]){continue search;}J=(this.isXMLDocument)?o.tag:o.tag.toUpperCase();D=o.id;c=o.classList;q=o.classes;G=o.attributes;W=o.pseudos;w=(M===(P.length-1));
-this.bitUniques={};if(w){this.uniques=V;this.found=p;}else{this.uniques={};this.found=[];}if(M===0){this[B](U,J,D,q,G,W,c);if(s&&w&&p.length){break search;
-}}else{if(s&&w){for(L=0,K=N.length;L<K;L++){this[B](N[L],J,D,q,G,W,c);if(p.length){break search;}}}else{for(L=0,K=N.length;L<K;L++){this[B](N[L],J,D,q,G,W,c);
-}}}N=this.found;}}if(I||(F.expressions.length>1)){this.sort(p);}return(s)?(p[0]||null):p;};k.uidx=1;k.uidk="slick-uniqueid";k.getUIDXML=function(n){var c=n.getAttribute(this.uidk);
-if(!c){c=this.uidx++;n.setAttribute(this.uidk,c);}return c;};k.getUIDHTML=function(c){return c.uniqueNumber||(c.uniqueNumber=this.uidx++);};k.sort=function(c){if(!this.documentSorter){return c;
-}c.sort(this.documentSorter);return c;};k.cacheNTH={};k.matchNTH=/^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;k.parseNTHArgument=function(q){var o=q.match(this.matchNTH);
-if(!o){return false;}var p=o[2]||false;var n=o[1]||1;if(n=="-"){n=-1;}var c=+o[3]||0;o=(p=="n")?{a:n,b:c}:(p=="odd")?{a:2,b:1}:(p=="even")?{a:2,b:0}:{a:0,b:n};
-return(this.cacheNTH[q]=o);};k.createNTHPseudo=function(p,n,c,o){return function(s,q){var u=this.getUID(s);if(!this[c][u]){var A=s.parentNode;if(!A){return false;
-}var r=A[p],t=1;if(o){var z=s.nodeName;do{if(r.nodeName!=z){continue;}this[c][this.getUID(r)]=t++;}while((r=r[n]));}else{do{if(r.nodeType!=1){continue;
-}this[c][this.getUID(r)]=t++;}while((r=r[n]));}}q=q||"n";var v=this.cacheNTH[q]||this.parseNTHArgument(q);if(!v){return false;}var y=v.a,x=v.b,w=this[c][u];
-if(y==0){return x==w;}if(y>0){if(w<x){return false;}}else{if(x<w){return false;}}return((w-x)%y)==0;};};k.pushArray=function(p,c,r,o,n,q){if(this.matchSelector(p,c,r,o,n,q)){this.found.push(p);
-}};k.pushUID=function(q,c,s,p,n,r){var o=this.getUID(q);if(!this.uniques[o]&&this.matchSelector(q,c,s,p,n,r)){this.uniques[o]=true;this.found.push(q);}};
-k.matchNode=function(n,o){if(this.isHTMLDocument&&this.nativeMatchesSelector){try{return this.nativeMatchesSelector.call(n,o.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g,'[$1="$2"]'));
-}catch(u){}}var t=this.Slick.parse(o);if(!t){return true;}var r=t.expressions,s=0,q;for(q=0;(currentExpression=r[q]);q++){if(currentExpression.length==1){var p=currentExpression[0];
-if(this.matchSelector(n,(this.isXMLDocument)?p.tag:p.tag.toUpperCase(),p.id,p.classes,p.attributes,p.pseudos)){return true;}s++;}}if(s==t.length){return false;
-}var c=this.search(this.document,t),v;for(q=0;v=c[q++];){if(v===n){return true;}}return false;};k.matchPseudo=function(q,c,p){var n="pseudo:"+c;if(this[n]){return this[n](q,p);
-}var o=this.getAttribute(q,c);return(p)?p==o:!!o;};k.matchSelector=function(o,v,c,p,q,s){if(v){var t=(this.isXMLDocument)?o.nodeName:o.nodeName.toUpperCase();
-if(v=="*"){if(t<"@"){return false;}}else{if(t!=v){return false;}}}if(c&&o.getAttribute("id")!=c){return false;}var r,n,u;if(p){for(r=p.length;r--;){u=o.getAttribute("class")||o.className;
-if(!(u&&p[r].regexp.test(u))){return false;}}}if(q){for(r=q.length;r--;){n=q[r];if(n.operator?!n.test(this.getAttribute(o,n.key)):!this.hasAttribute(o,n.key)){return false;
-}}}if(s){for(r=s.length;r--;){n=s[r];if(!this.matchPseudo(o,n.key,n.value)){return false;}}}return true;};var j={" ":function(q,w,n,r,s,u,p){var t,v,o;
-if(this.isHTMLDocument){getById:if(n){v=this.document.getElementById(n);if((!v&&q.all)||(this.idGetsName&&v&&v.getAttributeNode("id").nodeValue!=n)){o=q.all[n];
-if(!o){return;}if(!o[0]){o=[o];}for(t=0;v=o[t++];){var c=v.getAttributeNode("id");if(c&&c.nodeValue==n){this.push(v,w,null,r,s,u);break;}}return;}if(!v){if(this.contains(this.root,q)){return;
-}else{break getById;}}else{if(this.document!==q&&!this.contains(q,v)){return;}}this.push(v,w,null,r,s,u);return;}getByClass:if(r&&q.getElementsByClassName&&!this.brokenGEBCN){o=q.getElementsByClassName(p.join(" "));
-if(!(o&&o.length)){break getByClass;}for(t=0;v=o[t++];){this.push(v,w,n,null,s,u);}return;}}getByTag:{o=q.getElementsByTagName(w);if(!(o&&o.length)){break getByTag;
-}if(!this.brokenStarGEBTN){w=null;}for(t=0;v=o[t++];){this.push(v,w,n,r,s,u);}}},">":function(p,c,r,o,n,q){if((p=p.firstChild)){do{if(p.nodeType==1){this.push(p,c,r,o,n,q);
-}}while((p=p.nextSibling));}},"+":function(p,c,r,o,n,q){while((p=p.nextSibling)){if(p.nodeType==1){this.push(p,c,r,o,n,q);break;}}},"^":function(p,c,r,o,n,q){p=p.firstChild;
-if(p){if(p.nodeType==1){this.push(p,c,r,o,n,q);}else{this["combinator:+"](p,c,r,o,n,q);}}},"~":function(q,c,s,p,n,r){while((q=q.nextSibling)){if(q.nodeType!=1){continue;
-}var o=this.getUID(q);if(this.bitUniques[o]){break;}this.bitUniques[o]=true;this.push(q,c,s,p,n,r);}},"++":function(p,c,r,o,n,q){this["combinator:+"](p,c,r,o,n,q);
-this["combinator:!+"](p,c,r,o,n,q);},"~~":function(p,c,r,o,n,q){this["combinator:~"](p,c,r,o,n,q);this["combinator:!~"](p,c,r,o,n,q);},"!":function(p,c,r,o,n,q){while((p=p.parentNode)){if(p!==this.document){this.push(p,c,r,o,n,q);
-}}},"!>":function(p,c,r,o,n,q){p=p.parentNode;if(p!==this.document){this.push(p,c,r,o,n,q);}},"!+":function(p,c,r,o,n,q){while((p=p.previousSibling)){if(p.nodeType==1){this.push(p,c,r,o,n,q);
-break;}}},"!^":function(p,c,r,o,n,q){p=p.lastChild;if(p){if(p.nodeType==1){this.push(p,c,r,o,n,q);}else{this["combinator:!+"](p,c,r,o,n,q);}}},"!~":function(q,c,s,p,n,r){while((q=q.previousSibling)){if(q.nodeType!=1){continue;
-}var o=this.getUID(q);if(this.bitUniques[o]){break;}this.bitUniques[o]=true;this.push(q,c,s,p,n,r);}}};for(var i in j){k["combinator:"+i]=j[i];}var l={empty:function(c){var n=c.firstChild;
-return !(n&&n.nodeType==1)&&!(c.innerText||c.textContent||"").length;},not:function(c,n){return !this.matchNode(c,n);},contains:function(c,n){return(c.innerText||c.textContent||"").indexOf(n)>-1;
-},"first-child":function(c){while((c=c.previousSibling)){if(c.nodeType==1){return false;}}return true;},"last-child":function(c){while((c=c.nextSibling)){if(c.nodeType==1){return false;
-}}return true;},"only-child":function(o){var n=o;while((n=n.previousSibling)){if(n.nodeType==1){return false;}}var c=o;while((c=c.nextSibling)){if(c.nodeType==1){return false;
-}}return true;},"nth-child":k.createNTHPseudo("firstChild","nextSibling","posNTH"),"nth-last-child":k.createNTHPseudo("lastChild","previousSibling","posNTHLast"),"nth-of-type":k.createNTHPseudo("firstChild","nextSibling","posNTHType",true),"nth-last-of-type":k.createNTHPseudo("lastChild","previousSibling","posNTHTypeLast",true),index:function(n,c){return this["pseudo:nth-child"](n,""+c+1);
-},even:function(c){return this["pseudo:nth-child"](c,"2n");},odd:function(c){return this["pseudo:nth-child"](c,"2n+1");},"first-of-type":function(c){var n=c.nodeName;
-while((c=c.previousSibling)){if(c.nodeName==n){return false;}}return true;},"last-of-type":function(c){var n=c.nodeName;while((c=c.nextSibling)){if(c.nodeName==n){return false;
-}}return true;},"only-of-type":function(o){var n=o,p=o.nodeName;while((n=n.previousSibling)){if(n.nodeName==p){return false;}}var c=o;while((c=c.nextSibling)){if(c.nodeName==p){return false;
-}}return true;},enabled:function(c){return !c.disabled;},disabled:function(c){return c.disabled;},checked:function(c){return c.checked||c.selected;},focus:function(c){return this.isHTMLDocument&&this.document.activeElement===c&&(c.href||c.type||this.hasAttribute(c,"tabindex"));
-},root:function(c){return(c===this.root);},selected:function(c){return c.selected;}};for(var b in l){k["pseudo:"+b]=l[b];}var a=k.attributeGetters={"class":function(){return this.getAttribute("class")||this.className;
-},"for":function(){return("htmlFor" in this)?this.htmlFor:this.getAttribute("for");},href:function(){return("href" in this)?this.getAttribute("href",2):this.getAttribute("href");
-},style:function(){return(this.style)?this.style.cssText:this.getAttribute("style");},tabindex:function(){var c=this.getAttributeNode("tabindex");return(c&&c.specified)?c.nodeValue:null;
-},type:function(){return this.getAttribute("type");},maxlength:function(){var c=this.getAttributeNode("maxLength");return(c&&c.specified)?c.nodeValue:null;
-}};a.MAXLENGTH=a.maxLength=a.maxlength;var e=k.Slick=(this.Slick||{});e.version="1.1.6";e.search=function(n,o,c){return k.search(n,o,c);};e.find=function(c,n){return k.search(c,n,null,true);
-};e.contains=function(c,n){k.setDocument(c);return k.contains(c,n);};e.getAttribute=function(n,c){k.setDocument(n);return k.getAttribute(n,c);};e.hasAttribute=function(n,c){k.setDocument(n);
-return k.hasAttribute(n,c);};e.match=function(n,c){if(!(n&&c)){return false;}if(!c||c===n){return true;}k.setDocument(n);return k.matchNode(n,c);};e.defineAttributeGetter=function(c,n){k.attributeGetters[c]=n;
-return this;};e.lookupAttributeGetter=function(c){return k.attributeGetters[c];};e.definePseudo=function(c,n){k["pseudo:"+c]=function(p,o){return n.call(p,o);
-};return this;};e.lookupPseudo=function(c){var n=k["pseudo:"+c];if(n){return function(o){return n.call(this,o);};}return null;};e.override=function(n,c){k.override(n,c);
-return this;};e.isXML=k.isXML;e.uidOf=function(c){return k.getUIDHTML(c);};if(!this.Slick){this.Slick=e;}}).apply((typeof exports!="undefined")?exports:this);
-var Element=function(b,g){var h=Element.Constructors[b];if(h){return h(g);}if(typeof b!="string"){return document.id(b).set(g);}if(!g){g={};}if(!(/^[\w-]+$/).test(b)){var e=Slick.parse(b).expressions[0][0];
-b=(e.tag=="*")?"div":e.tag;if(e.id&&g.id==null){g.id=e.id;}var d=e.attributes;if(d){for(var a,f=0,c=d.length;f<c;f++){a=d[f];if(g[a.key]!=null){continue;
-}if(a.value!=null&&a.operator=="="){g[a.key]=a.value;}else{if(!a.value&&!a.operator){g[a.key]=true;}}}}if(e.classList&&g["class"]==null){g["class"]=e.classList.join(" ");
-}}return document.newElement(b,g);};if(Browser.Element){Element.prototype=Browser.Element.prototype;}new Type("Element",Element).mirror(function(a){if(Array.prototype[a]){return;
-}var b={};b[a]=function(){var h=[],e=arguments,j=true;for(var g=0,d=this.length;g<d;g++){var f=this[g],c=h[g]=f[a].apply(f,e);j=(j&&typeOf(c)=="element");
-}return(j)?new Elements(h):h;};Elements.implement(b);});if(!Browser.Element){Element.parent=Object;Element.Prototype={"$family":Function.from("element").hide()};
-Element.mirror(function(a,b){Element.Prototype[a]=b;});}Element.Constructors={};var IFrame=new Type("IFrame",function(){var e=Array.link(arguments,{properties:Type.isObject,iframe:function(f){return(f!=null);
-}});var c=e.properties||{},b;if(e.iframe){b=document.id(e.iframe);}var d=c.onload||function(){};delete c.onload;c.id=c.name=[c.id,c.name,b?(b.id||b.name):"IFrame_"+String.uniqueID()].pick();
-b=new Element(b||"iframe",c);var a=function(){d.call(b.contentWindow);};if(window.frames[c.id]){a();}else{b.addListener("load",a);}return b;});var Elements=this.Elements=function(a){if(a&&a.length){var e={},d;
-for(var c=0;d=a[c++];){var b=Slick.uidOf(d);if(!e[b]){e[b]=true;this.push(d);}}}};Elements.prototype={length:0};Elements.parent=Array;new Type("Elements",Elements).implement({filter:function(a,b){if(!a){return this;
-}return new Elements(Array.filter(this,(typeOf(a)=="string")?function(c){return c.match(a);}:a,b));}.protect(),push:function(){var d=this.length;for(var b=0,a=arguments.length;
-b<a;b++){var c=document.id(arguments[b]);if(c){this[d++]=c;}}return(this.length=d);}.protect(),unshift:function(){var b=[];for(var c=0,a=arguments.length;
-c<a;c++){var d=document.id(arguments[c]);if(d){b.push(d);}}return Array.prototype.unshift.apply(this,b);}.protect(),concat:function(){var b=new Elements(this);
-for(var c=0,a=arguments.length;c<a;c++){var d=arguments[c];if(Type.isEnumerable(d)){b.append(d);}else{b.push(d);}}return b;}.protect(),append:function(c){for(var b=0,a=c.length;
-b<a;b++){this.push(c[b]);}return this;}.protect(),empty:function(){while(this.length){delete this[--this.length];}return this;}.protect()});(function(){var g=Array.prototype.splice,b={"0":0,"1":1,length:2};
-g.call(b,1,1);if(b[1]==1){Elements.implement("splice",function(){var h=this.length;var e=g.apply(this,arguments);while(h>=this.length){delete this[h--];
-}return e;}.protect());}Elements.implement(Array.prototype);Array.mirror(Elements);var f;try{var a=document.createElement("<input name=x>");f=(a.name=="x");
-}catch(c){}var d=function(e){return(""+e).replace(/&/g,"&amp;").replace(/"/g,"&quot;");};Document.implement({newElement:function(e,h){if(h&&h.checked!=null){h.defaultChecked=h.checked;
-}if(f&&h){e="<"+e;if(h.name){e+=' name="'+d(h.name)+'"';}if(h.type){e+=' type="'+d(h.type)+'"';}e+=">";delete h.name;delete h.type;}return this.id(this.createElement(e)).set(h);
-}});})();Document.implement({newTextNode:function(a){return this.createTextNode(a);},getDocument:function(){return this;},getWindow:function(){return this.window;
-},id:(function(){var a={string:function(d,c,b){d=Slick.find(b,"#"+d.replace(/(\W)/g,"\\$1"));return(d)?a.element(d,c):null;},element:function(b,c){$uid(b);
-if(!c&&!b.$family&&!(/^(?:object|embed)$/i).test(b.tagName)){Object.append(b,Element.Prototype);}return b;},object:function(c,d,b){if(c.toElement){return a.element(c.toElement(b),d);
-}return null;}};a.textnode=a.whitespace=a.window=a.document=function(b){return b;};return function(c,e,d){if(c&&c.$family&&c.uid){return c;}var b=typeOf(c);
-return(a[b])?a[b](c,e,d||document):null;};})()});if(window.$==null){Window.implement("$",function(a,b){return document.id(a,b,this.document);});}Window.implement({getDocument:function(){return this.document;
-},getWindow:function(){return this;}});[Document,Element].invoke("implement",{getElements:function(a){return Slick.search(this,a,new Elements);},getElement:function(a){return document.id(Slick.find(this,a));
-}});var contains={contains:function(a){return Slick.contains(this,a);}};if(!document.contains){Document.implement(contains);}if(!document.createElement("div").contains){Element.implement(contains);
-}var injectCombinator=function(d,c){if(!d){return c;}d=Object.clone(Slick.parse(d));var b=d.expressions;for(var a=b.length;a--;){b[a][0].combinator=c;}return d;
-};Object.forEach({getNext:"~",getPrevious:"!~",getParent:"!"},function(a,b){Element.implement(b,function(c){return this.getElement(injectCombinator(c,a));
-});});Object.forEach({getAllNext:"~",getAllPrevious:"!~",getSiblings:"~~",getChildren:">",getParents:"!"},function(a,b){Element.implement(b,function(c){return this.getElements(injectCombinator(c,a));
-});});Element.implement({getFirst:function(a){return document.id(Slick.search(this,injectCombinator(a,">"))[0]);},getLast:function(a){return document.id(Slick.search(this,injectCombinator(a,">")).getLast());
-},getWindow:function(){return this.ownerDocument.window;},getDocument:function(){return this.ownerDocument;},getElementById:function(a){return document.id(Slick.find(this,"#"+(""+a).replace(/(\W)/g,"\\$1")));
-},match:function(a){return !a||Slick.match(this,a);}});if(window.$$==null){Window.implement("$$",function(a){if(arguments.length==1){if(typeof a=="string"){return Slick.search(this.document,a,new Elements);
-}else{if(Type.isEnumerable(a)){return new Elements(a);}}}return new Elements(arguments);});}(function(){var b={before:function(n,m){var o=m.parentNode;
-if(o){o.insertBefore(n,m);}},after:function(n,m){var o=m.parentNode;if(o){o.insertBefore(n,m.nextSibling);}},bottom:function(n,m){m.appendChild(n);},top:function(n,m){m.insertBefore(n,m.firstChild);
-}};b.inside=b.bottom;var k={},d={};var i={};Array.forEach(["type","value","defaultValue","accessKey","cellPadding","cellSpacing","colSpan","frameBorder","readOnly","rowSpan","tabIndex","useMap"],function(m){i[m.toLowerCase()]=m;
-});Object.append(i,{html:"innerHTML",text:(function(){var m=document.createElement("div");return(m.textContent==null)?"innerText":"textContent";})()});
-Object.forEach(i,function(n,m){d[m]=function(o,p){o[n]=p;};k[m]=function(o){return o[n];};});var a=["compact","nowrap","ismap","declare","noshade","checked","disabled","readOnly","multiple","selected","noresize","defer","defaultChecked","autofocus","controls","autoplay","loop"];
-var h={};Array.forEach(a,function(m){var n=m.toLowerCase();h[n]=m;d[n]=function(o,p){o[m]=!!p;};k[n]=function(o){return !!o[m];};});Object.append(d,{"class":function(m,n){("className" in m)?m.className=n:m.setAttribute("class",n);
-},"for":function(m,n){("htmlFor" in m)?m.htmlFor=n:m.setAttribute("for",n);},style:function(m,n){(m.style)?m.style.cssText=n:m.setAttribute("style",n);
-}});Element.implement({setProperty:function(n,o){var m=n.toLowerCase();if(o==null){if(!h[m]){this.removeAttribute(n);return this;}o=false;}var p=d[m];if(p){p(this,o);
-}else{this.setAttribute(n,o);}return this;},setProperties:function(m){for(var n in m){this.setProperty(n,m[n]);}return this;},getProperty:function(o){var n=k[o.toLowerCase()];
-if(n){return n(this);}var m=Slick.getAttribute(this,o);return(!m&&!Slick.hasAttribute(this,o))?null:m;},getProperties:function(){var m=Array.from(arguments);
-return m.map(this.getProperty,this).associate(m);},removeProperty:function(m){return this.setProperty(m,null);},removeProperties:function(){Array.each(arguments,this.removeProperty,this);
-return this;},set:function(o,n){var m=Element.Properties[o];(m&&m.set)?m.set.call(this,n):this.setProperty(o,n);}.overloadSetter(),get:function(n){var m=Element.Properties[n];
-return(m&&m.get)?m.get.apply(this):this.getProperty(n);}.overloadGetter(),erase:function(n){var m=Element.Properties[n];(m&&m.erase)?m.erase.apply(this):this.removeProperty(n);
-return this;},hasClass:function(m){return this.className.clean().contains(m," ");},addClass:function(m){if(!this.hasClass(m)){this.className=(this.className+" "+m).clean();
-}return this;},removeClass:function(m){this.className=this.className.replace(new RegExp("(^|\\s)"+m+"(?:\\s|$)"),"$1");return this;},toggleClass:function(m,n){if(n==null){n=!this.hasClass(m);
-}return(n)?this.addClass(m):this.removeClass(m);},adopt:function(){var p=this,m,r=Array.flatten(arguments),q=r.length;if(q>1){p=m=document.createDocumentFragment();
-}for(var o=0;o<q;o++){var n=document.id(r[o],true);if(n){p.appendChild(n);}}if(m){this.appendChild(m);}return this;},appendText:function(n,m){return this.grab(this.getDocument().newTextNode(n),m);
-},grab:function(n,m){b[m||"bottom"](document.id(n,true),this);return this;},inject:function(n,m){b[m||"bottom"](this,document.id(n,true));return this;},replaces:function(m){m=document.id(m,true);
-m.parentNode.replaceChild(this,m);return this;},wraps:function(n,m){n=document.id(n,true);return this.replaces(n).grab(n,m);},getSelected:function(){this.selectedIndex;
-return new Elements(Array.from(this.options).filter(function(m){return m.selected;}));},toQueryString:function(){var m=[];this.getElements("input, select, textarea").each(function(o){var n=o.type;
-if(!o.name||o.disabled||n=="submit"||n=="reset"||n=="file"||n=="image"){return;}var p=(o.get("tag")=="select")?o.getSelected().map(function(q){return document.id(q).get("value");
-}):((n=="radio"||n=="checkbox")&&!o.checked)?null:o.get("value");Array.from(p).each(function(q){if(typeof q!="undefined"){m.push(encodeURIComponent(o.name)+"="+encodeURIComponent(q));
-}});});return m.join("&");}});var j={},e={};var c=function(m){return(e[m]||(e[m]={}));};var g=function(n){var m=n.uid;if(n.removeEvents){n.removeEvents();
-}if(n.clearAttributes){n.clearAttributes();}if(m!=null){delete j[m];delete e[m];}return n;};var l={input:"checked",option:"selected",textarea:"value"};
-Element.implement({destroy:function(){var m=g(this).getElementsByTagName("*");Array.each(m,g);Element.dispose(this);return null;},empty:function(){Array.from(this.childNodes).each(Element.dispose);
-return this;},dispose:function(){return(this.parentNode)?this.parentNode.removeChild(this):this;},clone:function(r,p){r=r!==false;var w=this.cloneNode(r),o=[w],q=[this],u;
-if(r){o.append(Array.from(w.getElementsByTagName("*")));q.append(Array.from(this.getElementsByTagName("*")));}for(u=o.length;u--;){var s=o[u],v=q[u];if(!p){s.removeAttribute("id");
-}if(s.clearAttributes){s.clearAttributes();s.mergeAttributes(v);s.removeAttribute("uid");if(s.options){var z=s.options,m=v.options;for(var t=z.length;t--;
-){z[t].selected=m[t].selected;}}}var n=l[v.tagName.toLowerCase()];if(n&&v[n]){s[n]=v[n];}}if(Browser.ie){var x=w.getElementsByTagName("object"),y=this.getElementsByTagName("object");
-for(u=x.length;u--;){x[u].outerHTML=y[u].outerHTML;}}return document.id(w);}});[Element,Window,Document].invoke("implement",{addListener:function(p,o){if(p=="unload"){var m=o,n=this;
-o=function(){n.removeListener("unload",o);m();};}else{j[$uid(this)]=this;}if(this.addEventListener){this.addEventListener(p,o,!!arguments[2]);}else{this.attachEvent("on"+p,o);
-}return this;},removeListener:function(n,m){if(this.removeEventListener){this.removeEventListener(n,m,!!arguments[2]);}else{this.detachEvent("on"+n,m);
-}return this;},retrieve:function(n,m){var p=c($uid(this)),o=p[n];if(m!=null&&o==null){o=p[n]=m;}return o!=null?o:null;},store:function(n,m){var o=c($uid(this));
-o[n]=m;return this;},eliminate:function(m){var n=c($uid(this));delete n[m];return this;}});if(window.attachEvent&&!window.addEventListener){window.addListener("unload",function(){Object.each(j,g);
-if(window.CollectGarbage){CollectGarbage();}});}Element.Properties={};Element.Properties.style={set:function(m){this.style.cssText=m;},get:function(){return this.style.cssText;
-},erase:function(){this.style.cssText="";}};Element.Properties.tag={get:function(){return this.tagName.toLowerCase();}};Element.Properties.html=(function(){var s=Function.attempt(function(){var u=document.createElement("table");
-u.innerHTML="<tr><td></td></tr>";});var t=document.createElement("div");var o={table:[1,"<table>","</table>"],select:[1,"<select>","</select>"],tbody:[2,"<table><tbody>","</tbody></table>"],tr:[3,"<table><tbody><tr>","</tr></tbody></table>"]};
-o.thead=o.tfoot=o.tbody;t.innerHTML="<nav></nav>";var n=t.childNodes.length==1;if(!n){var q="abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video".split(" "),p=document.createDocumentFragment(),m=q.length;
-while(m--){p.createElement(q[m]);}p.appendChild(t);}var r={set:function(v){if(typeOf(v)=="array"){v=v.join("");}var w=(!s&&o[this.get("tag")]);if(!w&&!n){w=[0,"",""];
-}if(w){var x=t;x.innerHTML=w[1]+v+w[2];for(var u=w[0];u--;){x=x.firstChild;}this.empty().adopt(x.childNodes);}else{this.innerHTML=v;}}};r.erase=r.set;return r;
-})();var f=document.createElement("form");f.innerHTML="<select><option>s</option></select>";if(f.firstChild.value!="s"){Element.Properties.value={set:function(r){var n=this.get("tag");
-if(n!="select"){return this.setProperty("value",r);}var o=this.getElements("option");for(var p=0;p<o.length;p++){var q=o[p],m=q.getAttributeNode("value"),s=(m&&m.specified)?q.value:q.get("text");
-if(s==r){return q.selected=true;}}},get:function(){var o=this,n=o.get("tag");if(n!="select"&&n!="option"){return this.getProperty("value");}if(n=="select"&&!(o=o.getSelected()[0])){return"";
-}var m=o.getAttributeNode("value");return(m&&m.specified)?o.value:o.get("text");}};}})();(function(){var f=document.html;Element.Properties.styles={set:function(i){this.setStyles(i);
-}};var h=(f.style.opacity!=null),a=(f.style.filter!=null),g=/alpha\(opacity=([\d.]+)\)/i;var b=function(j,i){j.store("$opacity",i);j.style.visibility=i>0?"visible":"hidden";
-};var d=(h?function(j,i){j.style.opacity=i;}:(a?function(j,i){if(!j.currentStyle||!j.currentStyle.hasLayout){j.style.zoom=1;}i=(i*100).limit(0,100).round();
-i=(i==100)?"":"alpha(opacity="+i+")";var k=j.style.filter||j.getComputedStyle("filter")||"";j.style.filter=g.test(k)?k.replace(g,i):k+i;}:b));var e=(h?function(j){var i=j.style.opacity||j.getComputedStyle("opacity");
-return(i=="")?1:i.toFloat();}:(a?function(j){var k=(j.style.filter||j.getComputedStyle("filter")),i;if(k){i=k.match(g);}return(i==null||k==null)?1:(i[1]/100);
-}:function(j){var i=j.retrieve("$opacity");if(i==null){i=(j.style.visibility=="hidden"?0:1);}return i;}));var c=(f.style.cssFloat==null)?"styleFloat":"cssFloat";
-Element.implement({getComputedStyle:function(k){if(this.currentStyle){return this.currentStyle[k.camelCase()];}var j=Element.getDocument(this).defaultView,i=j?j.getComputedStyle(this,null):null;
-return(i)?i.getPropertyValue((k==c)?"float":k.hyphenate()):null;},setStyle:function(j,i){if(j=="opacity"){d(this,parseFloat(i));return this;}j=(j=="float"?c:j).camelCase();
-if(typeOf(i)!="string"){var k=(Element.Styles[j]||"@").split(" ");i=Array.from(i).map(function(m,l){if(!k[l]){return"";}return(typeOf(m)=="number")?k[l].replace("@",Math.round(m)):m;
-}).join(" ");}else{if(i==String(Number(i))){i=Math.round(i);}}this.style[j]=i;return this;},getStyle:function(o){if(o=="opacity"){return e(this);}o=(o=="float"?c:o).camelCase();
-var i=this.style[o];if(!i||o=="zIndex"){i=[];for(var n in Element.ShortStyles){if(o!=n){continue;}for(var m in Element.ShortStyles[n]){i.push(this.getStyle(m));
-}return i.join(" ");}i=this.getComputedStyle(o);}if(i){i=String(i);var k=i.match(/rgba?\([\d\s,]+\)/);if(k){i=i.replace(k[0],k[0].rgbToHex());}}if(Browser.opera||(Browser.ie&&isNaN(parseFloat(i)))){if((/^(height|width)$/).test(o)){var j=(o=="width")?["left","right"]:["top","bottom"],l=0;
-j.each(function(p){l+=this.getStyle("border-"+p+"-width").toInt()+this.getStyle("padding-"+p).toInt();},this);return this["offset"+o.capitalize()]-l+"px";
-}if(Browser.opera&&String(i).indexOf("px")!=-1){return i;}if((/^border(.+)Width|margin|padding/).test(o)){return"0px";}}return i;},setStyles:function(j){for(var i in j){this.setStyle(i,j[i]);
-}return this;},getStyles:function(){var i={};Array.flatten(arguments).each(function(j){i[j]=this.getStyle(j);},this);return i;}});Element.Styles={left:"@px",top:"@px",bottom:"@px",right:"@px",width:"@px",height:"@px",maxWidth:"@px",maxHeight:"@px",minWidth:"@px",minHeight:"@px",backgroundColor:"rgb(@, @, @)",backgroundPosition:"@px @px",color:"rgb(@, @, @)",fontSize:"@px",letterSpacing:"@px",lineHeight:"@px",clip:"rect(@px @px @px @px)",margin:"@px @px @px @px",padding:"@px @px @px @px",border:"@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)",borderWidth:"@px @px @px @px",borderStyle:"@ @ @ @",borderColor:"rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)",zIndex:"@",zoom:"@",fontWeight:"@",textIndent:"@px",opacity:"@"};
-Element.ShortStyles={margin:{},padding:{},border:{},borderWidth:{},borderStyle:{},borderColor:{}};["Top","Right","Bottom","Left"].each(function(o){var n=Element.ShortStyles;
-var j=Element.Styles;["margin","padding"].each(function(p){var q=p+o;n[p][q]=j[q]="@px";});var m="border"+o;n.border[m]=j[m]="@px @ rgb(@, @, @)";var l=m+"Width",i=m+"Style",k=m+"Color";
-n[m]={};n.borderWidth[l]=n[m][l]=j[l]="@px";n.borderStyle[i]=n[m][i]=j[i]="@";n.borderColor[k]=n[m][k]=j[k]="rgb(@, @, @)";});})();(function(){Element.Properties.events={set:function(b){this.addEvents(b);
-}};[Element,Window,Document].invoke("implement",{addEvent:function(f,h){var i=this.retrieve("events",{});if(!i[f]){i[f]={keys:[],values:[]};}if(i[f].keys.contains(h)){return this;
-}i[f].keys.push(h);var g=f,b=Element.Events[f],d=h,j=this;if(b){if(b.onAdd){b.onAdd.call(this,h,f);}if(b.condition){d=function(k){if(b.condition.call(this,k,f)){return h.call(this,k);
-}return true;};}if(b.base){g=Function.from(b.base).call(this,f);}}var e=function(){return h.call(j);};var c=Element.NativeEvents[g];if(c){if(c==2){e=function(k){k=new DOMEvent(k,j.getWindow());
-if(d.call(j,k)===false){k.stop();}};}this.addListener(g,e,arguments[2]);}i[f].values.push(e);return this;},removeEvent:function(e,d){var c=this.retrieve("events");
-if(!c||!c[e]){return this;}var h=c[e];var b=h.keys.indexOf(d);if(b==-1){return this;}var g=h.values[b];delete h.keys[b];delete h.values[b];var f=Element.Events[e];
-if(f){if(f.onRemove){f.onRemove.call(this,d,e);}if(f.base){e=Function.from(f.base).call(this,e);}}return(Element.NativeEvents[e])?this.removeListener(e,g,arguments[2]):this;
-},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]);}return this;},removeEvents:function(b){var d;if(typeOf(b)=="object"){for(d in b){this.removeEvent(d,b[d]);
-}return this;}var c=this.retrieve("events");if(!c){return this;}if(!b){for(d in c){this.removeEvents(d);}this.eliminate("events");}else{if(c[b]){c[b].keys.each(function(e){this.removeEvent(b,e);
-},this);delete c[b];}}return this;},fireEvent:function(e,c,b){var d=this.retrieve("events");if(!d||!d[e]){return this;}c=Array.from(c);d[e].keys.each(function(f){if(b){f.delay(b,this,c);
-}else{f.apply(this,c);}},this);return this;},cloneEvents:function(e,d){e=document.id(e);var c=e.retrieve("events");if(!c){return this;}if(!d){for(var b in c){this.cloneEvents(e,b);
-}}else{if(c[d]){c[d].keys.each(function(f){this.addEvent(d,f);},this);}}return this;}});Element.NativeEvents={click:2,dblclick:2,mouseup:2,mousedown:2,contextmenu:2,mousewheel:2,DOMMouseScroll:2,mouseover:2,mouseout:2,mousemove:2,selectstart:2,selectend:2,keydown:2,keypress:2,keyup:2,orientationchange:2,touchstart:2,touchmove:2,touchend:2,touchcancel:2,gesturestart:2,gesturechange:2,gestureend:2,focus:2,blur:2,change:2,reset:2,select:2,submit:2,paste:2,input:2,load:2,unload:1,beforeunload:2,resize:1,move:1,DOMContentLoaded:1,readystatechange:1,error:1,abort:1,scroll:1};
-var a=function(b){var c=b.relatedTarget;if(c==null){return true;}if(!c){return false;}return(c!=this&&c.prefix!="xul"&&typeOf(this)!="document"&&!this.contains(c));
-};Element.Events={mouseenter:{base:"mouseover",condition:a},mouseleave:{base:"mouseout",condition:a},mousewheel:{base:(Browser.firefox)?"DOMMouseScroll":"mousewheel"}};
-if(!window.addEventListener){Element.NativeEvents.propertychange=2;Element.Events.change={base:function(){var b=this.type;return(this.get("tag")=="input"&&(b=="radio"||b=="checkbox"))?"propertychange":"change";
-},condition:function(b){return !!(this.type!="radio"||this.checked);}};}})();(function(){var c=!!window.addEventListener;Element.NativeEvents.focusin=Element.NativeEvents.focusout=2;
-var k=function(l,m,n,o,p){while(p&&p!=l){if(m(p,o)){return n.call(p,o,p);}p=document.id(p.parentNode);}};var a={mouseenter:{base:"mouseover"},mouseleave:{base:"mouseout"},focus:{base:"focus"+(c?"":"in"),capture:true},blur:{base:c?"blur":"focusout",capture:true}};
-var b="$delegation:";var i=function(l){return{base:"focusin",remove:function(m,o){var p=m.retrieve(b+l+"listeners",{})[o];if(p&&p.forms){for(var n=p.forms.length;
-n--;){p.forms[n].removeEvent(l,p.fns[n]);}}},listen:function(x,r,v,n,t,s){var o=(t.get("tag")=="form")?t:n.target.getParent("form");if(!o){return;}var u=x.retrieve(b+l+"listeners",{}),p=u[s]||{forms:[],fns:[]},m=p.forms,w=p.fns;
-if(m.indexOf(o)!=-1){return;}m.push(o);var q=function(y){k(x,r,v,y,t);};o.addEvent(l,q);w.push(q);u[s]=p;x.store(b+l+"listeners",u);}};};var d=function(l){return{base:"focusin",listen:function(m,n,p,q,r){var o={blur:function(){this.removeEvents(o);
-}};o[l]=function(s){k(m,n,p,s,r);};q.target.addEvents(o);}};};if(!c){Object.append(a,{submit:i("submit"),reset:i("reset"),change:d("change"),select:d("select")});
-}var h=Element.prototype,f=h.addEvent,j=h.removeEvent;var e=function(l,m){return function(r,q,n){if(r.indexOf(":relay")==-1){return l.call(this,r,q,n);
-}var o=Slick.parse(r).expressions[0][0];if(o.pseudos[0].key!="relay"){return l.call(this,r,q,n);}var p=o.tag;o.pseudos.slice(1).each(function(s){p+=":"+s.key+(s.value?"("+s.value+")":"");
-});l.call(this,r,q);return m.call(this,p,o.pseudos[0].value,q);};};var g={addEvent:function(v,q,x){var t=this.retrieve("$delegates",{}),r=t[v];if(r){for(var y in r){if(r[y].fn==x&&r[y].match==q){return this;
-}}}var p=v,u=q,o=x,n=a[v]||{};v=n.base||p;q=function(B){return Slick.match(B,u);};var w=Element.Events[p];if(w&&w.condition){var l=q,m=w.condition;q=function(C,B){return l(C,B)&&m.call(C,B,v);
-};}var z=this,s=String.uniqueID();var A=n.listen?function(B,C){if(!C&&B&&B.target){C=B.target;}if(C){n.listen(z,q,x,B,C,s);}}:function(B,C){if(!C&&B&&B.target){C=B.target;
-}if(C){k(z,q,x,B,C);}};if(!r){r={};}r[s]={match:u,fn:o,delegator:A};t[p]=r;return f.call(this,v,A,n.capture);},removeEvent:function(r,n,t,u){var q=this.retrieve("$delegates",{}),p=q[r];
-if(!p){return this;}if(u){var m=r,w=p[u].delegator,l=a[r]||{};r=l.base||m;if(l.remove){l.remove(this,u);}delete p[u];q[m]=p;return j.call(this,r,w);}var o,v;
-if(t){for(o in p){v=p[o];if(v.match==n&&v.fn==t){return g.removeEvent.call(this,r,n,t,o);}}}else{for(o in p){v=p[o];if(v.match==n){g.removeEvent.call(this,r,n,v.fn,o);
-}}}return this;}};[Element,Window,Document].invoke("implement",{addEvent:e(f,g.addEvent),removeEvent:e(j,g.removeEvent)});})();(function(){var h=document.createElement("div"),e=document.createElement("div");
-h.style.height="0";h.appendChild(e);var d=(e.offsetParent===h);h=e=null;var l=function(m){return k(m,"position")!="static"||a(m);};var i=function(m){return l(m)||(/^(?:table|td|th)$/i).test(m.tagName);
-};Element.implement({scrollTo:function(m,n){if(a(this)){this.getWindow().scrollTo(m,n);}else{this.scrollLeft=m;this.scrollTop=n;}return this;},getSize:function(){if(a(this)){return this.getWindow().getSize();
-}return{x:this.offsetWidth,y:this.offsetHeight};},getScrollSize:function(){if(a(this)){return this.getWindow().getScrollSize();}return{x:this.scrollWidth,y:this.scrollHeight};
-},getScroll:function(){if(a(this)){return this.getWindow().getScroll();}return{x:this.scrollLeft,y:this.scrollTop};},getScrolls:function(){var n=this.parentNode,m={x:0,y:0};
-while(n&&!a(n)){m.x+=n.scrollLeft;m.y+=n.scrollTop;n=n.parentNode;}return m;},getOffsetParent:d?function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;
-}var n=(k(m,"position")=="static")?i:l;while((m=m.parentNode)){if(n(m)){return m;}}return null;}:function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;
-}try{return m.offsetParent;}catch(n){}return null;},getOffsets:function(){if(this.getBoundingClientRect&&!Browser.Platform.ios){var r=this.getBoundingClientRect(),o=document.id(this.getDocument().documentElement),q=o.getScroll(),t=this.getScrolls(),s=(k(this,"position")=="fixed");
-return{x:r.left.toInt()+t.x+((s)?0:q.x)-o.clientLeft,y:r.top.toInt()+t.y+((s)?0:q.y)-o.clientTop};}var n=this,m={x:0,y:0};if(a(this)){return m;}while(n&&!a(n)){m.x+=n.offsetLeft;
-m.y+=n.offsetTop;if(Browser.firefox){if(!c(n)){m.x+=b(n);m.y+=g(n);}var p=n.parentNode;if(p&&k(p,"overflow")!="visible"){m.x+=b(p);m.y+=g(p);}}else{if(n!=this&&Browser.safari){m.x+=b(n);
-m.y+=g(n);}}n=n.offsetParent;}if(Browser.firefox&&!c(this)){m.x-=b(this);m.y-=g(this);}return m;},getPosition:function(p){var q=this.getOffsets(),n=this.getScrolls();
-var m={x:q.x-n.x,y:q.y-n.y};if(p&&(p=document.id(p))){var o=p.getPosition();return{x:m.x-o.x-b(p),y:m.y-o.y-g(p)};}return m;},getCoordinates:function(o){if(a(this)){return this.getWindow().getCoordinates();
-}var m=this.getPosition(o),n=this.getSize();var p={left:m.x,top:m.y,width:n.x,height:n.y};p.right=p.left+p.width;p.bottom=p.top+p.height;return p;},computePosition:function(m){return{left:m.x-j(this,"margin-left"),top:m.y-j(this,"margin-top")};
-},setPosition:function(m){return this.setStyles(this.computePosition(m));}});[Document,Window].invoke("implement",{getSize:function(){var m=f(this);return{x:m.clientWidth,y:m.clientHeight};
-},getScroll:function(){var n=this.getWindow(),m=f(this);return{x:n.pageXOffset||m.scrollLeft,y:n.pageYOffset||m.scrollTop};},getScrollSize:function(){var o=f(this),n=this.getSize(),m=this.getDocument().body;
-return{x:Math.max(o.scrollWidth,m.scrollWidth,n.x),y:Math.max(o.scrollHeight,m.scrollHeight,n.y)};},getPosition:function(){return{x:0,y:0};},getCoordinates:function(){var m=this.getSize();
-return{top:0,left:0,bottom:m.y,right:m.x,height:m.y,width:m.x};}});var k=Element.getComputedStyle;function j(m,n){return k(m,n).toInt()||0;}function c(m){return k(m,"-moz-box-sizing")=="border-box";
-}function g(m){return j(m,"border-top-width");}function b(m){return j(m,"border-left-width");}function a(m){return(/^(?:body|html)$/i).test(m.tagName);
-}function f(m){var n=m.getDocument();return(!n.compatMode||n.compatMode=="CSS1Compat")?n.html:n.body;}})();Element.alias({position:"setPosition"});[Window,Document,Element].invoke("implement",{getHeight:function(){return this.getSize().y;
-},getWidth:function(){return this.getSize().x;},getScrollTop:function(){return this.getScroll().y;},getScrollLeft:function(){return this.getScroll().x;
-},getScrollHeight:function(){return this.getScrollSize().y;},getScrollWidth:function(){return this.getScrollSize().x;},getTop:function(){return this.getPosition().y;
-},getLeft:function(){return this.getPosition().x;}});(function(){var f=this.Fx=new Class({Implements:[Chain,Events,Options],options:{fps:60,unit:false,duration:500,frames:null,frameSkip:true,link:"ignore"},initialize:function(g){this.subject=this.subject||this;
-this.setOptions(g);},getTransition:function(){return function(g){return -(Math.cos(Math.PI*g)-1)/2;};},step:function(g){if(this.options.frameSkip){var h=(this.time!=null)?(g-this.time):0,i=h/this.frameInterval;
-this.time=g;this.frame+=i;}else{this.frame++;}if(this.frame<this.frames){var j=this.transition(this.frame/this.frames);this.set(this.compute(this.from,this.to,j));
-}else{this.frame=this.frames;this.set(this.compute(this.from,this.to,1));this.stop();}},set:function(g){return g;},compute:function(i,h,g){return f.compute(i,h,g);
-},check:function(){if(!this.isRunning()){return true;}switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.pass(arguments,this));
-return false;}return false;},start:function(k,j){if(!this.check(k,j)){return this;}this.from=k;this.to=j;this.frame=(this.options.frameSkip)?0:-1;this.time=null;
-this.transition=this.getTransition();var i=this.options.frames,h=this.options.fps,g=this.options.duration;this.duration=f.Durations[g]||g.toInt();this.frameInterval=1000/h;
-this.frames=i||Math.round(this.duration/this.frameInterval);this.fireEvent("start",this.subject);b.call(this,h);return this;},stop:function(){if(this.isRunning()){this.time=null;
-d.call(this,this.options.fps);if(this.frames==this.frame){this.fireEvent("complete",this.subject);if(!this.callChain()){this.fireEvent("chainComplete",this.subject);
-}}else{this.fireEvent("stop",this.subject);}}return this;},cancel:function(){if(this.isRunning()){this.time=null;d.call(this,this.options.fps);this.frame=this.frames;
-this.fireEvent("cancel",this.subject).clearChain();}return this;},pause:function(){if(this.isRunning()){this.time=null;d.call(this,this.options.fps);}return this;
-},resume:function(){if((this.frame<this.frames)&&!this.isRunning()){b.call(this,this.options.fps);}return this;},isRunning:function(){var g=e[this.options.fps];
-return g&&g.contains(this);}});f.compute=function(i,h,g){return(h-i)*g+i;};f.Durations={"short":250,normal:500,"long":1000};var e={},c={};var a=function(){var h=Date.now();
-for(var j=this.length;j--;){var g=this[j];if(g){g.step(h);}}};var b=function(h){var g=e[h]||(e[h]=[]);g.push(this);if(!c[h]){c[h]=a.periodical(Math.round(1000/h),g);
-}};var d=function(h){var g=e[h];if(g){g.erase(this);if(!g.length&&c[h]){delete e[h];c[h]=clearInterval(c[h]);}}};})();Fx.CSS=new Class({Extends:Fx,prepare:function(c,d,b){b=Array.from(b);
-if(b[1]==null){b[1]=b[0];b[0]=c.getStyle(d);}var a=b.map(this.parse);return{from:a[0],to:a[1]};},parse:function(a){a=Function.from(a)();a=(typeof a=="string")?a.split(" "):Array.from(a);
-return a.map(function(c){c=String(c);var b=false;Object.each(Fx.CSS.Parsers,function(f,e){if(b){return;}var d=f.parse(c);if(d||d===0){b={value:d,parser:f};
-}});b=b||{value:c,parser:Fx.CSS.Parsers.String};return b;});},compute:function(d,c,b){var a=[];(Math.min(d.length,c.length)).times(function(e){a.push({value:d[e].parser.compute(d[e].value,c[e].value,b),parser:d[e].parser});
-});a.$family=Function.from("fx:css:value");return a;},serve:function(c,b){if(typeOf(c)!="fx:css:value"){c=this.parse(c);}var a=[];c.each(function(d){a=a.concat(d.parser.serve(d.value,b));
-});return a;},render:function(a,d,c,b){a.setStyle(d,this.serve(c,b));},search:function(a){if(Fx.CSS.Cache[a]){return Fx.CSS.Cache[a];}var c={},b=new RegExp("^"+a.escapeRegExp()+"$");
-Array.each(document.styleSheets,function(f,e){var d=f.href;if(d&&d.contains("://")&&!d.contains(document.domain)){return;}var g=f.rules||f.cssRules;Array.each(g,function(k,h){if(!k.style){return;
-}var j=(k.selectorText)?k.selectorText.replace(/^\w+/,function(i){return i.toLowerCase();}):null;if(!j||!b.test(j)){return;}Object.each(Element.Styles,function(l,i){if(!k.style[i]||Element.ShortStyles[i]){return;
-}l=String(k.style[i]);c[i]=((/^rgb/).test(l))?l.rgbToHex():l;});});});return Fx.CSS.Cache[a]=c;}});Fx.CSS.Cache={};Fx.CSS.Parsers={Color:{parse:function(a){if(a.match(/^#[0-9a-f]{3,6}$/i)){return a.hexToRgb(true);
-}return((a=a.match(/(\d+),\s*(\d+),\s*(\d+)/)))?[a[1],a[2],a[3]]:false;},compute:function(c,b,a){return c.map(function(e,d){return Math.round(Fx.compute(c[d],b[d],a));
-});},serve:function(a){return a.map(Number);}},Number:{parse:parseFloat,compute:Fx.compute,serve:function(b,a){return(a)?b+a:b;}},String:{parse:Function.from(false),compute:function(b,a){return a;
-},serve:function(a){return a;}}};Fx.Tween=new Class({Extends:Fx.CSS,initialize:function(b,a){this.element=this.subject=document.id(b);this.parent(a);},set:function(b,a){if(arguments.length==1){a=b;
-b=this.property||this.options.property;}this.render(this.element,b,a,this.options.unit);return this;},start:function(c,e,d){if(!this.check(c,e,d)){return this;
-}var b=Array.flatten(arguments);this.property=this.options.property||b.shift();var a=this.prepare(this.element,this.property,b);return this.parent(a.from,a.to);
-}});Element.Properties.tween={set:function(a){this.get("tween").cancel().setOptions(a);return this;},get:function(){var a=this.retrieve("tween");if(!a){a=new Fx.Tween(this,{link:"cancel"});
-this.store("tween",a);}return a;}};Element.implement({tween:function(a,c,b){this.get("tween").start(a,c,b);return this;},fade:function(c){var d=this.get("tween"),f,e,a;
-if(c==null){c="toggle";}switch(c){case"in":f="start";e=1;break;case"out":f="start";e=0;break;case"show":f="set";e=1;break;case"hide":f="set";e=0;break;
-case"toggle":var b=this.retrieve("fade:flag",this.getStyle("opacity")==1);f="start";e=b?0:1;this.store("fade:flag",!b);a=true;break;default:f="start";e=c;
-}if(!a){this.eliminate("fade:flag");}d[f]("opacity",e);if(f=="set"||e!=0){this.setStyle("visibility",e==0?"hidden":"visible");}else{d.chain(function(){this.element.setStyle("visibility","hidden");
-});}return this;},highlight:function(c,a){if(!a){a=this.retrieve("highlight:original",this.getStyle("background-color"));a=(a=="transparent")?"#fff":a;
-}var b=this.get("tween");b.start("background-color",c||"#ffff88",a).chain(function(){this.setStyle("background-color",this.retrieve("highlight:original"));
-b.callChain();}.bind(this));return this;}});Fx.Morph=new Class({Extends:Fx.CSS,initialize:function(b,a){this.element=this.subject=document.id(b);this.parent(a);
-},set:function(a){if(typeof a=="string"){a=this.search(a);}for(var b in a){this.render(this.element,b,a[b],this.options.unit);}return this;},compute:function(e,d,c){var a={};
-for(var b in e){a[b]=this.parent(e[b],d[b],c);}return a;},start:function(b){if(!this.check(b)){return this;}if(typeof b=="string"){b=this.search(b);}var e={},d={};
-for(var c in b){var a=this.prepare(this.element,c,b[c]);e[c]=a.from;d[c]=a.to;}return this.parent(e,d);}});Element.Properties.morph={set:function(a){this.get("morph").cancel().setOptions(a);
-return this;},get:function(){var a=this.retrieve("morph");if(!a){a=new Fx.Morph(this,{link:"cancel"});this.store("morph",a);}return a;}};Element.implement({morph:function(a){this.get("morph").start(a);
-return this;}});Fx.implement({getTransition:function(){var a=this.options.transition||Fx.Transitions.Sine.easeInOut;if(typeof a=="string"){var b=a.split(":");
-a=Fx.Transitions;a=a[b[0]]||a[b[0].capitalize()];if(b[1]){a=a["ease"+b[1].capitalize()+(b[2]?b[2].capitalize():"")];}}return a;}});Fx.Transition=function(c,b){b=Array.from(b);
-var a=function(d){return c(d,b);};return Object.append(a,{easeIn:a,easeOut:function(d){return 1-c(1-d,b);},easeInOut:function(d){return(d<=0.5?c(2*d,b):(2-c(2*(1-d),b)))/2;
-}});};Fx.Transitions={linear:function(a){return a;}};Fx.Transitions.extend=function(a){for(var b in a){Fx.Transitions[b]=new Fx.Transition(a[b]);}};Fx.Transitions.extend({Pow:function(b,a){return Math.pow(b,a&&a[0]||6);
-},Expo:function(a){return Math.pow(2,8*(a-1));},Circ:function(a){return 1-Math.sin(Math.acos(a));},Sine:function(a){return 1-Math.cos(a*Math.PI/2);},Back:function(b,a){a=a&&a[0]||1.618;
-return Math.pow(b,2)*((a+1)*b-a);},Bounce:function(f){var e;for(var d=0,c=1;1;d+=c,c/=2){if(f>=(7-4*d)/11){e=c*c-Math.pow((11-6*d-11*f)/4,2);break;}}return e;
-},Elastic:function(b,a){return Math.pow(2,10*--b)*Math.cos(20*b*Math.PI*(a&&a[0]||1)/3);}});["Quad","Cubic","Quart","Quint"].each(function(b,a){Fx.Transitions[b]=new Fx.Transition(function(c){return Math.pow(c,a+2);
-});});(function(){var d=function(){},a=("onprogress" in new Browser.Request);var c=this.Request=new Class({Implements:[Chain,Events,Options],options:{url:"",data:"",headers:{"X-Requested-With":"XMLHttpRequest",Accept:"text/javascript, text/html, application/xml, text/xml, */*"},async:true,format:false,method:"post",link:"ignore",isSuccess:null,emulation:true,urlEncoded:true,encoding:"utf-8",evalScripts:false,evalResponse:false,timeout:0,noCache:false},initialize:function(e){this.xhr=new Browser.Request();
-this.setOptions(e);this.headers=this.options.headers;},onStateChange:function(){var e=this.xhr;if(e.readyState!=4||!this.running){return;}this.running=false;
-this.status=0;Function.attempt(function(){var f=e.status;this.status=(f==1223)?204:f;}.bind(this));e.onreadystatechange=d;if(a){e.onprogress=e.onloadstart=d;
-}clearTimeout(this.timer);this.response={text:this.xhr.responseText||"",xml:this.xhr.responseXML};if(this.options.isSuccess.call(this,this.status)){this.success(this.response.text,this.response.xml);
-}else{this.failure();}},isSuccess:function(){var e=this.status;return(e>=200&&e<300);},isRunning:function(){return !!this.running;},processScripts:function(e){if(this.options.evalResponse||(/(ecma|java)script/).test(this.getHeader("Content-type"))){return Browser.exec(e);
-}return e.stripScripts(this.options.evalScripts);},success:function(f,e){this.onSuccess(this.processScripts(f),e);},onSuccess:function(){this.fireEvent("complete",arguments).fireEvent("success",arguments).callChain();
-},failure:function(){this.onFailure();},onFailure:function(){this.fireEvent("complete").fireEvent("failure",this.xhr);},loadstart:function(e){this.fireEvent("loadstart",[e,this.xhr]);
-},progress:function(e){this.fireEvent("progress",[e,this.xhr]);},timeout:function(){this.fireEvent("timeout",this.xhr);},setHeader:function(e,f){this.headers[e]=f;
-return this;},getHeader:function(e){return Function.attempt(function(){return this.xhr.getResponseHeader(e);}.bind(this));},check:function(){if(!this.running){return true;
-}switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.pass(arguments,this));return false;}return false;},send:function(o){if(!this.check(o)){return this;
-}this.options.isSuccess=this.options.isSuccess||this.isSuccess;this.running=true;var l=typeOf(o);if(l=="string"||l=="element"){o={data:o};}var h=this.options;
-o=Object.append({data:h.data,url:h.url,method:h.method},o);var j=o.data,f=String(o.url),e=o.method.toLowerCase();switch(typeOf(j)){case"element":j=document.id(j).toQueryString();
-break;case"object":case"hash":j=Object.toQueryString(j);}if(this.options.format){var m="format="+this.options.format;j=(j)?m+"&"+j:m;}if(this.options.emulation&&!["get","post"].contains(e)){var k="_method="+e;
-j=(j)?k+"&"+j:k;e="post";}if(this.options.urlEncoded&&["post","put"].contains(e)){var g=(this.options.encoding)?"; charset="+this.options.encoding:"";this.headers["Content-type"]="application/x-www-form-urlencoded"+g;
-}if(!f){f=document.location.pathname;}var i=f.lastIndexOf("/");if(i>-1&&(i=f.indexOf("#"))>-1){f=f.substr(0,i);}if(this.options.noCache){f+=(f.contains("?")?"&":"?")+String.uniqueID();
-}if(j&&e=="get"){f+=(f.contains("?")?"&":"?")+j;j=null;}var n=this.xhr;if(a){n.onloadstart=this.loadstart.bind(this);n.onprogress=this.progress.bind(this);
-}n.open(e.toUpperCase(),f,this.options.async,this.options.user,this.options.password);if(this.options.user&&"withCredentials" in n){n.withCredentials=true;
-}n.onreadystatechange=this.onStateChange.bind(this);Object.each(this.headers,function(q,p){try{n.setRequestHeader(p,q);}catch(r){this.fireEvent("exception",[p,q]);
-}},this);this.fireEvent("request");n.send(j);if(!this.options.async){this.onStateChange();}if(this.options.timeout){this.timer=this.timeout.delay(this.options.timeout,this);
-}return this;},cancel:function(){if(!this.running){return this;}this.running=false;var e=this.xhr;e.abort();clearTimeout(this.timer);e.onreadystatechange=d;
-if(a){e.onprogress=e.onloadstart=d;}this.xhr=new Browser.Request();this.fireEvent("cancel");return this;}});var b={};["get","post","put","delete","GET","POST","PUT","DELETE"].each(function(e){b[e]=function(g){var f={method:e};
-if(g!=null){f.data=g;}return this.send(f);};});c.implement(b);Element.Properties.send={set:function(e){var f=this.get("send").cancel();f.setOptions(e);
-return this;},get:function(){var e=this.retrieve("send");if(!e){e=new c({data:this,link:"cancel",method:this.get("method")||"post",url:this.get("action")});
-this.store("send",e);}return e;}};Element.implement({send:function(e){var f=this.get("send");f.send({data:this,url:e||f.options.url});return this;}});})();
-Request.HTML=new Class({Extends:Request,options:{update:false,append:false,evalScripts:true,filter:false,headers:{Accept:"text/html, application/xml, text/xml, */*"}},success:function(f){var e=this.options,c=this.response;
-c.html=f.stripScripts(function(h){c.javascript=h;});var d=c.html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);if(d){c.html=d[1];}var b=new Element("div").set("html",c.html);
-c.tree=b.childNodes;c.elements=b.getElements(e.filter||"*");if(e.filter){c.tree=c.elements;}if(e.update){var g=document.id(e.update).empty();if(e.filter){g.adopt(c.elements);
-}else{g.set("html",c.html);}}else{if(e.append){var a=document.id(e.append);if(e.filter){c.elements.reverse().inject(a);}else{a.adopt(b.getChildren());}}}if(e.evalScripts){Browser.exec(c.javascript);
-}this.onSuccess(c.tree,c.elements,c.html,c.javascript);}});Element.Properties.load={set:function(a){var b=this.get("load").cancel();b.setOptions(a);return this;
-},get:function(){var a=this.retrieve("load");if(!a){a=new Request.HTML({data:this,link:"cancel",update:this,method:"get"});this.store("load",a);}return a;
-}};Element.implement({load:function(){this.get("load").send(Array.link(arguments,{data:Type.isObject,url:Type.isString}));return this;}});if(typeof JSON=="undefined"){this.JSON={};
-}(function(){var special={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"};var escape=function(chr){return special[chr]||"\\u"+("0000"+chr.charCodeAt(0).toString(16)).slice(-4);
-};JSON.validate=function(string){string=string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"");
-return(/^[\],:{}\s]*$/).test(string);};JSON.encode=JSON.stringify?function(obj){return JSON.stringify(obj);}:function(obj){if(obj&&obj.toJSON){obj=obj.toJSON();
-}switch(typeOf(obj)){case"string":return'"'+obj.replace(/[\x00-\x1f\\"]/g,escape)+'"';case"array":return"["+obj.map(JSON.encode).clean()+"]";case"object":case"hash":var string=[];
-Object.each(obj,function(value,key){var json=JSON.encode(value);if(json){string.push(JSON.encode(key)+":"+json);}});return"{"+string+"}";case"number":case"boolean":return""+obj;
-case"null":return"null";}return null;};JSON.decode=function(string,secure){if(!string||typeOf(string)!="string"){return null;}if(secure||JSON.secure){if(JSON.parse){return JSON.parse(string);
-}if(!JSON.validate(string)){throw new Error("JSON could not decode the input; security is enabled and the value is not secure.");}}return eval("("+string+")");
-};})();Request.JSON=new Class({Extends:Request,options:{secure:true},initialize:function(a){this.parent(a);Object.append(this.headers,{Accept:"application/json","X-Request":"JSON"});
-},success:function(c){var b;try{b=this.response.json=JSON.decode(c,this.options.secure);}catch(a){this.fireEvent("error",[c,a]);return;}if(b==null){this.onFailure();
-}else{this.onSuccess(b,c);}}});var Cookie=new Class({Implements:Options,options:{path:"/",domain:false,duration:false,secure:false,document:document,encode:true},initialize:function(b,a){this.key=b;
-this.setOptions(a);},write:function(b){if(this.options.encode){b=encodeURIComponent(b);}if(this.options.domain){b+="; domain="+this.options.domain;}if(this.options.path){b+="; path="+this.options.path;
-}if(this.options.duration){var a=new Date();a.setTime(a.getTime()+this.options.duration*24*60*60*1000);b+="; expires="+a.toGMTString();}if(this.options.secure){b+="; secure";
-}this.options.document.cookie=this.key+"="+b;return this;},read:function(){var a=this.options.document.cookie.match("(?:^|;)\\s*"+this.key.escapeRegExp()+"=([^;]*)");
-return(a)?decodeURIComponent(a[1]):null;},dispose:function(){new Cookie(this.key,Object.merge({},this.options,{duration:-1})).write("");return this;}});
-Cookie.write=function(b,c,a){return new Cookie(b,a).write(c);};Cookie.read=function(a){return new Cookie(a).read();};Cookie.dispose=function(b,a){return new Cookie(b,a).dispose();
-};(function(i,k){var l,f,e=[],c,b,d=k.createElement("div");var g=function(){clearTimeout(b);if(l){return;}Browser.loaded=l=true;k.removeListener("DOMContentLoaded",g).removeListener("readystatechange",a);
-k.fireEvent("domready");i.fireEvent("domready");};var a=function(){for(var m=e.length;m--;){if(e[m]()){g();return true;}}return false;};var j=function(){clearTimeout(b);
-if(!a()){b=setTimeout(j,10);}};k.addListener("DOMContentLoaded",g);var h=function(){try{d.doScroll();return true;}catch(m){}return false;};if(d.doScroll&&!h()){e.push(h);
-c=true;}if(k.readyState){e.push(function(){var m=k.readyState;return(m=="loaded"||m=="complete");});}if("onreadystatechange" in k){k.addListener("readystatechange",a);
-}else{c=true;}if(c){j();}Element.Events.domready={onAdd:function(m){if(l){m.call(this);}}};Element.Events.load={base:"load",onAdd:function(m){if(f&&this==i){m.call(this);
-}},condition:function(){if(this==i){g();delete Element.Events.load;}return true;}};i.addEvent("load",function(){f=true;});})(window,document);(function(){var Swiff=this.Swiff=new Class({Implements:Options,options:{id:null,height:1,width:1,container:null,properties:{},params:{quality:"high",allowScriptAccess:"always",wMode:"window",swLiveConnect:true},callBacks:{},vars:{}},toElement:function(){return this.object;
-},initialize:function(path,options){this.instance="Swiff_"+String.uniqueID();this.setOptions(options);options=this.options;var id=this.id=options.id||this.instance;
-var container=document.id(options.container);Swiff.CallBacks[this.instance]={};var params=options.params,vars=options.vars,callBacks=options.callBacks;
-var properties=Object.append({height:options.height,width:options.width},options.properties);var self=this;for(var callBack in callBacks){Swiff.CallBacks[this.instance][callBack]=(function(option){return function(){return option.apply(self.object,arguments);
-};})(callBacks[callBack]);vars[callBack]="Swiff.CallBacks."+this.instance+"."+callBack;}params.flashVars=Object.toQueryString(vars);if(Browser.ie){properties.classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";
-params.movie=path;}else{properties.type="application/x-shockwave-flash";}properties.data=path;var build='<object id="'+id+'"';for(var property in properties){build+=" "+property+'="'+properties[property]+'"';
-}build+=">";for(var param in params){if(params[param]){build+='<param name="'+param+'" value="'+params[param]+'" />';}}build+="</object>";this.object=((container)?container.empty():new Element("div")).set("html",build).firstChild;
-},replaces:function(element){element=document.id(element,true);element.parentNode.replaceChild(this.toElement(),element);return this;},inject:function(element){document.id(element,true).appendChild(this.toElement());
-return this;},remote:function(){return Swiff.remote.apply(Swiff,[this.toElement()].append(arguments));}});Swiff.CallBacks={};Swiff.remote=function(obj,fn){var rs=obj.CallFunction('<invoke name="'+fn+'" returntype="javascript">'+__flash__argumentsToXML(arguments,2)+"</invoke>");
-return eval(rs);};})(); \ No newline at end of file
diff --git a/module/web/media/js/mootools-more-1.4.0.1.js b/module/web/media/js/mootools-more-1.4.0.1.js
deleted file mode 100644
index f3f8e4ee1..000000000
--- a/module/web/media/js/mootools-more-1.4.0.1.js
+++ /dev/null
@@ -1,216 +0,0 @@
-// MooTools: the javascript framework.
-// Load this file's selection again by visiting: http://mootools.net/more/c1cc18c2fff04bcc58921b4dff80a6f1
-// Or build this file again with packager using: packager build More/Form.Request More/Fx.Reveal More/Sortables More/Request.Periodical More/Color
-/*
----
-copyrights:
- - [MooTools](http://mootools.net)
-
-licenses:
- - [MIT License](http://mootools.net/license.txt)
-...
-*/
-MooTools.More={version:"1.4.0.1",build:"a4244edf2aa97ac8a196fc96082dd35af1abab87"};Class.Mutators.Binds=function(a){if(!this.prototype.initialize){this.implement("initialize",function(){});
-}return Array.from(a).concat(this.prototype.Binds||[]);};Class.Mutators.initialize=function(a){return function(){Array.from(this.Binds).each(function(b){var c=this[b];
-if(c){this[b]=c.bind(this);}},this);return a.apply(this,arguments);};};Class.Occlude=new Class({occlude:function(c,b){b=document.id(b||this.element);var a=b.retrieve(c||this.property);
-if(a&&!this.occluded){return(this.occluded=a);}this.occluded=false;b.store(c||this.property,this);return this.occluded;}});Class.refactor=function(b,a){Object.each(a,function(e,d){var c=b.prototype[d];
-c=(c&&c.$origin)||c||function(){};b.implement(d,(typeof e=="function")?function(){var f=this.previous;this.previous=c;var g=e.apply(this,arguments);this.previous=f;
-return g;}:e);});return b;};(function(){var b=function(e,d){var f=[];Object.each(d,function(g){Object.each(g,function(h){e.each(function(i){f.push(i+"-"+h+(i=="border"?"-width":""));
-});});});return f;};var c=function(f,e){var d=0;Object.each(e,function(h,g){if(g.test(f)){d=d+h.toInt();}});return d;};var a=function(d){return !!(!d||d.offsetHeight||d.offsetWidth);
-};Element.implement({measure:function(h){if(a(this)){return h.call(this);}var g=this.getParent(),e=[];while(!a(g)&&g!=document.body){e.push(g.expose());
-g=g.getParent();}var f=this.expose(),d=h.call(this);f();e.each(function(i){i();});return d;},expose:function(){if(this.getStyle("display")!="none"){return function(){};
-}var d=this.style.cssText;this.setStyles({display:"block",position:"absolute",visibility:"hidden"});return function(){this.style.cssText=d;}.bind(this);
-},getDimensions:function(d){d=Object.merge({computeSize:false},d);var i={x:0,y:0};var h=function(j,e){return(e.computeSize)?j.getComputedSize(e):j.getSize();
-};var f=this.getParent("body");if(f&&this.getStyle("display")=="none"){i=this.measure(function(){return h(this,d);});}else{if(f){try{i=h(this,d);}catch(g){}}}return Object.append(i,(i.x||i.x===0)?{width:i.x,height:i.y}:{x:i.width,y:i.height});
-},getComputedSize:function(d){d=Object.merge({styles:["padding","border"],planes:{height:["top","bottom"],width:["left","right"]},mode:"both"},d);var g={},e={width:0,height:0},f;
-if(d.mode=="vertical"){delete e.width;delete d.planes.width;}else{if(d.mode=="horizontal"){delete e.height;delete d.planes.height;}}b(d.styles,d.planes).each(function(h){g[h]=this.getStyle(h).toInt();
-},this);Object.each(d.planes,function(i,h){var k=h.capitalize(),j=this.getStyle(h);if(j=="auto"&&!f){f=this.getDimensions();}j=g[h]=(j=="auto")?f[h]:j.toInt();
-e["total"+k]=j;i.each(function(m){var l=c(m,g);e["computed"+m.capitalize()]=l;e["total"+k]+=l;});},this);return Object.append(e,g);}});})();(function(b){var a=Element.Position={options:{relativeTo:document.body,position:{x:"center",y:"center"},offset:{x:0,y:0}},getOptions:function(d,c){c=Object.merge({},a.options,c);
-a.setPositionOption(c);a.setEdgeOption(c);a.setOffsetOption(d,c);a.setDimensionsOption(d,c);return c;},setPositionOption:function(c){c.position=a.getCoordinateFromValue(c.position);
-},setEdgeOption:function(d){var c=a.getCoordinateFromValue(d.edge);d.edge=c?c:(d.position.x=="center"&&d.position.y=="center")?{x:"center",y:"center"}:{x:"left",y:"top"};
-},setOffsetOption:function(f,d){var c={x:0,y:0},g=f.measure(function(){return document.id(this.getOffsetParent());}),e=g.getScroll();if(!g||g==f.getDocument().body){return;
-}c=g.measure(function(){var i=this.getPosition();if(this.getStyle("position")=="fixed"){var h=window.getScroll();i.x+=h.x;i.y+=h.y;}return i;});d.offset={parentPositioned:g!=document.id(d.relativeTo),x:d.offset.x-c.x+e.x,y:d.offset.y-c.y+e.y};
-},setDimensionsOption:function(d,c){c.dimensions=d.getDimensions({computeSize:true,styles:["padding","border","margin"]});},getPosition:function(e,d){var c={};
-d=a.getOptions(e,d);var f=document.id(d.relativeTo)||document.body;a.setPositionCoordinates(d,c,f);if(d.edge){a.toEdge(c,d);}var g=d.offset;c.left=((c.x>=0||g.parentPositioned||d.allowNegative)?c.x:0).toInt();
-c.top=((c.y>=0||g.parentPositioned||d.allowNegative)?c.y:0).toInt();a.toMinMax(c,d);if(d.relFixedPosition||f.getStyle("position")=="fixed"){a.toRelFixedPosition(f,c);
-}if(d.ignoreScroll){a.toIgnoreScroll(f,c);}if(d.ignoreMargins){a.toIgnoreMargins(c,d);}c.left=Math.ceil(c.left);c.top=Math.ceil(c.top);delete c.x;delete c.y;
-return c;},setPositionCoordinates:function(k,g,d){var f=k.offset.y,h=k.offset.x,e=(d==document.body)?window.getScroll():d.getPosition(),j=e.y,c=e.x,i=window.getSize();
-switch(k.position.x){case"left":g.x=c+h;break;case"right":g.x=c+h+d.offsetWidth;break;default:g.x=c+((d==document.body?i.x:d.offsetWidth)/2)+h;break;}switch(k.position.y){case"top":g.y=j+f;
-break;case"bottom":g.y=j+f+d.offsetHeight;break;default:g.y=j+((d==document.body?i.y:d.offsetHeight)/2)+f;break;}},toMinMax:function(c,d){var f={left:"x",top:"y"},e;
-["minimum","maximum"].each(function(g){["left","top"].each(function(h){e=d[g]?d[g][f[h]]:null;if(e!=null&&((g=="minimum")?c[h]<e:c[h]>e)){c[h]=e;}});});
-},toRelFixedPosition:function(e,c){var d=window.getScroll();c.top+=d.y;c.left+=d.x;},toIgnoreScroll:function(e,d){var c=e.getScroll();d.top-=c.y;d.left-=c.x;
-},toIgnoreMargins:function(c,d){c.left+=d.edge.x=="right"?d.dimensions["margin-right"]:(d.edge.x!="center"?-d.dimensions["margin-left"]:-d.dimensions["margin-left"]+((d.dimensions["margin-right"]+d.dimensions["margin-left"])/2));
-c.top+=d.edge.y=="bottom"?d.dimensions["margin-bottom"]:(d.edge.y!="center"?-d.dimensions["margin-top"]:-d.dimensions["margin-top"]+((d.dimensions["margin-bottom"]+d.dimensions["margin-top"])/2));
-},toEdge:function(c,d){var e={},g=d.dimensions,f=d.edge;switch(f.x){case"left":e.x=0;break;case"right":e.x=-g.x-g.computedRight-g.computedLeft;break;default:e.x=-(Math.round(g.totalWidth/2));
-break;}switch(f.y){case"top":e.y=0;break;case"bottom":e.y=-g.y-g.computedTop-g.computedBottom;break;default:e.y=-(Math.round(g.totalHeight/2));break;}c.x+=e.x;
-c.y+=e.y;},getCoordinateFromValue:function(c){if(typeOf(c)!="string"){return c;}c=c.toLowerCase();return{x:c.test("left")?"left":(c.test("right")?"right":"center"),y:c.test(/upper|top/)?"top":(c.test("bottom")?"bottom":"center")};
-}};Element.implement({position:function(d){if(d&&(d.x!=null||d.y!=null)){return(b?b.apply(this,arguments):this);}var c=this.setStyle("position","absolute").calculatePosition(d);
-return(d&&d.returnPos)?c:this.setStyles(c);},calculatePosition:function(c){return a.getPosition(this,c);}});})(Element.prototype.position);var IframeShim=new Class({Implements:[Options,Events,Class.Occlude],options:{className:"iframeShim",src:'javascript:false;document.write("");',display:false,zIndex:null,margin:0,offset:{x:0,y:0},browsers:(Browser.ie6||(Browser.firefox&&Browser.version<3&&Browser.Platform.mac))},property:"IframeShim",initialize:function(b,a){this.element=document.id(b);
-if(this.occlude()){return this.occluded;}this.setOptions(a);this.makeShim();return this;},makeShim:function(){if(this.options.browsers){var c=this.element.getStyle("zIndex").toInt();
-if(!c){c=1;var b=this.element.getStyle("position");if(b=="static"||!b){this.element.setStyle("position","relative");}this.element.setStyle("zIndex",c);
-}c=((this.options.zIndex!=null||this.options.zIndex===0)&&c>this.options.zIndex)?this.options.zIndex:c-1;if(c<0){c=1;}this.shim=new Element("iframe",{src:this.options.src,scrolling:"no",frameborder:0,styles:{zIndex:c,position:"absolute",border:"none",filter:"progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)"},"class":this.options.className}).store("IframeShim",this);
-var a=(function(){this.shim.inject(this.element,"after");this[this.options.display?"show":"hide"]();this.fireEvent("inject");}).bind(this);if(!IframeShim.ready){window.addEvent("load",a);
-}else{a();}}else{this.position=this.hide=this.show=this.dispose=Function.from(this);}},position:function(){if(!IframeShim.ready||!this.shim){return this;
-}var a=this.element.measure(function(){return this.getSize();});if(this.options.margin!=undefined){a.x=a.x-(this.options.margin*2);a.y=a.y-(this.options.margin*2);
-this.options.offset.x+=this.options.margin;this.options.offset.y+=this.options.margin;}this.shim.set({width:a.x,height:a.y}).position({relativeTo:this.element,offset:this.options.offset});
-return this;},hide:function(){if(this.shim){this.shim.setStyle("display","none");}return this;},show:function(){if(this.shim){this.shim.setStyle("display","block");
-}return this.position();},dispose:function(){if(this.shim){this.shim.dispose();}return this;},destroy:function(){if(this.shim){this.shim.destroy();}return this;
-}});window.addEvent("load",function(){IframeShim.ready=true;});var Mask=new Class({Implements:[Options,Events],Binds:["position"],options:{style:{},"class":"mask",maskMargins:false,useIframeShim:true,iframeShimOptions:{}},initialize:function(b,a){this.target=document.id(b)||document.id(document.body);
-this.target.store("mask",this);this.setOptions(a);this.render();this.inject();},render:function(){this.element=new Element("div",{"class":this.options["class"],id:this.options.id||"mask-"+String.uniqueID(),styles:Object.merge({},this.options.style,{display:"none"}),events:{click:function(a){this.fireEvent("click",a);
-if(this.options.hideOnClick){this.hide();}}.bind(this)}});this.hidden=true;},toElement:function(){return this.element;},inject:function(b,a){a=a||(this.options.inject?this.options.inject.where:"")||this.target==document.body?"inside":"after";
-b=b||(this.options.inject&&this.options.inject.target)||this.target;this.element.inject(b,a);if(this.options.useIframeShim){this.shim=new IframeShim(this.element,this.options.iframeShimOptions);
-this.addEvents({show:this.shim.show.bind(this.shim),hide:this.shim.hide.bind(this.shim),destroy:this.shim.destroy.bind(this.shim)});}},position:function(){this.resize(this.options.width,this.options.height);
-this.element.position({relativeTo:this.target,position:"topLeft",ignoreMargins:!this.options.maskMargins,ignoreScroll:this.target==document.body});return this;
-},resize:function(a,e){var b={styles:["padding","border"]};if(this.options.maskMargins){b.styles.push("margin");}var d=this.target.getComputedSize(b);if(this.target==document.body){this.element.setStyles({width:0,height:0});
-var c=window.getScrollSize();if(d.totalHeight<c.y){d.totalHeight=c.y;}if(d.totalWidth<c.x){d.totalWidth=c.x;}}this.element.setStyles({width:Array.pick([a,d.totalWidth,d.x]),height:Array.pick([e,d.totalHeight,d.y])});
-return this;},show:function(){if(!this.hidden){return this;}window.addEvent("resize",this.position);this.position();this.showMask.apply(this,arguments);
-return this;},showMask:function(){this.element.setStyle("display","block");this.hidden=false;this.fireEvent("show");},hide:function(){if(this.hidden){return this;
-}window.removeEvent("resize",this.position);this.hideMask.apply(this,arguments);if(this.options.destroyOnHide){return this.destroy();}return this;},hideMask:function(){this.element.setStyle("display","none");
-this.hidden=true;this.fireEvent("hide");},toggle:function(){this[this.hidden?"show":"hide"]();},destroy:function(){this.hide();this.element.destroy();this.fireEvent("destroy");
-this.target.eliminate("mask");}});Element.Properties.mask={set:function(b){var a=this.retrieve("mask");if(a){a.destroy();}return this.eliminate("mask").store("mask:options",b);
-},get:function(){var a=this.retrieve("mask");if(!a){a=new Mask(this,this.retrieve("mask:options"));this.store("mask",a);}return a;}};Element.implement({mask:function(a){if(a){this.set("mask",a);
-}this.get("mask").show();return this;},unmask:function(){this.get("mask").hide();return this;}});var Spinner=new Class({Extends:Mask,Implements:Chain,options:{"class":"spinner",containerPosition:{},content:{"class":"spinner-content"},messageContainer:{"class":"spinner-msg"},img:{"class":"spinner-img"},fxOptions:{link:"chain"}},initialize:function(c,a){this.target=document.id(c)||document.id(document.body);
-this.target.store("spinner",this);this.setOptions(a);this.render();this.inject();var b=function(){this.active=false;}.bind(this);this.addEvents({hide:b,show:b});
-},render:function(){this.parent();this.element.set("id",this.options.id||"spinner-"+String.uniqueID());this.content=document.id(this.options.content)||new Element("div",this.options.content);
-this.content.inject(this.element);if(this.options.message){this.msg=document.id(this.options.message)||new Element("p",this.options.messageContainer).appendText(this.options.message);
-this.msg.inject(this.content);}if(this.options.img){this.img=document.id(this.options.img)||new Element("div",this.options.img);this.img.inject(this.content);
-}this.element.set("tween",this.options.fxOptions);},show:function(a){if(this.active){return this.chain(this.show.bind(this));}if(!this.hidden){this.callChain.delay(20,this);
-return this;}this.active=true;return this.parent(a);},showMask:function(a){var b=function(){this.content.position(Object.merge({relativeTo:this.element},this.options.containerPosition));
-}.bind(this);if(a){this.parent();b();}else{if(!this.options.style.opacity){this.options.style.opacity=this.element.getStyle("opacity").toFloat();}this.element.setStyles({display:"block",opacity:0}).tween("opacity",this.options.style.opacity);
-b();this.hidden=false;this.fireEvent("show");this.callChain();}},hide:function(a){if(this.active){return this.chain(this.hide.bind(this));}if(this.hidden){this.callChain.delay(20,this);
-return this;}this.active=true;return this.parent(a);},hideMask:function(a){if(a){return this.parent();}this.element.tween("opacity",0).get("tween").chain(function(){this.element.setStyle("display","none");
-this.hidden=true;this.fireEvent("hide");this.callChain();}.bind(this));},destroy:function(){this.content.destroy();this.parent();this.target.eliminate("spinner");
-}});Request=Class.refactor(Request,{options:{useSpinner:false,spinnerOptions:{},spinnerTarget:false},initialize:function(a){this._send=this.send;this.send=function(b){var c=this.getSpinner();
-if(c){c.chain(this._send.pass(b,this)).show();}else{this._send(b);}return this;};this.previous(a);},getSpinner:function(){if(!this.spinner){var b=document.id(this.options.spinnerTarget)||document.id(this.options.update);
-if(this.options.useSpinner&&b){b.set("spinner",this.options.spinnerOptions);var a=this.spinner=b.get("spinner");["complete","exception","cancel"].each(function(c){this.addEvent(c,a.hide.bind(a));
-},this);}}return this.spinner;}});Element.Properties.spinner={set:function(a){var b=this.retrieve("spinner");if(b){b.destroy();}return this.eliminate("spinner").store("spinner:options",a);
-},get:function(){var a=this.retrieve("spinner");if(!a){a=new Spinner(this,this.retrieve("spinner:options"));this.store("spinner",a);}return a;}};Element.implement({spin:function(a){if(a){this.set("spinner",a);
-}this.get("spinner").show();return this;},unspin:function(){this.get("spinner").hide();return this;}});String.implement({parseQueryString:function(d,a){if(d==null){d=true;
-}if(a==null){a=true;}var c=this.split(/[&;]/),b={};if(!c.length){return b;}c.each(function(i){var e=i.indexOf("=")+1,g=e?i.substr(e):"",f=e?i.substr(0,e-1).match(/([^\]\[]+|(\B)(?=\]))/g):[i],h=b;
-if(!f){return;}if(a){g=decodeURIComponent(g);}f.each(function(k,j){if(d){k=decodeURIComponent(k);}var l=h[k];if(j<f.length-1){h=h[k]=l||{};}else{if(typeOf(l)=="array"){l.push(g);
-}else{h[k]=l!=null?[l,g]:g;}}});});return b;},cleanQueryString:function(a){return this.split("&").filter(function(e){var b=e.indexOf("="),c=b<0?"":e.substr(0,b),d=e.substr(b+1);
-return a?a.call(null,c,d):(d||d===0);}).join("&");}});(function(){Events.Pseudos=function(h,e,f){var d="_monitorEvents:";var c=function(i){return{store:i.store?function(j,k){i.store(d+j,k);
-}:function(j,k){(i._monitorEvents||(i._monitorEvents={}))[j]=k;},retrieve:i.retrieve?function(j,k){return i.retrieve(d+j,k);}:function(j,k){if(!i._monitorEvents){return k;
-}return i._monitorEvents[j]||k;}};};var g=function(k){if(k.indexOf(":")==-1||!h){return null;}var j=Slick.parse(k).expressions[0][0],p=j.pseudos,i=p.length,o=[];
-while(i--){var n=p[i].key,m=h[n];if(m!=null){o.push({event:j.tag,value:p[i].value,pseudo:n,original:k,listener:m});}}return o.length?o:null;};return{addEvent:function(m,p,j){var n=g(m);
-if(!n){return e.call(this,m,p,j);}var k=c(this),r=k.retrieve(m,[]),i=n[0].event,l=Array.slice(arguments,2),o=p,q=this;n.each(function(s){var t=s.listener,u=o;
-if(t==false){i+=":"+s.pseudo+"("+s.value+")";}else{o=function(){t.call(q,s,u,arguments,o);};}});r.include({type:i,event:p,monitor:o});k.store(m,r);if(m!=i){e.apply(this,[m,p].concat(l));
-}return e.apply(this,[i,o].concat(l));},removeEvent:function(m,l){var k=g(m);if(!k){return f.call(this,m,l);}var n=c(this),j=n.retrieve(m);if(!j){return this;
-}var i=Array.slice(arguments,2);f.apply(this,[m,l].concat(i));j.each(function(o,p){if(!l||o.event==l){f.apply(this,[o.type,o.monitor].concat(i));}delete j[p];
-},this);n.store(m,j);return this;}};};var b={once:function(e,f,d,c){f.apply(this,d);this.removeEvent(e.event,c).removeEvent(e.original,f);},throttle:function(d,e,c){if(!e._throttled){e.apply(this,c);
-e._throttled=setTimeout(function(){e._throttled=false;},d.value||250);}},pause:function(d,e,c){clearTimeout(e._pause);e._pause=e.delay(d.value||250,this,c);
-}};Events.definePseudo=function(c,d){b[c]=d;return this;};Events.lookupPseudo=function(c){return b[c];};var a=Events.prototype;Events.implement(Events.Pseudos(b,a.addEvent,a.removeEvent));
-["Request","Fx"].each(function(c){if(this[c]){this[c].implement(Events.prototype);}});})();(function(){var d={relay:false},c=["once","throttle","pause"],b=c.length;
-while(b--){d[c[b]]=Events.lookupPseudo(c[b]);}DOMEvent.definePseudo=function(e,f){d[e]=f;return this;};var a=Element.prototype;[Element,Window,Document].invoke("implement",Events.Pseudos(d,a.addEvent,a.removeEvent));
-})();if(!window.Form){window.Form={};}(function(){Form.Request=new Class({Binds:["onSubmit","onFormValidate"],Implements:[Options,Events,Class.Occlude],options:{requestOptions:{evalScripts:true,useSpinner:true,emulation:false,link:"ignore"},sendButtonClicked:true,extraData:{},resetForm:true},property:"form.request",initialize:function(b,c,a){this.element=document.id(b);
-if(this.occlude()){return this.occluded;}this.setOptions(a).setTarget(c).attach();},setTarget:function(a){this.target=document.id(a);if(!this.request){this.makeRequest();
-}else{this.request.setOptions({update:this.target});}return this;},toElement:function(){return this.element;},makeRequest:function(){var a=this;this.request=new Request.HTML(Object.merge({update:this.target,emulation:false,spinnerTarget:this.element,method:this.element.get("method")||"post"},this.options.requestOptions)).addEvents({success:function(c,e,d,b){["complete","success"].each(function(f){a.fireEvent(f,[a.target,c,e,d,b]);
-});},failure:function(){a.fireEvent("complete",arguments).fireEvent("failure",arguments);},exception:function(){a.fireEvent("failure",arguments);}});return this.attachReset();
-},attachReset:function(){if(!this.options.resetForm){return this;}this.request.addEvent("success",function(){Function.attempt(function(){this.element.reset();
-}.bind(this));if(window.OverText){OverText.update();}}.bind(this));return this;},attach:function(a){var c=(a!=false)?"addEvent":"removeEvent";this.element[c]("click:relay(button, input[type=submit])",this.saveClickedButton.bind(this));
-var b=this.element.retrieve("validator");if(b){b[c]("onFormValidate",this.onFormValidate);}else{this.element[c]("submit",this.onSubmit);}return this;},detach:function(){return this.attach(false);
-},enable:function(){return this.attach();},disable:function(){return this.detach();},onFormValidate:function(c,b,a){if(!a){return;}var d=this.element.retrieve("validator");
-if(c||(d&&!d.options.stopOnFailure)){a.stop();this.send();}},onSubmit:function(a){var b=this.element.retrieve("validator");if(b){this.element.removeEvent("submit",this.onSubmit);
-b.addEvent("onFormValidate",this.onFormValidate);this.element.validate();return;}if(a){a.stop();}this.send();},saveClickedButton:function(b,c){var a=c.get("name");
-if(!a||!this.options.sendButtonClicked){return;}this.options.extraData[a]=c.get("value")||true;this.clickedCleaner=function(){delete this.options.extraData[a];
-this.clickedCleaner=function(){};}.bind(this);},clickedCleaner:function(){},send:function(){var b=this.element.toQueryString().trim(),a=Object.toQueryString(this.options.extraData);
-if(b){b+="&"+a;}else{b=a;}this.fireEvent("send",[this.element,b.parseQueryString()]);this.request.send({data:b,url:this.options.requestOptions.url||this.element.get("action")});
-this.clickedCleaner();return this;}});Element.implement("formUpdate",function(c,b){var a=this.retrieve("form.request");if(!a){a=new Form.Request(this,c,b);
-}else{if(c){a.setTarget(c);}if(b){a.setOptions(b).makeRequest();}}a.send();return this;});})();Element.implement({isDisplayed:function(){return this.getStyle("display")!="none";
-},isVisible:function(){var a=this.offsetWidth,b=this.offsetHeight;return(a==0&&b==0)?false:(a>0&&b>0)?true:this.style.display!="none";},toggle:function(){return this[this.isDisplayed()?"hide":"show"]();
-},hide:function(){var b;try{b=this.getStyle("display");}catch(a){}if(b=="none"){return this;}return this.store("element:_originalDisplay",b||"").setStyle("display","none");
-},show:function(a){if(!a&&this.isDisplayed()){return this;}a=a||this.retrieve("element:_originalDisplay")||"block";return this.setStyle("display",(a=="none")?"block":a);
-},swapClass:function(a,b){return this.removeClass(a).addClass(b);}});Document.implement({clearSelection:function(){if(window.getSelection){var a=window.getSelection();
-if(a&&a.removeAllRanges){a.removeAllRanges();}}else{if(document.selection&&document.selection.empty){try{document.selection.empty();}catch(b){}}}}});(function(){var a=function(d){var b=d.options.hideInputs;
-if(window.OverText){var c=[null];OverText.each(function(e){c.include("."+e.options.labelClass);});if(c){b+=c.join(", ");}}return(b)?d.element.getElements(b):null;
-};Fx.Reveal=new Class({Extends:Fx.Morph,options:{link:"cancel",styles:["padding","border","margin"],transitionOpacity:!Browser.ie6,mode:"vertical",display:function(){return this.element.get("tag")!="tr"?"block":"table-row";
-},opacity:1,hideInputs:Browser.ie?"select, input, textarea, object, embed":null},dissolve:function(){if(!this.hiding&&!this.showing){if(this.element.getStyle("display")!="none"){this.hiding=true;
-this.showing=false;this.hidden=true;this.cssText=this.element.style.cssText;var d=this.element.getComputedSize({styles:this.options.styles,mode:this.options.mode});
-if(this.options.transitionOpacity){d.opacity=this.options.opacity;}var c={};Object.each(d,function(f,e){c[e]=[f,0];});this.element.setStyles({display:Function.from(this.options.display).call(this),overflow:"hidden"});
-var b=a(this);if(b){b.setStyle("visibility","hidden");}this.$chain.unshift(function(){if(this.hidden){this.hiding=false;this.element.style.cssText=this.cssText;
-this.element.setStyle("display","none");if(b){b.setStyle("visibility","visible");}}this.fireEvent("hide",this.element);this.callChain();}.bind(this));this.start(c);
-}else{this.callChain.delay(10,this);this.fireEvent("complete",this.element);this.fireEvent("hide",this.element);}}else{if(this.options.link=="chain"){this.chain(this.dissolve.bind(this));
-}else{if(this.options.link=="cancel"&&!this.hiding){this.cancel();this.dissolve();}}}return this;},reveal:function(){if(!this.showing&&!this.hiding){if(this.element.getStyle("display")=="none"){this.hiding=false;
-this.showing=true;this.hidden=false;this.cssText=this.element.style.cssText;var d;this.element.measure(function(){d=this.element.getComputedSize({styles:this.options.styles,mode:this.options.mode});
-}.bind(this));if(this.options.heightOverride!=null){d.height=this.options.heightOverride.toInt();}if(this.options.widthOverride!=null){d.width=this.options.widthOverride.toInt();
-}if(this.options.transitionOpacity){this.element.setStyle("opacity",0);d.opacity=this.options.opacity;}var c={height:0,display:Function.from(this.options.display).call(this)};
-Object.each(d,function(f,e){c[e]=0;});c.overflow="hidden";this.element.setStyles(c);var b=a(this);if(b){b.setStyle("visibility","hidden");}this.$chain.unshift(function(){this.element.style.cssText=this.cssText;
-this.element.setStyle("display",Function.from(this.options.display).call(this));if(!this.hidden){this.showing=false;}if(b){b.setStyle("visibility","visible");
-}this.callChain();this.fireEvent("show",this.element);}.bind(this));this.start(d);}else{this.callChain();this.fireEvent("complete",this.element);this.fireEvent("show",this.element);
-}}else{if(this.options.link=="chain"){this.chain(this.reveal.bind(this));}else{if(this.options.link=="cancel"&&!this.showing){this.cancel();this.reveal();
-}}}return this;},toggle:function(){if(this.element.getStyle("display")=="none"){this.reveal();}else{this.dissolve();}return this;},cancel:function(){this.parent.apply(this,arguments);
-if(this.cssText!=null){this.element.style.cssText=this.cssText;}this.hiding=false;this.showing=false;return this;}});Element.Properties.reveal={set:function(b){this.get("reveal").cancel().setOptions(b);
-return this;},get:function(){var b=this.retrieve("reveal");if(!b){b=new Fx.Reveal(this);this.store("reveal",b);}return b;}};Element.Properties.dissolve=Element.Properties.reveal;
-Element.implement({reveal:function(b){this.get("reveal").setOptions(b).reveal();return this;},dissolve:function(b){this.get("reveal").setOptions(b).dissolve();
-return this;},nix:function(b){var c=Array.link(arguments,{destroy:Type.isBoolean,options:Type.isObject});this.get("reveal").setOptions(b).dissolve().chain(function(){this[c.destroy?"destroy":"dispose"]();
-}.bind(this));return this;},wink:function(){var c=Array.link(arguments,{duration:Type.isNumber,options:Type.isObject});var b=this.get("reveal").setOptions(c.options);
-b.reveal().chain(function(){(function(){b.dissolve();}).delay(c.duration||2000);});}});})();var Drag=new Class({Implements:[Events,Options],options:{snap:6,unit:"px",grid:false,style:true,limit:false,handle:false,invert:false,preventDefault:false,stopPropagation:false,modifiers:{x:"left",y:"top"}},initialize:function(){var b=Array.link(arguments,{options:Type.isObject,element:function(c){return c!=null;
-}});this.element=document.id(b.element);this.document=this.element.getDocument();this.setOptions(b.options||{});var a=typeOf(this.options.handle);this.handles=((a=="array"||a=="collection")?$$(this.options.handle):document.id(this.options.handle))||this.element;
-this.mouse={now:{},pos:{}};this.value={start:{},now:{}};this.selection=(Browser.ie)?"selectstart":"mousedown";if(Browser.ie&&!Drag.ondragstartFixed){document.ondragstart=Function.from(false);
-Drag.ondragstartFixed=true;}this.bound={start:this.start.bind(this),check:this.check.bind(this),drag:this.drag.bind(this),stop:this.stop.bind(this),cancel:this.cancel.bind(this),eventStop:Function.from(false)};
-this.attach();},attach:function(){this.handles.addEvent("mousedown",this.bound.start);return this;},detach:function(){this.handles.removeEvent("mousedown",this.bound.start);
-return this;},start:function(a){var j=this.options;if(a.rightClick){return;}if(j.preventDefault){a.preventDefault();}if(j.stopPropagation){a.stopPropagation();
-}this.mouse.start=a.page;this.fireEvent("beforeStart",this.element);var c=j.limit;this.limit={x:[],y:[]};var e,g;for(e in j.modifiers){if(!j.modifiers[e]){continue;
-}var b=this.element.getStyle(j.modifiers[e]);if(b&&!b.match(/px$/)){if(!g){g=this.element.getCoordinates(this.element.getOffsetParent());}b=g[j.modifiers[e]];
-}if(j.style){this.value.now[e]=(b||0).toInt();}else{this.value.now[e]=this.element[j.modifiers[e]];}if(j.invert){this.value.now[e]*=-1;}this.mouse.pos[e]=a.page[e]-this.value.now[e];
-if(c&&c[e]){var d=2;while(d--){var f=c[e][d];if(f||f===0){this.limit[e][d]=(typeof f=="function")?f():f;}}}}if(typeOf(this.options.grid)=="number"){this.options.grid={x:this.options.grid,y:this.options.grid};
-}var h={mousemove:this.bound.check,mouseup:this.bound.cancel};h[this.selection]=this.bound.eventStop;this.document.addEvents(h);},check:function(a){if(this.options.preventDefault){a.preventDefault();
-}var b=Math.round(Math.sqrt(Math.pow(a.page.x-this.mouse.start.x,2)+Math.pow(a.page.y-this.mouse.start.y,2)));if(b>this.options.snap){this.cancel();this.document.addEvents({mousemove:this.bound.drag,mouseup:this.bound.stop});
-this.fireEvent("start",[this.element,a]).fireEvent("snap",this.element);}},drag:function(b){var a=this.options;if(a.preventDefault){b.preventDefault();
-}this.mouse.now=b.page;for(var c in a.modifiers){if(!a.modifiers[c]){continue;}this.value.now[c]=this.mouse.now[c]-this.mouse.pos[c];if(a.invert){this.value.now[c]*=-1;
-}if(a.limit&&this.limit[c]){if((this.limit[c][1]||this.limit[c][1]===0)&&(this.value.now[c]>this.limit[c][1])){this.value.now[c]=this.limit[c][1];}else{if((this.limit[c][0]||this.limit[c][0]===0)&&(this.value.now[c]<this.limit[c][0])){this.value.now[c]=this.limit[c][0];
-}}}if(a.grid[c]){this.value.now[c]-=((this.value.now[c]-(this.limit[c][0]||0))%a.grid[c]);}if(a.style){this.element.setStyle(a.modifiers[c],this.value.now[c]+a.unit);
-}else{this.element[a.modifiers[c]]=this.value.now[c];}}this.fireEvent("drag",[this.element,b]);},cancel:function(a){this.document.removeEvents({mousemove:this.bound.check,mouseup:this.bound.cancel});
-if(a){this.document.removeEvent(this.selection,this.bound.eventStop);this.fireEvent("cancel",this.element);}},stop:function(b){var a={mousemove:this.bound.drag,mouseup:this.bound.stop};
-a[this.selection]=this.bound.eventStop;this.document.removeEvents(a);if(b){this.fireEvent("complete",[this.element,b]);}}});Element.implement({makeResizable:function(a){var b=new Drag(this,Object.merge({modifiers:{x:"width",y:"height"}},a));
-this.store("resizer",b);return b.addEvent("drag",function(){this.fireEvent("resize",b);}.bind(this));}});Drag.Move=new Class({Extends:Drag,options:{droppables:[],container:false,precalculate:false,includeMargins:true,checkDroppables:true},initialize:function(b,a){this.parent(b,a);
-b=this.element;this.droppables=$$(this.options.droppables);this.container=document.id(this.options.container);if(this.container&&typeOf(this.container)!="element"){this.container=document.id(this.container.getDocument().body);
-}if(this.options.style){if(this.options.modifiers.x=="left"&&this.options.modifiers.y=="top"){var c=b.getOffsetParent(),d=b.getStyles("left","top");if(c&&(d.left=="auto"||d.top=="auto")){b.setPosition(b.getPosition(c));
-}}if(b.getStyle("position")=="static"){b.setStyle("position","absolute");}}this.addEvent("start",this.checkDroppables,true);this.overed=null;},start:function(a){if(this.container){this.options.limit=this.calculateLimit();
-}if(this.options.precalculate){this.positions=this.droppables.map(function(b){return b.getCoordinates();});}this.parent(a);},calculateLimit:function(){var j=this.element,e=this.container,d=document.id(j.getOffsetParent())||document.body,h=e.getCoordinates(d),c={},b={},k={},g={},m={};
-["top","right","bottom","left"].each(function(q){c[q]=j.getStyle("margin-"+q).toInt();b[q]=j.getStyle("border-"+q).toInt();k[q]=e.getStyle("margin-"+q).toInt();
-g[q]=e.getStyle("border-"+q).toInt();m[q]=d.getStyle("padding-"+q).toInt();},this);var f=j.offsetWidth+c.left+c.right,p=j.offsetHeight+c.top+c.bottom,i=0,l=0,o=h.right-g.right-f,a=h.bottom-g.bottom-p;
-if(this.options.includeMargins){i+=c.left;l+=c.top;}else{o+=c.right;a+=c.bottom;}if(j.getStyle("position")=="relative"){var n=j.getCoordinates(d);n.left-=j.getStyle("left").toInt();
-n.top-=j.getStyle("top").toInt();i-=n.left;l-=n.top;if(e.getStyle("position")!="relative"){i+=g.left;l+=g.top;}o+=c.left-n.left;a+=c.top-n.top;if(e!=d){i+=k.left+m.left;
-l+=((Browser.ie6||Browser.ie7)?0:k.top)+m.top;}}else{i-=c.left;l-=c.top;if(e!=d){i+=h.left+g.left;l+=h.top+g.top;}}return{x:[i,o],y:[l,a]};},getDroppableCoordinates:function(c){var b=c.getCoordinates();
-if(c.getStyle("position")=="fixed"){var a=window.getScroll();b.left+=a.x;b.right+=a.x;b.top+=a.y;b.bottom+=a.y;}return b;},checkDroppables:function(){var a=this.droppables.filter(function(d,c){d=this.positions?this.positions[c]:this.getDroppableCoordinates(d);
-var b=this.mouse.now;return(b.x>d.left&&b.x<d.right&&b.y<d.bottom&&b.y>d.top);},this).getLast();if(this.overed!=a){if(this.overed){this.fireEvent("leave",[this.element,this.overed]);
-}if(a){this.fireEvent("enter",[this.element,a]);}this.overed=a;}},drag:function(a){this.parent(a);if(this.options.checkDroppables&&this.droppables.length){this.checkDroppables();
-}},stop:function(a){this.checkDroppables();this.fireEvent("drop",[this.element,this.overed,a]);this.overed=null;return this.parent(a);}});Element.implement({makeDraggable:function(a){var b=new Drag.Move(this,a);
-this.store("dragger",b);return b;}});var Sortables=new Class({Implements:[Events,Options],options:{opacity:1,clone:false,revert:false,handle:false,dragOptions:{}},initialize:function(a,b){this.setOptions(b);
-this.elements=[];this.lists=[];this.idle=true;this.addLists($$(document.id(a)||a));if(!this.options.clone){this.options.revert=false;}if(this.options.revert){this.effect=new Fx.Morph(null,Object.merge({duration:250,link:"cancel"},this.options.revert));
-}},attach:function(){this.addLists(this.lists);return this;},detach:function(){this.lists=this.removeLists(this.lists);return this;},addItems:function(){Array.flatten(arguments).each(function(a){this.elements.push(a);
-var b=a.retrieve("sortables:start",function(c){this.start.call(this,c,a);}.bind(this));(this.options.handle?a.getElement(this.options.handle)||a:a).addEvent("mousedown",b);
-},this);return this;},addLists:function(){Array.flatten(arguments).each(function(a){this.lists.include(a);this.addItems(a.getChildren());},this);return this;
-},removeItems:function(){return $$(Array.flatten(arguments).map(function(a){this.elements.erase(a);var b=a.retrieve("sortables:start");(this.options.handle?a.getElement(this.options.handle)||a:a).removeEvent("mousedown",b);
-return a;},this));},removeLists:function(){return $$(Array.flatten(arguments).map(function(a){this.lists.erase(a);this.removeItems(a.getChildren());return a;
-},this));},getClone:function(b,a){if(!this.options.clone){return new Element(a.tagName).inject(document.body);}if(typeOf(this.options.clone)=="function"){return this.options.clone.call(this,b,a,this.list);
-}var c=a.clone(true).setStyles({margin:0,position:"absolute",visibility:"hidden",width:a.getStyle("width")}).addEvent("mousedown",function(d){a.fireEvent("mousedown",d);
-});if(c.get("html").test("radio")){c.getElements("input[type=radio]").each(function(d,e){d.set("name","clone_"+e);if(d.get("checked")){a.getElements("input[type=radio]")[e].set("checked",true);
-}});}return c.inject(this.list).setPosition(a.getPosition(a.getOffsetParent()));},getDroppables:function(){var a=this.list.getChildren().erase(this.clone).erase(this.element);
-if(!this.options.constrain){a.append(this.lists).erase(this.list);}return a;},insert:function(c,b){var a="inside";if(this.lists.contains(b)){this.list=b;
-this.drag.droppables=this.getDroppables();}else{a=this.element.getAllPrevious().contains(b)?"before":"after";}this.element.inject(b,a);this.fireEvent("sort",[this.element,this.clone]);
-},start:function(b,a){if(!this.idle||b.rightClick||["button","input","a","textarea"].contains(b.target.get("tag"))){return;}this.idle=false;this.element=a;
-this.opacity=a.getStyle("opacity");this.list=a.getParent();this.clone=this.getClone(b,a);this.drag=new Drag.Move(this.clone,Object.merge({droppables:this.getDroppables()},this.options.dragOptions)).addEvents({onSnap:function(){b.stop();
-this.clone.setStyle("visibility","visible");this.element.setStyle("opacity",this.options.opacity||0);this.fireEvent("start",[this.element,this.clone]);
-}.bind(this),onEnter:this.insert.bind(this),onCancel:this.end.bind(this),onComplete:this.end.bind(this)});this.clone.inject(this.element,"before");this.drag.start(b);
-},end:function(){this.drag.detach();this.element.setStyle("opacity",this.opacity);if(this.effect){var b=this.element.getStyles("width","height"),d=this.clone,c=d.computePosition(this.element.getPosition(this.clone.getOffsetParent()));
-var a=function(){this.removeEvent("cancel",a);d.destroy();};this.effect.element=d;this.effect.start({top:c.top,left:c.left,width:b.width,height:b.height,opacity:0.25}).addEvent("cancel",a).chain(a);
-}else{this.clone.destroy();}this.reset();},reset:function(){this.idle=true;this.fireEvent("complete",this.element);},serialize:function(){var c=Array.link(arguments,{modifier:Type.isFunction,index:function(d){return d!=null;
-}});var b=this.lists.map(function(d){return d.getChildren().map(c.modifier||function(e){return e.get("id");},this);},this);var a=c.index;if(this.lists.length==1){a=0;
-}return(a||a===0)&&a>=0&&a<this.lists.length?b[a]:b;}});Request.implement({options:{initialDelay:5000,delay:5000,limit:60000},startTimer:function(b){var a=function(){if(!this.running){this.send({data:b});
-}};this.lastDelay=this.options.initialDelay;this.timer=a.delay(this.lastDelay,this);this.completeCheck=function(c){clearTimeout(this.timer);this.lastDelay=(c)?this.options.delay:(this.lastDelay+this.options.delay).min(this.options.limit);
-this.timer=a.delay(this.lastDelay,this);};return this.addEvent("complete",this.completeCheck);},stopTimer:function(){clearTimeout(this.timer);return this.removeEvent("complete",this.completeCheck);
-}});(function(){var a=this.Color=new Type("Color",function(c,d){if(arguments.length>=3){d="rgb";c=Array.slice(arguments,0,3);}else{if(typeof c=="string"){if(c.match(/rgb/)){c=c.rgbToHex().hexToRgb(true);
-}else{if(c.match(/hsb/)){c=c.hsbToRgb();}else{c=c.hexToRgb(true);}}}}d=d||"rgb";switch(d){case"hsb":var b=c;c=c.hsbToRgb();c.hsb=b;break;case"hex":c=c.hexToRgb(true);
-break;}c.rgb=c.slice(0,3);c.hsb=c.hsb||c.rgbToHsb();c.hex=c.rgbToHex();return Object.append(c,this);});a.implement({mix:function(){var b=Array.slice(arguments);
-var d=(typeOf(b.getLast())=="number")?b.pop():50;var c=this.slice();b.each(function(e){e=new a(e);for(var f=0;f<3;f++){c[f]=Math.round((c[f]/100*(100-d))+(e[f]/100*d));
-}});return new a(c,"rgb");},invert:function(){return new a(this.map(function(b){return 255-b;}));},setHue:function(b){return new a([b,this.hsb[1],this.hsb[2]],"hsb");
-},setSaturation:function(b){return new a([this.hsb[0],b,this.hsb[2]],"hsb");},setBrightness:function(b){return new a([this.hsb[0],this.hsb[1],b],"hsb");
-}});this.$RGB=function(e,d,c){return new a([e,d,c],"rgb");};this.$HSB=function(e,d,c){return new a([e,d,c],"hsb");};this.$HEX=function(b){return new a(b,"hex");
-};Array.implement({rgbToHsb:function(){var c=this[0],d=this[1],k=this[2],h=0;var j=Math.max(c,d,k),f=Math.min(c,d,k);var l=j-f;var i=j/255,g=(j!=0)?l/j:0;
-if(g!=0){var e=(j-c)/l;var b=(j-d)/l;var m=(j-k)/l;if(c==j){h=m-b;}else{if(d==j){h=2+e-m;}else{h=4+b-e;}}h/=6;if(h<0){h++;}}return[Math.round(h*360),Math.round(g*100),Math.round(i*100)];
-},hsbToRgb:function(){var d=Math.round(this[2]/100*255);if(this[1]==0){return[d,d,d];}else{var b=this[0]%360;var g=b%60;var h=Math.round((this[2]*(100-this[1]))/10000*255);
-var e=Math.round((this[2]*(6000-this[1]*g))/600000*255);var c=Math.round((this[2]*(6000-this[1]*(60-g)))/600000*255);switch(Math.floor(b/60)){case 0:return[d,c,h];
-case 1:return[e,d,h];case 2:return[h,d,c];case 3:return[h,e,d];case 4:return[c,h,d];case 5:return[d,h,e];}}return false;}});String.implement({rgbToHsb:function(){var b=this.match(/\d{1,3}/g);
-return(b)?b.rgbToHsb():null;},hsbToRgb:function(){var b=this.match(/\d{1,3}/g);return(b)?b.hsbToRgb():null;}});})(); \ No newline at end of file
diff --git a/module/web/media/js/package_ui.js b/module/web/media/js/package_ui.js
deleted file mode 100644
index 3ea965649..000000000
--- a/module/web/media/js/package_ui.js
+++ /dev/null
@@ -1,377 +0,0 @@
-var root = this;
-
-document.addEvent("domready", function() {
- root.load = new Fx.Tween($("load-indicator"), {link: "cancel"});
- root.load.set("opacity", 0);
-
-
- root.packageBox = new MooDialog({destroyOnHide: false});
- root.packageBox.setContent($('pack_box'));
-
- $('pack_reset').addEvent('click', function() {
- $('pack_form').reset();
- root.packageBox.close();
- });
-});
-
-function indicateLoad() {
- //$("load-indicator").reveal();
- root.load.start("opacity", 1)
-}
-
-function indicateFinish() {
- root.load.start("opacity", 0)
-}
-
-function indicateSuccess() {
- indicateFinish();
- root.notify.alert('{{_("Success")}}.', {
- 'className': 'success'
- });
-}
-
-function indicateFail() {
- indicateFinish();
- root.notify.alert('{{_("Failed")}}.', {
- 'className': 'error'
- });
-}
-
-var PackageUI = new Class({
- initialize: function(url, type) {
- this.url = url;
- this.type = type;
- this.packages = [];
- this.parsePackages();
-
- this.sorts = new Sortables($("package-list"), {
- constrain: false,
- clone: true,
- revert: true,
- opacity: 0.4,
- handle: ".package_drag",
- onComplete: this.saveSort.bind(this)
- });
-
- $("del_finished").addEvent("click", this.deleteFinished.bind(this));
- $("restart_failed").addEvent("click", this.restartFailed.bind(this));
-
- },
-
- parsePackages: function() {
- $("package-list").getChildren("li").each(function(ele) {
- var id = ele.getFirst().get("id").match(/[0-9]+/);
- this.packages.push(new Package(this, id, ele))
- }.bind(this))
- },
-
- loadPackages: function() {
- },
-
- deleteFinished: function() {
- indicateLoad();
- new Request.JSON({
- method: 'get',
- url: '/api/deleteFinished',
- onSuccess: function(data) {
- if (data.length > 0) {
- window.location.reload()
- } else {
- this.packages.each(function(pack) {
- pack.close();
- });
- indicateSuccess();
- }
- }.bind(this),
- onFailure: indicateFail
- }).send();
- },
-
- restartFailed: function() {
- indicateLoad();
- new Request.JSON({
- method: 'get',
- url: '/api/restartFailed',
- onSuccess: function(data) {
- this.packages.each(function(pack) {
- pack.close();
- });
- indicateSuccess();
- }.bind(this),
- onFailure: indicateFail
- }).send();
- },
-
- startSort: function(ele, copy) {
- },
-
- saveSort: function(ele, copy) {
- var order = [];
- this.sorts.serialize(function(li, pos) {
- if (li == ele && ele.retrieve("order") != pos) {
- order.push(ele.retrieve("pid") + "|" + pos)
- }
- li.store("order", pos)
- });
- if (order.length > 0) {
- indicateLoad();
- new Request.JSON({
- method: 'get',
- url: '/json/package_order/' + order[0],
- onSuccess: indicateFinish,
- onFailure: indicateFail
- }).send();
- }
- }
-
-});
-
-var Package = new Class({
- initialize: function(ui, id, ele, data) {
- this.ui = ui;
- this.id = id;
- this.linksLoaded = false;
-
- if (!ele) {
- this.createElement(data);
- } else {
- this.ele = ele;
- this.order = ele.getElements("div.order")[0].get("html");
- this.ele.store("order", this.order);
- this.ele.store("pid", this.id);
- this.parseElement();
- }
-
- var pname = this.ele.getElements(".packagename")[0];
- this.buttons = new Fx.Tween(this.ele.getElements(".buttons")[0], {link: "cancel"});
- this.buttons.set("opacity", 0);
-
- pname.addEvent("mouseenter", function(e) {
- this.buttons.start("opacity", 1)
- }.bind(this));
-
- pname.addEvent("mouseleave", function(e) {
- this.buttons.start("opacity", 0)
- }.bind(this));
-
-
- },
-
- createElement: function() {
- alert("create")
- },
-
- parseElement: function() {
- var imgs = this.ele.getElements('img');
-
- this.name = this.ele.getElements('.name')[0];
- this.folder = this.ele.getElements('.folder')[0];
- this.password = this.ele.getElements('.password')[0];
-
- imgs[1].addEvent('click', this.deletePackage.bind(this));
- imgs[2].addEvent('click', this.restartPackage.bind(this));
- imgs[3].addEvent('click', this.editPackage.bind(this));
- imgs[4].addEvent('click', this.movePackage.bind(this));
-
- this.ele.getElement('.packagename').addEvent('click', this.toggle.bind(this));
-
- },
-
- loadLinks: function() {
- indicateLoad();
- new Request.JSON({
- method: 'get',
- url: '/json/package/' + this.id,
- onSuccess: this.createLinks.bind(this),
- onFailure: indicateFail
- }).send();
- },
-
- createLinks: function(data) {
- var ul = $("sort_children_{id}".substitute({"id": this.id}));
- ul.set("html", "");
- data.links.each(function(link) {
- link.id = link.fid;
- var li = new Element("li", {
- "style": {
- "margin-left": 0
- }
- });
-
- var html = "<span style='cursor: move' class='child_status sorthandle'><img src='/media/default/img/{icon}' style='width: 12px; height:12px;'/></span>\n".substitute({"icon": link.icon});
- html += "<span style='font-size: 15px'>{name}</span><br /><div class='child_secrow'>".substitute({"name": link.name});
- html += "<span class='child_status'>{statusmsg}</span>{error}&nbsp;".substitute({"statusmsg": link.statusmsg, "error":link.error});
- html += "<span class='child_status'>{format_size}</span>".substitute({"format_size": link.format_size});
- html += "<span class='child_status'>{plugin}</span>&nbsp;&nbsp;".substitute({"plugin": link.plugin});
- html += "<img title='{{_("Delete Link")}}' style='cursor: pointer;' width='10px' height='10px' src='/media/default/img/delete.png' />&nbsp;&nbsp;";
- html += "<img title='{{_("Restart Link")}}' style='cursor: pointer;margin-left: -4px' width='10px' height='10px' src='/media/default/img/arrow_refresh.png' /></div>";
-
- var div = new Element("div", {
- "id": "file_" + link.id,
- "class": "child",
- "html": html
- });
-
- li.store("order", link.order);
- li.store("lid", link.id);
-
- li.adopt(div);
- ul.adopt(li);
- });
- this.sorts = new Sortables(ul, {
- constrain: false,
- clone: true,
- revert: true,
- opacity: 0.4,
- handle: ".sorthandle",
- onComplete: this.saveSort.bind(this)
- });
- this.registerLinkEvents();
- this.linksLoaded = true;
- indicateFinish();
- this.toggle();
- },
-
- registerLinkEvents: function() {
- this.ele.getElements('.child').each(function(child) {
- var lid = child.get('id').match(/[0-9]+/);
- var imgs = child.getElements('.child_secrow img');
- imgs[0].addEvent('click', function(e) {
- new Request({
- method: 'get',
- url: '/api/deleteFiles/[' + this + "]",
- onSuccess: function() {
- $('file_' + this).nix()
- }.bind(this),
- onFailure: indicateFail
- }).send();
- }.bind(lid));
-
- imgs[1].addEvent('click', function(e) {
- new Request({
- method: 'get',
- url: '/api/restartFile/' + this,
- onSuccess: function() {
- var ele = $('file_' + this);
- var imgs = ele.getElements("img");
- imgs[0].set("src", "/media/default/img/status_queue.png");
- var spans = ele.getElements(".child_status");
- spans[1].set("html", "queued");
- indicateSuccess();
- }.bind(this),
- onFailure: indicateFail
- }).send();
- }.bind(lid));
- });
- },
-
- toggle: function() {
- var child = this.ele.getElement('.children');
- if (child.getStyle('display') == "block") {
- child.dissolve();
- } else {
- if (!this.linksLoaded) {
- this.loadLinks();
- } else {
- child.reveal();
- }
- }
- },
-
-
- deletePackage: function(event) {
- indicateLoad();
- new Request({
- method: 'get',
- url: '/api/deletePackages/[' + this.id + "]",
- onSuccess: function() {
- this.ele.nix();
- indicateFinish();
- }.bind(this),
- onFailure: indicateFail
- }).send();
- //hide_pack();
- event.stop();
- },
-
- restartPackage: function(event) {
- indicateLoad();
- new Request({
- method: 'get',
- url: '/api/restartPackage/' + this.id,
- onSuccess: function() {
- this.close();
- indicateSuccess();
- }.bind(this),
- onFailure: indicateFail
- }).send();
- event.stop();
- },
-
- close: function() {
- var child = this.ele.getElement('.children');
- if (child.getStyle('display') == "block") {
- child.dissolve();
- }
- var ul = $("sort_children_{id}".substitute({"id": this.id}));
- ul.erase("html");
- this.linksLoaded = false;
- },
-
- movePackage: function(event) {
- indicateLoad();
- new Request({
- method: 'get',
- url: '/json/move_package/' + ((this.ui.type + 1) % 2) + "/" + this.id,
- onSuccess: function() {
- this.ele.nix();
- indicateFinish();
- }.bind(this),
- onFailure: indicateFail
- }).send();
- event.stop();
- },
-
- editPackage: function(event) {
- $("pack_form").removeEvents("submit");
- $("pack_form").addEvent("submit", this.savePackage.bind(this));
-
- $("pack_id").set("value", this.id);
- $("pack_name").set("value", this.name.get("text"));
- $("pack_folder").set("value", this.folder.get("text"));
- $("pack_pws").set("value", this.password.get("text"));
-
- root.packageBox.open();
- event.stop();
- },
-
- savePackage: function(event) {
- $("pack_form").send();
- this.name.set("text", $("pack_name").get("value"));
- this.folder.set("text", $("pack_folder").get("value"));
- this.password.set("text", $("pack_pws").get("value"));
- root.packageBox.close();
- event.stop();
- },
-
- saveSort: function(ele, copy) {
- var order = [];
- this.sorts.serialize(function(li, pos) {
- if (li == ele && ele.retrieve("order") != pos) {
- order.push(ele.retrieve("lid") + "|" + pos)
- }
- li.store("order", pos)
- });
- if (order.length > 0) {
- indicateLoad();
- new Request.JSON({
- method: 'get',
- url: '/json/link_order/' + order[0],
- onSuccess: indicateFinish,
- onFailure: indicateFail
- }).send();
- }
- }
-
-});
-
diff --git a/module/web/media/js/purr_static.js b/module/web/media/js/purr_static.js
deleted file mode 100644
index 7e0aee949..000000000
--- a/module/web/media/js/purr_static.js
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
----
-script: purr.js
-
-description: Class to create growl-style popup notifications.
-
-license: MIT-style
-
-authors: [atom smith]
-
-requires:
-- core/1.3: [Core, Browser, Array, Function, Number, String, Hash, Event, Class.Extras, Element.Event, Element.Style, Element.Dimensions, Fx.CSS, FX.Tween, Fx.Morph]
-
-provides: [Purr, Element.alert]
-...
-*/
-
-
-var Purr = new Class({
-
- 'options': {
- 'mode': 'top',
- 'position': 'left',
- 'elementAlertClass': 'purr-element-alert',
- 'elements': {
- 'wrapper': 'div',
- 'alert': 'div',
- 'buttonWrapper': 'div',
- 'button': 'button'
- },
- 'elementOptions': {
- 'wrapper': {
- 'styles': {
- 'position': 'fixed',
- 'z-index': '9999'
- },
- 'class': 'purr-wrapper'
- },
- 'alert': {
- 'class': 'purr-alert',
- 'styles': {
- 'opacity': '.85'
- }
- },
- 'buttonWrapper': {
- 'class': 'purr-button-wrapper'
- },
- 'button': {
- 'class': 'purr-button'
- }
- },
- 'alert': {
- 'buttons': [],
- 'clickDismiss': true,
- 'hoverWait': true,
- 'hideAfter': 5000,
- 'fx': {
- 'duration': 500
- },
- 'highlightRepeat': false,
- 'highlight': { // false to disable highlighting
- 'start': '#FF0',
- 'end': false
- }
- }
- },
-
- 'Implements': [Options, Events, Chain],
-
- 'initialize': function(options){
- this.setOptions(options);
- this.createWrapper();
- return this;
- },
-
- 'bindAlert': function(){
- return this.alert.bind(this);
- },
-
- 'createWrapper': function(){
- this.wrapper = new Element(this.options.elements.wrapper, this.options.elementOptions.wrapper);
- if(this.options.mode == 'top')
- {
- this.wrapper.setStyle('top', 0);
- }
- else
- {
- this.wrapper.setStyle('bottom', 0);
- }
- document.id(document.body).grab(this.wrapper);
- this.positionWrapper(this.options.position);
- },
-
- 'positionWrapper': function(position){
- if(typeOf(position) == 'object')
- {
-
- var wrapperCoords = this.getWrapperCoords();
-
- this.wrapper.setStyles({
- 'bottom': '',
- 'left': position.x,
- 'top': position.y - wrapperCoords.height,
- 'position': 'absolute'
- });
- }
- else if(position == 'left')
- {
- this.wrapper.setStyle('left', 0);
- }
- else if(position == 'right')
- {
- this.wrapper.setStyle('right', 0);
- }
- else
- {
- this.wrapper.setStyle('left', (window.innerWidth / 2) - (this.getWrapperCoords().width / 2));
- }
- return this;
- },
-
- 'getWrapperCoords': function(){
- this.wrapper.setStyle('visibility', 'hidden');
- var measurer = this.alert('need something in here to measure');
- var coords = this.wrapper.getCoordinates();
- measurer.destroy();
- this.wrapper.setStyle('visibility','');
- return coords;
- },
-
- 'alert': function(msg, options){
-
- options = Object.merge({}, this.options.alert, options || {});
-
- var alert = new Element(this.options.elements.alert, this.options.elementOptions.alert);
-
- if(typeOf(msg) == 'string')
- {
- alert.set('html', msg);
- }
- else if(typeOf(msg) == 'element')
- {
- alert.grab(msg);
- }
- else if(typeOf(msg) == 'array')
- {
- var alerts = [];
- msg.each(function(m){
- alerts.push(this.alert(m, options));
- }, this);
- return alerts;
- }
-
- alert.store('options', options);
-
- if(options.buttons.length > 0)
- {
- options.clickDismiss = false;
- options.hideAfter = false;
- options.hoverWait = false;
- var buttonWrapper = new Element(this.options.elements.buttonWrapper, this.options.elementOptions.buttonWrapper);
- alert.grab(buttonWrapper);
- options.buttons.each(function(button){
- if(button.text !== undefined)
- {
- var callbackButton = new Element(this.options.elements.button, this.options.elementOptions.button);
- callbackButton.set('html', button.text);
- if(button.callback !== undefined)
- {
- callbackButton.addEvent('click', button.callback.pass(alert));
- }
- if(button.dismiss !== undefined && button.dismiss)
- {
- callbackButton.addEvent('click', this.dismiss.pass(alert, this));
- }
- buttonWrapper.grab(callbackButton);
- }
- }, this);
- }
- if(options.className !== undefined)
- {
- alert.addClass(options.className);
- }
-
- this.wrapper.grab(alert, (this.options.mode == 'top') ? 'bottom' : 'top');
-
- var fx = Object.merge(this.options.alert.fx, options.fx);
- var alertFx = new Fx.Morph(alert, fx);
- alert.store('fx', alertFx);
- this.fadeIn(alert);
-
- if(options.highlight)
- {
- alertFx.addEvent('complete', function(){
- alert.highlight(options.highlight.start, options.highlight.end);
- if(options.highlightRepeat)
- {
- alert.highlight.periodical(options.highlightRepeat, alert, [options.highlight.start, options.highlight.end]);
- }
- });
- }
- if(options.hideAfter)
- {
- this.dismiss(alert);
- }
-
- if(options.clickDismiss)
- {
- alert.addEvent('click', function(){
- this.holdUp = false;
- this.dismiss(alert, true);
- }.bind(this));
- }
-
- if(options.hoverWait)
- {
- alert.addEvents({
- 'mouseenter': function(){
- this.holdUp = true;
- }.bind(this),
- 'mouseleave': function(){
- this.holdUp = false;
- }.bind(this)
- });
- }
-
- return alert;
- },
-
- 'fadeIn': function(alert){
- var alertFx = alert.retrieve('fx');
- alertFx.set({
- 'opacity': 0
- });
- alertFx.start({
- 'opacity': [this.options.elementOptions.alert.styles.opacity, '.9'].pick()
- });
- },
-
- 'dismiss': function(alert, now){
- now = now || false;
- var options = alert.retrieve('options');
- if(now)
- {
- this.fadeOut(alert);
- }
- else
- {
- this.fadeOut.delay(options.hideAfter, this, alert);
- }
- },
-
- 'fadeOut': function(alert){
- if(this.holdUp)
- {
- this.dismiss.delay(100, this, [alert, true]);
- return null;
- }
- var alertFx = alert.retrieve('fx');
- if(!alertFx)
- {
- return null;
- }
- var to = {
- 'opacity': 0
- };
- if(this.options.mode == 'top')
- {
- to['margin-top'] = '-'+alert.offsetHeight+'px';
- }
- else
- {
- to['margin-bottom'] = '-'+alert.offsetHeight+'px';
- }
- alertFx.start(to);
- alertFx.addEvent('complete', function(){
- alert.destroy();
- });
- }
-});
-
-Element.implement({
-
- 'alert': function(msg, options){
- var alert = this.retrieve('alert');
- if(!alert)
- {
- options = options || {
- 'mode':'top'
- };
- alert = new Purr(options);
- this.store('alert', alert);
- }
-
- var coords = this.getCoordinates();
-
- alert.alert(msg, options);
-
- alert.wrapper.setStyles({
- 'bottom': '',
- 'left': (coords.left - (alert.wrapper.getWidth() / 2)) + (this.getWidth() / 2),
- 'top': coords.top - (alert.wrapper.getHeight()),
- 'position': 'absolute'
- });
-
- }
-
-}); \ No newline at end of file
diff --git a/module/web/media/js/settings.coffee b/module/web/media/js/settings.coffee
deleted file mode 100644
index 9205233e3..000000000
--- a/module/web/media/js/settings.coffee
+++ /dev/null
@@ -1,107 +0,0 @@
-root = this
-
-window.addEvent 'domready', ->
- root.accountDialog = new MooDialog {destroyOnHide: false}
- root.accountDialog.setContent $ 'account_box'
-
- new TinyTab $$('#toptabs li a'), $$('#tabs-body > span')
-
- $$('ul.nav').each (nav) ->
- new MooDropMenu nav, {
- onOpen: (el) -> el.fade 'in'
- onClose: (el) -> el.fade 'out'
- onInitialize: (el) -> el.fade('hide').set 'tween', {duration:500}
- }
-
- new SettingsUI()
-
-
-class SettingsUI
- constructor: ->
- @menu = $$ "#general-menu li"
- @menu.append $$ "#plugin-menu li"
-
- @name = $ "tabsback"
- @general = $ "general_form_content"
- @plugin = $ "plugin_form_content"
-
- el.addEvent 'click', @menuClick.bind(this) for el in @menu
-
- $("general|submit").addEvent "click", @configSubmit.bind(this)
- $("plugin|submit").addEvent "click", @configSubmit.bind(this)
-
- $("account_add").addEvent "click", (e) ->
- root.accountDialog.open()
- e.stop()
-
- $("account_reset").addEvent "click", (e) ->
- root.accountDialog.close()
-
- $("account_add_button").addEvent "click", @addAccount.bind(this)
- $("account_submit").addEvent "click", @submitAccounts.bind(this)
-
-
- menuClick: (e) ->
- [category, section] = e.target.get("id").split("|")
- name = e.target.get "text"
-
-
- target = if category is "general" then @general else @plugin
- target.dissolve()
-
- new Request({
- "method" : "get"
- "url" : "/json/load_config/#{category}/#{section}"
- "onSuccess": (data) =>
- target.set "html", data
- target.reveal()
- this.name.set "text", name
- }).send()
-
-
- configSubmit: (e) ->
- category = e.target.get("id").split("|")[0];
- form = $("#{category}_form");
-
- form.set "send", {
- "method": "post"
- "url": "/json/save_config/#{category}"
- "onSuccess" : ->
- root.notify.alert '{{ _("Settings saved.")}}', {
- 'className': 'success'
- }
- "onFailure": ->
- root.notify.alert '{{ _("Error occured.")}}', {
- 'className': 'error'
- }
- }
- form.send()
- e.stop()
-
- addAccount: (e) ->
- form = $ "add_account_form"
- form.set "send", {
- "method": "post"
- "onSuccess" : -> window.location.reload()
- "onFailure": ->
- root.notify.alert '{{_("Error occured.")}}', {
- 'className': 'error'
- }
- }
-
- form.send()
- e.stop()
-
- submitAccounts: (e) ->
- form = $ "account_form"
- form.set "send", {
- "method": "post",
- "onSuccess" : -> window.location.reload()
- "onFailure": ->
- root.notify.alert('{{ _("Error occured.") }}', {
- 'className': 'error'
- });
- }
-
- form.send()
- e.stop() \ No newline at end of file
diff --git a/module/web/media/js/settings.js b/module/web/media/js/settings.js
deleted file mode 100644
index 9191fac72..000000000
--- a/module/web/media/js/settings.js
+++ /dev/null
@@ -1,3 +0,0 @@
-{% autoescape true %}
-var SettingsUI,root;var __bind=function(a,b){return function(){return a.apply(b,arguments)}};root=this;window.addEvent("domready",function(){root.accountDialog=new MooDialog({destroyOnHide:false});root.accountDialog.setContent($("account_box"));new TinyTab($$("#toptabs li a"),$$("#tabs-body > span"));$$("ul.nav").each(function(a){return new MooDropMenu(a,{onOpen:function(b){return b.fade("in")},onClose:function(b){return b.fade("out")},onInitialize:function(b){return b.fade("hide").set("tween",{duration:500})}})});return new SettingsUI()});SettingsUI=(function(){function a(){var c,e,b,d;this.menu=$$("#general-menu li");this.menu.append($$("#plugin-menu li"));this.name=$("tabsback");this.general=$("general_form_content");this.plugin=$("plugin_form_content");d=this.menu;for(e=0,b=d.length;e<b;e++){c=d[e];c.addEvent("click",this.menuClick.bind(this))}$("general|submit").addEvent("click",this.configSubmit.bind(this));$("plugin|submit").addEvent("click",this.configSubmit.bind(this));$("account_add").addEvent("click",function(f){root.accountDialog.open();return f.stop()});$("account_reset").addEvent("click",function(f){return root.accountDialog.close()});$("account_add_button").addEvent("click",this.addAccount.bind(this));$("account_submit").addEvent("click",this.submitAccounts.bind(this))}a.prototype.menuClick=function(h){var c,b,g,f,d;d=h.target.get("id").split("|"),c=d[0],g=d[1];b=h.target.get("text");f=c==="general"?this.general:this.plugin;f.dissolve();return new Request({method:"get",url:"/json/load_config/"+c+"/"+g,onSuccess:__bind(function(e){f.set("html",e);f.reveal();return this.name.set("text",b)},this)}).send()};a.prototype.configSubmit=function(d){var c,b;c=d.target.get("id").split("|")[0];b=$(""+c+"_form");b.set("send",{method:"post",url:"/json/save_config/"+c,onSuccess:function(){return root.notify.alert('{{ _("Settings saved.")}}',{className:"success"})},onFailure:function(){return root.notify.alert('{{ _("Error occured.")}}',{className:"error"})}});b.send();return d.stop()};a.prototype.addAccount=function(c){var b;b=$("add_account_form");b.set("send",{method:"post",onSuccess:function(){return window.location.reload()},onFailure:function(){return root.notify.alert('{{_("Error occured.")}}',{className:"error"})}});b.send();return c.stop()};a.prototype.submitAccounts=function(c){var b;b=$("account_form");b.set("send",{method:"post",onSuccess:function(){return window.location.reload()},onFailure:function(){return root.notify.alert('{{ _("Error occured.") }}',{className:"error"})}});b.send();return c.stop()};return a})();
-{% endautoescape %} \ No newline at end of file
diff --git a/module/web/media/js/tinytab_static.js b/module/web/media/js/tinytab_static.js
deleted file mode 100644
index 6c38292f5..000000000
--- a/module/web/media/js/tinytab_static.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
----
-description: TinyTab - Tiny and simple tab handler for Mootools.
-
-license: MIT-style
-
-authors:
-- Danillo César de O. Melo
-
-requires:
-- core/1.2.4: '*'
-
-provides: TinyTab
-
-...
-*/
-(function($) {
- this.TinyTab = new Class({
- Implements: Events,
- initialize: function(tabs, contents, opt) {
- this.tabs = tabs;
- this.contents = contents;
- this.header = $("tabsback");
- this.headers = [];
- for(var i =0; i < this.tabs.length; i++){
- this.headers.push("");
- }
- if(!opt) opt = {};
- this.css = opt.selectedClass || 'selected';
- this.select(this.tabs[0]);
- tabs.each(function(el){
- el.addEvent('click',function(e){
- this.select(el);
- e.stop();
- }.bind(this));
- }.bind(this));
- },
-
- select: function(el) {
- this.tabs.removeClass(this.css);
- el.addClass(this.css);
- this.contents.setStyle('display','none');
- var index = this.tabs.indexOf(el);
- this.header.set("text", this.headers[index]);
- var content = this.contents[index];
- content.setStyle('display','block');
- this.fireEvent('change',[content,el]);
- }
- });
-})(document.id); \ No newline at end of file
diff --git a/module/web/middlewares.py b/module/web/middlewares.py
deleted file mode 100644
index e0e6c3102..000000000
--- a/module/web/middlewares.py
+++ /dev/null
@@ -1,133 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import gzip
-
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-
-class StripPathMiddleware(object):
- def __init__(self, app):
- self.app = app
-
- def __call__(self, e, h):
- e['PATH_INFO'] = e['PATH_INFO'].rstrip('/')
- return self.app(e, h)
-
-
-class PrefixMiddleware(object):
- def __init__(self, app, prefix="/pyload"):
- self.app = app
- self.prefix = prefix
-
- def __call__(self, e, h):
- path = e["PATH_INFO"]
- if path.startswith(self.prefix):
- e['PATH_INFO'] = path.replace(self.prefix, "", 1)
- return self.app(e, h)
-
-# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
-# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
-
-# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
-# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
-
-# WSGI middleware
-# Gzip-encodes the response.
-
-class GZipMiddleWare(object):
-
- def __init__(self, application, compress_level=6):
- self.application = application
- self.compress_level = int(compress_level)
-
- def __call__(self, environ, start_response):
- if 'gzip' not in environ.get('HTTP_ACCEPT_ENCODING', ''):
- # nothing for us to do, so this middleware will
- # be a no-op:
- return self.application(environ, start_response)
- response = GzipResponse(start_response, self.compress_level)
- app_iter = self.application(environ,
- response.gzip_start_response)
- if app_iter is not None:
- response.finish_response(app_iter)
-
- return response.write()
-
-def header_value(headers, key):
- for header, value in headers:
- if key.lower() == header.lower():
- return value
-
-def update_header(headers, key, value):
- remove_header(headers, key)
- headers.append((key, value))
-
-def remove_header(headers, key):
- for header, value in headers:
- if key.lower() == header.lower():
- headers.remove((header, value))
- break
-
-class GzipResponse(object):
-
- def __init__(self, start_response, compress_level):
- self.start_response = start_response
- self.compress_level = compress_level
- self.buffer = StringIO()
- self.compressible = False
- self.content_length = None
- self.headers = ()
-
- def gzip_start_response(self, status, headers, exc_info=None):
- self.headers = headers
- ct = header_value(headers,'content-type')
- ce = header_value(headers,'content-encoding')
- cl = header_value(headers, 'content-length')
- if cl:
- cl = int(cl)
- else:
- cl = 201
- self.compressible = False
- if ct and (ct.startswith('text/') or ct.startswith('application/')) \
- and 'zip' not in ct and cl > 200:
- self.compressible = True
- if ce:
- self.compressible = False
- if self.compressible:
- headers.append(('content-encoding', 'gzip'))
- remove_header(headers, 'content-length')
- self.headers = headers
- self.status = status
- return self.buffer.write
-
- def write(self):
- out = self.buffer
- out.seek(0)
- s = out.getvalue()
- out.close()
- return [s]
-
- def finish_response(self, app_iter):
- if self.compressible:
- output = gzip.GzipFile(mode='wb', compresslevel=self.compress_level,
- fileobj=self.buffer)
- else:
- output = self.buffer
- try:
- for s in app_iter:
- output.write(s)
- if self.compressible:
- output.close()
- finally:
- if hasattr(app_iter, 'close'):
- try:
- app_iter.close()
- except :
- pass
-
- content_length = self.buffer.tell()
- update_header(self.headers, "Content-Length" , str(content_length))
- self.start_response(self.status, self.headers) \ No newline at end of file
diff --git a/module/web/pyload_app.py b/module/web/pyload_app.py
deleted file mode 100644
index df4a4b3d4..000000000
--- a/module/web/pyload_app.py
+++ /dev/null
@@ -1,533 +0,0 @@
-#!/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
-"""
-from datetime import datetime
-from operator import itemgetter, attrgetter
-
-import time
-import os
-import sys
-from os import listdir
-from os.path import isdir, isfile, join, abspath
-from sys import getfilesystemencoding
-from urllib import unquote
-
-from bottle import route, static_file, request, response, redirect, HTTPError, error
-
-from webinterface import PYLOAD, PYLOAD_DIR, PROJECT_DIR, SETUP, env
-
-from utils import render_to_response, parse_permissions, parse_userdata, \
- login_required, get_permission, set_permission, permlist, toDict, set_session
-
-from filters import relpath, unquotepath
-
-from module.utils import formatSize, save_join, fs_encode, fs_decode
-
-# Helper
-
-def pre_processor():
- s = request.environ.get('beaker.session')
- user = parse_userdata(s)
- perms = parse_permissions(s)
- status = {}
- captcha = False
- update = False
- plugins = False
- if user["is_authenticated"]:
- status = PYLOAD.statusServer()
- info = PYLOAD.getInfoByPlugin("UpdateManager")
- captcha = PYLOAD.isCaptchaWaiting()
-
- # check if update check is available
- if info:
- if info["pyload"] == "True": update = True
- if info["plugins"] == "True": plugins = True
-
-
- return {"user": user,
- 'status': status,
- 'captcha': captcha,
- 'perms': perms,
- 'url': request.url,
- 'update': update,
- 'plugins': plugins}
-
-
-def base(messages):
- return render_to_response('base.html', {'messages': messages}, [pre_processor])
-
-
-## Views
-@error(500)
-def error500(error):
- print "An error occured while processing the request."
- if error.traceback:
- print error.traceback
-
- return base(["An Error occured, please enable debug mode to get more details.", error,
- error.traceback.replace("\n", "<br>") if error.traceback else "No Traceback"])
-
-# render js
-@route("/media/js/<path:re:.+\.js>")
-def js_dynamic(path):
- response.headers['Expires'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
- time.gmtime(time.time() + 60 * 60 * 24 * 2))
- response.headers['Cache-control'] = "public"
- response.headers['Content-Type'] = "text/javascript; charset=UTF-8"
-
- try:
- # static files are not rendered
- if "static" not in path and "mootools" not in path:
- t = env.get_template("js/%s" % path)
- return t.render()
- else:
- return static_file(path, root=join(PROJECT_DIR, "media", "js"))
- except:
- return HTTPError(404, "Not Found")
-
-@route('/media/<path:path>')
-def server_static(path):
- response.headers['Expires'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT",
- time.gmtime(time.time() + 60 * 60 * 24 * 7))
- response.headers['Cache-control'] = "public"
- return static_file(path, root=join(PROJECT_DIR, "media"))
-
-@route('/favicon.ico')
-def favicon():
- return static_file("favicon.ico", root=join(PROJECT_DIR, "media", "img"))
-
-
-@route('/login', method="GET")
-def login():
- if not PYLOAD and SETUP:
- redirect("/setup")
- else:
- return render_to_response("login.html", proc=[pre_processor])
-
-
-@route('/nopermission')
-def nopermission():
- return base([_("You dont have permission to access this page.")])
-
-
-@route("/login", method="POST")
-def login_post():
- user = request.forms.get("username")
- password = request.forms.get("password")
-
- info = PYLOAD.checkAuth(user, password)
-
- if not info:
- return render_to_response("login.html", {"errors": True}, [pre_processor])
-
- set_session(request, info)
- return redirect("/")
-
-
-@route("/logout")
-def logout():
- s = request.environ.get('beaker.session')
- s.delete()
- return render_to_response("logout.html", proc=[pre_processor])
-
-
-@route("/")
-@route("/home")
-@login_required("LIST")
-def home():
- try:
- res = [toDict(x) for x in PYLOAD.statusDownloads()]
- except:
- s = request.environ.get('beaker.session')
- s.delete()
- return redirect("/login")
-
- for link in res:
- if link["status"] == 12:
- link["information"] = "%s kB @ %s kB/s" % (link["size"] - link["bleft"], link["speed"])
-
- return render_to_response("home.html", {"res": res}, [pre_processor])
-
-
-@route("/queue")
-@login_required("LIST")
-def queue():
- queue = PYLOAD.getQueue()
-
- queue.sort(key=attrgetter("order"))
-
- return render_to_response('queue.html', {'content': queue, 'target': 1}, [pre_processor])
-
-
-@route("/collector")
-@login_required('LIST')
-def collector():
- queue = PYLOAD.getCollector()
-
- queue.sort(key=attrgetter("order"))
-
- return render_to_response('queue.html', {'content': queue, 'target': 0}, [pre_processor])
-
-
-@route("/downloads")
-@login_required('DOWNLOAD')
-def downloads():
- root = PYLOAD.getConfigValue("general", "download_folder")
-
- if not isdir(root):
- return base([_('Download directory not found.')])
- data = {
- 'folder': [],
- 'files': []
- }
-
- items = listdir(fs_encode(root))
-
- for item in sorted([fs_decode(x) for x in items]):
- if isdir(save_join(root, item)):
- folder = {
- 'name': item,
- 'path': item,
- 'files': []
- }
- files = listdir(save_join(root, item))
- for file in sorted([fs_decode(x) for x in files]):
- try:
- if isfile(save_join(root, item, file)):
- folder['files'].append(file)
- except:
- pass
-
- data['folder'].append(folder)
- elif isfile(join(root, item)):
- data['files'].append(item)
-
- return render_to_response('downloads.html', {'files': data}, [pre_processor])
-
-
-@route("/downloads/get/<path:re:.+>")
-@login_required("DOWNLOAD")
-def get_download(path):
- path = unquote(path).decode("utf8")
- #@TODO some files can not be downloaded
-
- root = PYLOAD.getConfigValue("general", "download_folder")
-
- path = path.replace("..", "")
- try:
- return static_file(fs_encode(path), fs_encode(root))
-
- except Exception, e:
- print e
- return HTTPError(404, "File not Found.")
-
-
-
-@route("/settings")
-@login_required('SETTINGS')
-def config():
- conf = PYLOAD.getConfig()
- plugin = PYLOAD.getPluginConfig()
-
- conf_menu = []
- plugin_menu = []
-
- for entry in sorted(conf.keys()):
- conf_menu.append((entry, conf[entry].description))
-
- for entry in sorted(plugin.keys()):
- plugin_menu.append((entry, plugin[entry].description))
-
- accs = PYLOAD.getAccounts(False)
-
- for data in accs:
- if data.trafficleft == -1:
- data.trafficleft = _("unlimited")
- elif not data.trafficleft:
- data.trafficleft = _("not available")
- else:
- data.trafficleft = formatSize(data.trafficleft * 1024)
-
- if data.validuntil == -1:
- data.validuntil = _("unlimited")
- elif not data.validuntil :
- data.validuntil = _("not available")
- else:
- t = time.localtime(data.validuntil)
- data.validuntil = time.strftime("%d.%m.%Y", t)
-
- if "time" in data.options:
- try:
- data.options["time"] = data.options["time"][0]
- except:
- data.options["time"] = "0:00-0:00"
-
- if "limitDL" in data.options:
- data.options["limitdl"] = data.options["limitDL"][0]
- else:
- data.options["limitdl"] = "0"
-
- return render_to_response('settings.html',
- {'conf': {'plugin': plugin_menu, 'general': conf_menu, 'accs': accs}, 'types': PYLOAD.getAccountTypes()},
- [pre_processor])
-
-
-@route("/filechooser")
-@route("/pathchooser")
-@route("/filechooser/:file#.+#")
-@route("/pathchooser/:path#.+#")
-@login_required('STATUS')
-def path(file="", path=""):
- if file:
- type = "file"
- else:
- type = "folder"
-
- path = os.path.normpath(unquotepath(path))
-
- if os.path.isfile(path):
- oldfile = path
- path = os.path.dirname(path)
- else:
- oldfile = ''
-
- abs = False
-
- if os.path.isdir(path):
- if os.path.isabs(path):
- cwd = os.path.abspath(path)
- abs = True
- else:
- cwd = relpath(path)
- else:
- cwd = os.getcwd()
-
- try:
- cwd = cwd.encode("utf8")
- except:
- pass
-
- cwd = os.path.normpath(os.path.abspath(cwd))
- parentdir = os.path.dirname(cwd)
- if not abs:
- if os.path.abspath(cwd) == "/":
- cwd = relpath(cwd)
- else:
- cwd = relpath(cwd) + os.path.sep
- parentdir = relpath(parentdir) + os.path.sep
-
- if os.path.abspath(cwd) == "/":
- parentdir = ""
-
- try:
- folders = os.listdir(cwd)
- except:
- folders = []
-
- files = []
-
- for f in folders:
- try:
- f = f.decode(getfilesystemencoding())
- data = {'name': f, 'fullpath': join(cwd, f)}
- data['sort'] = data['fullpath'].lower()
- data['modified'] = datetime.fromtimestamp(int(os.path.getmtime(join(cwd, f))))
- data['ext'] = os.path.splitext(f)[1]
- except:
- continue
-
- if os.path.isdir(join(cwd, f)):
- data['type'] = 'dir'
- else:
- data['type'] = 'file'
-
- if os.path.isfile(join(cwd, f)):
- data['size'] = os.path.getsize(join(cwd, f))
-
- power = 0
- while (data['size'] / 1024) > 0.3:
- power += 1
- data['size'] /= 1024.
- units = ('', 'K', 'M', 'G', 'T')
- data['unit'] = units[power] + 'Byte'
- else:
- data['size'] = ''
-
- files.append(data)
-
- files = sorted(files, key=itemgetter('type', 'sort'))
-
- return render_to_response('pathchooser.html',
- {'cwd': cwd, 'files': files, 'parentdir': parentdir, 'type': type, 'oldfile': oldfile,
- 'absolute': abs}, [])
-
-
-@route("/logs")
-@route("/logs", method="POST")
-@route("/logs/:item")
-@route("/logs/:item", method="POST")
-@login_required('LOGS')
-def logs(item=-1):
- s = request.environ.get('beaker.session')
-
- perpage = s.get('perpage', 34)
- reversed = s.get('reversed', False)
-
- warning = ""
- conf = PYLOAD.getConfigValue("log","file_log")
- if not conf:
- warning = "Warning: File log is disabled, see settings page."
-
- perpage_p = ((20, 20), (34, 34), (40, 40), (100, 100), (0, 'all'))
- fro = None
-
- if request.environ.get('REQUEST_METHOD', "GET") == "POST":
- try:
- fro = datetime.strptime(request.forms['from'], '%d.%m.%Y %H:%M:%S')
- except:
- pass
- try:
- perpage = int(request.forms['perpage'])
- s['perpage'] = perpage
-
- reversed = bool(request.forms.get('reversed', False))
- s['reversed'] = reversed
- except:
- pass
-
- s.save()
-
- try:
- item = int(item)
- except:
- pass
-
- log = PYLOAD.getLog()
- if not perpage:
- item = 0
-
- if item < 1 or type(item) is not int:
- item = 1 if len(log) - perpage + 1 < 1 else len(log) - perpage + 1
-
- if type(fro) is datetime: # we will search for datetime
- item = -1
-
- data = []
- counter = 0
- perpagecheck = 0
- for l in log:
- counter += 1
-
- if counter >= item:
- try:
- date, time, level, message = l.decode("utf8", "ignore").split(" ", 3)
- dtime = datetime.strptime(date + ' ' + time, '%d.%m.%Y %H:%M:%S')
- except:
- dtime = None
- date = '?'
- time = ' '
- level = '?'
- message = l
- if item == -1 and dtime is not None and fro <= dtime:
- item = counter #found our datetime
- if item >= 0:
- data.append({'line': counter, 'date': date + " " + time, 'level': level, 'message': message})
- perpagecheck += 1
- if fro is None and dtime is not None: #if fro not set set it to first showed line
- fro = dtime
- if perpagecheck >= perpage > 0:
- break
-
- if fro is None: #still not set, empty log?
- fro = datetime.now()
- if reversed:
- data.reverse()
- return render_to_response('logs.html', {'warning': warning, 'log': data, 'from': fro.strftime('%d.%m.%Y %H:%M:%S'),
- 'reversed': reversed, 'perpage': perpage, 'perpage_p': sorted(perpage_p),
- 'iprev': 1 if item - perpage < 1 else item - perpage,
- 'inext': (item + perpage) if item + perpage < len(log) else item},
- [pre_processor])
-
-
-@route("/admin")
-@route("/admin", method="POST")
-@login_required("ADMIN")
-def admin():
- # convert to dict
- user = dict([(name, toDict(y)) for name, y in PYLOAD.getAllUserData().iteritems()])
- perms = permlist()
-
- for data in user.itervalues():
- data["perms"] = {}
- get_permission(data["perms"], data["permission"])
- data["perms"]["admin"] = True if data["role"] is 0 else False
-
-
- s = request.environ.get('beaker.session')
- if request.environ.get('REQUEST_METHOD', "GET") == "POST":
- for name in user:
- if request.POST.get("%s|admin" % name, False):
- user[name]["role"] = 0
- user[name]["perms"]["admin"] = True
- elif name != s["name"]:
- user[name]["role"] = 1
- user[name]["perms"]["admin"] = False
-
- # set all perms to false
- for perm in perms:
- user[name]["perms"][perm] = False
-
-
- for perm in request.POST.getall("%s|perms" % name):
- user[name]["perms"][perm] = True
-
- user[name]["permission"] = set_permission(user[name]["perms"])
-
- PYLOAD.setUserPermission(name, user[name]["permission"], user[name]["role"])
-
- return render_to_response("admin.html", {"users": user, "permlist": perms}, [pre_processor])
-
-
-@route("/setup")
-def setup():
- if PYLOAD or not SETUP:
- return base([_("Run pyLoadCore.py -s to access the setup.")])
-
- return render_to_response('setup.html', {"user": False, "perms": False})
-
-
-@route("/info")
-def info():
- conf = PYLOAD.getConfigDict()
-
- if hasattr(os, "uname"):
- extra = os.uname()
- else:
- extra = tuple()
-
- data = {"python": sys.version,
- "os": " ".join((os.name, sys.platform) + extra),
- "version": PYLOAD.getServerVersion(),
- "folder": abspath(PYLOAD_DIR), "config": abspath(""),
- "download": abspath(conf["general"]["download_folder"]["value"]),
- "freespace": formatSize(PYLOAD.freeSpace()),
- "remote": conf["remote"]["port"]["value"],
- "webif": conf["webinterface"]["port"]["value"],
- "language": conf["general"]["language"]["value"]}
-
- return render_to_response("info.html", data, [pre_processor])
diff --git a/module/web/servers/lighttpd_default.conf b/module/web/servers/lighttpd_default.conf
deleted file mode 100644
index e56dda35f..000000000
--- a/module/web/servers/lighttpd_default.conf
+++ /dev/null
@@ -1,153 +0,0 @@
-# lighttpd configuration file
-#
-# use it as a base for lighttpd 1.0.0 and above
-#
-# $Id: lighttpd.conf,v 1.7 2004/11/03 22:26:05 weigon Exp $
-
-############ Options you really have to take care of ####################
-
-## modules to load
-# at least mod_access and mod_accesslog should be loaded
-# all other module should only be loaded if really neccesary
-# - saves some time
-# - saves memory
-server.modules = (
- "mod_rewrite",
- "mod_redirect",
- "mod_alias",
- "mod_access",
-# "mod_trigger_b4_dl",
-# "mod_auth",
-# "mod_status",
-# "mod_setenv",
- "mod_fastcgi",
-# "mod_proxy",
-# "mod_simple_vhost",
-# "mod_evhost",
-# "mod_userdir",
-# "mod_cgi",
-# "mod_compress",
-# "mod_ssi",
-# "mod_usertrack",
-# "mod_expire",
-# "mod_secdownload",
-# "mod_rrdtool",
-# "mod_accesslog"
- )
-
-## A static document-root. For virtual hosting take a look at the
-## mod_simple_vhost module.
-server.document-root = "%(path)"
-
-## where to send error-messages to
-server.errorlog = "%(path)/error.log"
-
-# files to check for if .../ is requested
-index-file.names = ( "index.php", "index.html",
- "index.htm", "default.htm" )
-
-## set the event-handler (read the performance section in the manual)
-# server.event-handler = "freebsd-kqueue" # needed on OS X
-
-# mimetype mapping
-mimetype.assign = (
- ".pdf" => "application/pdf",
- ".sig" => "application/pgp-signature",
- ".spl" => "application/futuresplash",
- ".class" => "application/octet-stream",
- ".ps" => "application/postscript",
- ".torrent" => "application/x-bittorrent",
- ".dvi" => "application/x-dvi",
- ".gz" => "application/x-gzip",
- ".pac" => "application/x-ns-proxy-autoconfig",
- ".swf" => "application/x-shockwave-flash",
- ".tar.gz" => "application/x-tgz",
- ".tgz" => "application/x-tgz",
- ".tar" => "application/x-tar",
- ".zip" => "application/zip",
- ".mp3" => "audio/mpeg",
- ".m3u" => "audio/x-mpegurl",
- ".wma" => "audio/x-ms-wma",
- ".wax" => "audio/x-ms-wax",
- ".ogg" => "application/ogg",
- ".wav" => "audio/x-wav",
- ".gif" => "image/gif",
- ".jar" => "application/x-java-archive",
- ".jpg" => "image/jpeg",
- ".jpeg" => "image/jpeg",
- ".png" => "image/png",
- ".xbm" => "image/x-xbitmap",
- ".xpm" => "image/x-xpixmap",
- ".xwd" => "image/x-xwindowdump",
- ".css" => "text/css",
- ".html" => "text/html",
- ".htm" => "text/html",
- ".js" => "text/javascript",
- ".asc" => "text/plain",
- ".c" => "text/plain",
- ".cpp" => "text/plain",
- ".log" => "text/plain",
- ".conf" => "text/plain",
- ".text" => "text/plain",
- ".txt" => "text/plain",
- ".dtd" => "text/xml",
- ".xml" => "text/xml",
- ".mpeg" => "video/mpeg",
- ".mpg" => "video/mpeg",
- ".mov" => "video/quicktime",
- ".qt" => "video/quicktime",
- ".avi" => "video/x-msvideo",
- ".asf" => "video/x-ms-asf",
- ".asx" => "video/x-ms-asf",
- ".wmv" => "video/x-ms-wmv",
- ".bz2" => "application/x-bzip",
- ".tbz" => "application/x-bzip-compressed-tar",
- ".tar.bz2" => "application/x-bzip-compressed-tar",
- # default mime type
- "" => "application/octet-stream",
- )
-
-# Use the "Content-Type" extended attribute to obtain mime type if possible
-#mimetype.use-xattr = "enable"
-
-#### accesslog module
-accesslog.filename = "%(path)/access.log"
-
-url.access-deny = ( "~", ".inc" )
-
-$HTTP["url"] =~ "\.pdf$" {
- server.range-requests = "disable"
-}
-static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
-
-server.pid-file = "%(path)/lighttpd.pid"
-
-server.bind = "%(host)"
-server.port = %(port)
-
-#server.document-root = "/home/user/public_html"
-fastcgi.server = (
- "/pyload.fcgi" => (
- "main" => (
- "host" => "127.0.0.1",
- "port" => 9295,
- "check-local" => "disable",
- "docroot" => "/",
- )
- ),
-)
-
-alias.url = (
- "/media/" => "%(media)/",
- "/admin/media/" => "/usr/lib/python%(version)/site-packages/django/contrib/admin/media/",
-)
-
-url.rewrite-once = (
- "^(/media.*)$" => "$1",
- "^(/admin/media.*)$" => "$1",
- "^/favicon\.ico$" => "/media/img/favicon.ico",
- "^(/pyload.fcgi.*)$" => "$1",
- "^(/.*)$" => "/pyload.fcgi$1",
-)
-
-%(ssl) \ No newline at end of file
diff --git a/module/web/servers/nginx_default.conf b/module/web/servers/nginx_default.conf
deleted file mode 100644
index b4ebd1e02..000000000
--- a/module/web/servers/nginx_default.conf
+++ /dev/null
@@ -1,87 +0,0 @@
-daemon off;
-pid %(path)/nginx.pid;
-worker_processes 2;
-
-error_log %(path)/error.log info;
-
-events {
- worker_connections 1024;
- use epoll;
-}
-
-http {
- include /etc/nginx/conf/mime.types;
- default_type application/octet-stream;
-
- %(ssl)
-
- log_format main
- '$remote_addr - $remote_user [$time_local] '
- '"$request" $status $bytes_sent '
- '"$http_referer" "$http_user_agent" '
- '"$gzip_ratio"';
-
- error_log %(path)/error.log info;
-
- client_header_timeout 10m;
- client_body_timeout 10m;
- send_timeout 10m;
-
- client_body_temp_path %(path)/client_body_temp;
- proxy_temp_path %(path)/proxy_temp;
- fastcgi_temp_path %(path)/fastcgi_temp;
-
-
- connection_pool_size 256;
- client_header_buffer_size 1k;
- large_client_header_buffers 4 2k;
- request_pool_size 4k;
-
- gzip on;
- gzip_min_length 1100;
- gzip_buffers 4 8k;
- gzip_types text/plain;
-
- output_buffers 1 32k;
- postpone_output 1460;
-
- sendfile on;
- tcp_nopush on;
- tcp_nodelay on;
-
- keepalive_timeout 75 20;
-
- ignore_invalid_headers on;
-
- server {
- listen %(port);
- server_name %(host);
- # site_media - folder in uri for static files
- location ^~ /media {
- root %(media)/..;
- }
- location ^~ /admin/media {
- root /usr/lib/python%(version)/site-packages/django/contrib;
- }
-location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|mov) {
- access_log off;
- expires 30d;
-}
- location / {
- # host and port to fastcgi server
- fastcgi_pass 127.0.0.1:9295;
- fastcgi_param PATH_INFO $fastcgi_script_name;
- fastcgi_param REQUEST_METHOD $request_method;
- fastcgi_param QUERY_STRING $query_string;
- fastcgi_param CONTENT_TYPE $content_type;
- fastcgi_param CONTENT_LENGTH $content_length;
- fastcgi_param SERVER_NAME $server_name;
- fastcgi_param SERVER_PORT $server_port;
- fastcgi_param SERVER_PROTOCOL $server_protocol;
- fastcgi_pass_header Authorization;
- fastcgi_intercept_errors off;
- }
- access_log %(path)/access.log main;
- error_log %(path)/error.log;
- }
- }
diff --git a/module/web/templates/500.html b/module/web/templates/500.html
deleted file mode 100644
index e15090b66..000000000
--- a/module/web/templates/500.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
- "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<head>
- <title>Server Error</title>
-</head>
-<body>
-<h1>Server Error occured. Please enable debug mode to get a more detailed report.</h1>
-</body>
-</html>
diff --git a/module/web/templates/default/admin.html b/module/web/templates/default/admin.html
deleted file mode 100644
index b049411fd..000000000
--- a/module/web/templates/default/admin.html
+++ /dev/null
@@ -1,98 +0,0 @@
-{% extends 'default/base.html' %}
-
-{% block head %}
- <script type="text/javascript" src="media/js/admin.js"></script>
-{% endblock %}
-
-
-{% block title %}{{ _("Administrate") }} - {{ super() }} {% endblock %}
-{% block subtitle %}{{ _("Administrate") }}{% endblock %}
-
-{% block content %}
-
- <a href="#" id="quit-pyload" style="font-size: large; font-weight: bold;">{{_("Quit pyLoad")}}</a> |
- <a href="#" id="restart-pyload" style="font-size: large; font-weight: bold;">{{_("Restart pyLoad")}}</a>
- <br>
- <br>
-
- {{ _("To add user or change passwords use:") }} <b>python pyLoadCore.py -u</b><br>
- {{ _("Important: Admin user have always all permissions!") }}
-
- <form action="" method="POST">
- <table class="settable wide">
- <thead style="font-size: 11px">
- <th>
- {{ _("Name") }}
- </th>
- <th>
- {{ _("Change Password") }}
- </th>
- <th>
- {{ _("Admin") }}
- </th>
- <th>
- {{ _("Permissions") }}
- </th>
- </thead>
-
- {% for name, data in users.iteritems() %}
- <tr>
- <td>{{ name }}</td>
- <td><a class="change_password" href="#" id="change_pw|{{name}}">{{ _("change") }}</a></td>
- <td><input name="{{ name }}|admin" type="checkbox" {% if data.perms.admin %}
- checked="True" {% endif %}"></td>
- <td>
- <select multiple="multiple" size="{{ permlist|length }}" name="{{ name }}|perms">
- {% for perm in permlist %}
- {% if data.perms|getitem(perm) %}
- <option selected="selected">{{ perm }}</option>
- {% else %}
- <option>{{ perm }}</option>
- {% endif %}
- {% endfor %}
- </select>
- </td>
- </tr>
- {% endfor %}
-
-
- </table>
-
- <button class="styled_button" type="submit">{{ _("Submit") }}</button>
- </form>
-{% endblock %}
-{% block hidden %}
- <div id="password_box" class="window_box" style="z-index: 2">
- <form id="password_form" action="/json/change_password" method="POST" enctype="multipart/form-data">
- <h1>{{ _("Change Password") }}</h1>
-
- <p>{{ _("Enter your current and desired Password.") }}</p>
- <label for="user_login">{{ _("User") }}
- <span class="small">{{ _("Your username.") }}</span>
- </label>
- <input id="user_login" name="user_login" type="text" size="20"/>
-
- <label for="login_current_password">{{ _("Current password") }}
- <span class="small">{{ _("The password for this account.") }}</span>
- </label>
- <input id="login_current_password" name="login_current_password" type="password" size="20"/>
-
- <label for="login_new_password">{{ _("New password") }}
- <span class="small">{{ _("The new password.") }}</span>
- </label>
- <input id="login_new_password" name="login_new_password" type="password" size="20"/>
-
- <label for="login_new_password2">{{ _("New password (repeat)") }}
- <span class="small">{{ _("Please repeat the new password.") }}</span>
- </label>
- <input id="login_new_password2" name="login_new_password2" type="password" size="20"/>
-
-
- <button id="login_password_button" type="submit">{{ _("Submit") }}</button>
- <button id="login_password_reset" style="margin-left: 0" type="reset">{{ _("Reset") }}</button>
- <div class="spacer"></div>
-
- </form>
-
- </div>
-{% endblock %}
diff --git a/module/web/templates/default/base.html b/module/web/templates/default/base.html
deleted file mode 100644
index 147c08a37..000000000
--- a/module/web/templates/default/base.html
+++ /dev/null
@@ -1,180 +0,0 @@
-<?xml version="1.0" ?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-<link rel="stylesheet" type="text/css" href="/media/default/css/default.css"/>
-<link rel="stylesheet" type="text/css" href="/media/default/css/window.css"/>
-<link rel="stylesheet" type="text/css" href="/media/default/css/MooDialog.css"/>
-
-<script type="text/javascript" src="/media/js/mootools-core-1.4.1.js"></script>
-<script type="text/javascript" src="/media/js/mootools-more-1.4.0.1.js"></script>
-<script type="text/javascript" src="/media/js/MooDialog_static.js"></script>
-<script type="text/javascript" src="/media/js/purr_static.js"></script>
-
-
-<script type="text/javascript" src="/media/js/base.js"></script>
-
-<title>{% block title %}pyLoad {{_("Webinterface")}}{% endblock %}</title>
-
-{% block head %}
-{% endblock %}
-</head>
-<body>
-<a class="anchor" name="top" id="top"></a>
-
-<div id="head-panel">
-
-
- <div id="head-search-and-login">
- {% block headpanel %}
-
- {% if user.is_authenticated %}
-
-
-{% if update %}
-<span>
-<span style="font-weight: bold; margin: 0 2px 0 2px;">{{_("pyLoad Update available!")}}</span>
-</span>
-{% endif %}
-
-
-{% if plugins %}
-<span>
-<span style="font-weight: bold; margin: 0 2px 0 2px;">{{_("Plugins updated, please restart!")}}</span>
-</span>
-{% endif %}
-
-<span id="cap_info" style="display: {% if captcha %}inline{%else%}none{% endif %}">
-<img src="/media/default/img/images.png" alt="Captcha:" style="vertical-align:middle; margin:2px" />
-<span style="font-weight: bold; cursor: pointer; margin-right: 2px;">{{_("Captcha waiting")}}</span>
-</span>
-
- <img src="/media/default/img/head-login.png" alt="User:" style="vertical-align:middle; margin:2px" /><span style="padding-right: 2px;">{{user.name}}</span>
- <ul id="user-actions">
- <li><a href="/logout" class="action logout" rel="nofollow">{{_("Logout")}}</a></li>
- {% if user.is_admin %}
- <li><a href="/admin" class="action profile" rel="nofollow">{{_("Administrate")}}</a></li>
- {% endif %}
- <li><a href="/info" class="action info" rel="nofollow">{{_("Info")}}</a></li>
-
- </ul>
-{% else %}
- <span style="padding-right: 2px;">{{_("Please Login!")}}</span>
-{% endif %}
-
- {% endblock %}
- </div>
-
- <a href="/"><img id="head-logo" src="/media/default/img/pyload-logo-edited3.5-new-font-small.png" alt="pyLoad" /></a>
-
- <div id="head-menu">
- <ul>
-
- {% macro selected(name, right=False) -%}
- {% if name in url -%}class="{% if right -%}right {% endif %}selected"{%- endif %}
- {% if not name in url and right -%}class="right"{%- endif %}
- {%- endmacro %}
-
-
- {% block menu %}
- <li>
- <a href="/" title=""><img src="/media/default/img/head-menu-home.png" alt="" /> {{_("Home")}}</a>
- </li>
- <li {{ selected('queue') }}>
- <a href="/queue/" title=""><img src="/media/default/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
- </li>
- <li {{ selected('collector') }}>
- <a href="/collector/" title=""><img src="/media/default/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
- </li>
- <li {{ selected('downloads') }}>
- <a href="/downloads/" title=""><img src="/media/default/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
- </li>
-{# <li {{ selected('filemanager') }}>#}
-{# <a href="/filemanager/" title=""><img src="/media/default/img/head-menu-download.png" alt="" /> {{_("FileManager")}}</a>#}
-{# </li>#}
- <li {{ selected('logs', True) }}>
- <a href="/logs/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-index.png" alt="" />{{_("Logs")}}</a>
- </li>
- <li {{ selected('settings', True) }}>
- <a href="/settings/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-config.png" alt="" />{{_("Config")}}</a>
- </li>
- {% endblock %}
-
- </ul>
- </div>
-
- <div style="clear:both;"></div>
-</div>
-
-{% if perms.STATUS %}
-<ul id="page-actions2">
- <li id="action_play"><a href="#" class="action play" accesskey="o" rel="nofollow">{{_("Start")}}</a></li>
- <li id="action_stop"><a href="#" class="action stop" accesskey="o" rel="nofollow">{{_("Stop")}}</a></li>
- <li id="action_cancel"><a href="#" class="action cancel" accesskey="o" rel="nofollow">{{_("Cancel")}}</a></li>
- <li id="action_add"><a href="#" class="action add" accesskey="o" rel="nofollow" >{{_("Add")}}</a></li>
-</ul>
-{% endif %}
-
-{% if perms.LIST %}
-<ul id="page-actions">
- <li><span class="time">{{_("Download:")}}</span><a id="time" style=" background-color: {% if status.download %}#8ffc25{% else %} #fc6e26{% endif %}; padding-left: 0cm; padding-right: 0.1cm; "> {% if status.download %}{{_("on")}}{% else %}{{_("off")}}{% endif %}</a></li>
- <li><span class="reconnect">{{_("Reconnect:")}}</span><a id="reconnect" style=" background-color: {% if status.reconnect %}#8ffc25{% else %} #fc6e26{% endif %}; padding-left: 0cm; padding-right: 0.1cm; "> {% if status.reconnect %}{{_("on")}}{% else %}{{_("off")}}{% endif %}</a></li>
- <li><a class="action backlink">{{_("Speed:")}} <b id="speed">{{ status.speed }}</b></a></li>
- <li><a class="action cog">{{_("Active:")}} <b id="aktiv" title="{{_("Active")}}">{{ status.active }}</b> / <b id="aktiv_from" title="{{_("Queued")}}">{{ status.queue }}</b> / <b id="aktiv_total" title="{{_("Total")}}">{{ status.total }}</b></a></li>
- <li><a href="" class="action revisions" accesskey="o" rel="nofollow">{{_("Reload page")}}</a></li>
-</ul>
-{% endif %}
-
-{% block pageactions %}
-{% endblock %}
-<br/>
-
-<div id="body-wrapper" class="dokuwiki">
-
-<div id="content" lang="en" dir="ltr">
-
-<h1>{% block subtitle %}pyLoad - {{_("Webinterface")}}{% endblock %}</h1>
-
-{% block statusbar %}
-{% endblock %}
-
-
-<br/>
-
-<div class="level1" style="clear:both">
-</div>
-<noscript><h1>Enable JavaScript to use the webinterface.</h1></noscript>
-
-{% for message in messages %}
- <b><p>{{message}}</p></b>
-{% endfor %}
-
-<div id="load-indicator" style="opacity: 0; float: right; margin-top: -10px;">
- <img src="/media/default/img/ajax-loader.gif" alt="" style="padding-right: 5px"/>
- {{_("loading")}}
-</div>
-
-{% block content %}
-{% endblock content %}
-
- <hr style="clear: both;" />
-
-<div id="foot">&copy; 2008-2011 pyLoad Team
-<a href="#top" class="action top" accesskey="x"><span>{{_("Back to top")}}</span></a><br />
-<!--<div class="breadcrumbs"></div>-->
-
-</div>
-</div>
-</div>
-
-<div style="display: none;">
- {% include "default/window.html" %}
- {% include "default/captcha.html" %}
- {% block hidden %}
- {% endblock %}
-</div>
-</body>
-</html>
diff --git a/module/web/templates/default/captcha.html b/module/web/templates/default/captcha.html
deleted file mode 100644
index 288375b76..000000000
--- a/module/web/templates/default/captcha.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<!-- Captcha box -->
-<div id="cap_box" class="window_box">
-
- <form id="cap_form" action="/json/set_captcha" method="POST" enctype="multipart/form-data" onsubmit="return false;">
-
- <h1>{{_("Captcha reading")}}</h1>
- <p id="cap_title">{{_("Please read the text on the captcha.")}}</p>
-
- <div id="cap_textual">
-
- <input id="cap_id" name="cap_id" type="hidden" value="" />
-
- <label>{{_("Captcha")}}
- <span class="small">{{_("The captcha.")}}</span>
- </label>
- <span class="cont">
- <img id="cap_textual_img" src="">
- </span>
-
- <label>{{_("Text")}}
- <span class="small">{{_("Input the text on the captcha.")}}</span>
- </label>
- <input id="cap_result" name="cap_result" type="text" size="20" />
-
- </div>
-
- <div id="cap_positional" style="text-align: center">
- <img id="cap_positional_img" src="" style="margin: 10px; cursor:pointer">
- </div>
-
- <div id="button_bar" style="text-align: center">
- <span>
- <button id="cap_submit" type="submit" style="margin-left: 0">{{_("Submit")}}</button>
- <button id="cap_reset" type="reset" style="margin-left: 0">{{_("Close")}}</button>
- </span>
- </div>
-
- <div class="spacer"></div>
-
- </form>
-
-</div> \ No newline at end of file
diff --git a/module/web/templates/default/downloads.html b/module/web/templates/default/downloads.html
deleted file mode 100644
index 450b8a102..000000000
--- a/module/web/templates/default/downloads.html
+++ /dev/null
@@ -1,29 +0,0 @@
-{% extends 'default/base.html' %}
-
-{% block title %}Downloads - {{super()}} {% endblock %}
-
-{% block subtitle %}
-{{_("Downloads")}}
-{% endblock %}
-
-{% block content %}
-
-<ul>
- {% for folder in files.folder %}
- <li>
- {{ folder.name }}
- <ul>
- {% for file in folder.files %}
- <li><a href='get/{{ folder.path|escape }}/{{ file|escape }}'>{{file}}</a></li>
- {% endfor %}
- </ul>
- </li>
- {% endfor %}
-
- {% for file in files.files %}
- <li> <a href='get/{{ file|escape }}'>{{ file }}</a></li>
- {% endfor %}
-
-</ul>
-
-{% endblock %} \ No newline at end of file
diff --git a/module/web/templates/default/filemanager.html b/module/web/templates/default/filemanager.html
deleted file mode 100644
index 97095c13e..000000000
--- a/module/web/templates/default/filemanager.html
+++ /dev/null
@@ -1,80 +0,0 @@
-{% extends 'default/base.html' %}
-
-{% block head %}
-
-<script type="text/javascript" src="/filemanager_ui.js"></script>
-
-<script type="text/javascript">
-
-document.addEvent("domready", function(){
- var fmUI = new FilemanagerUI("url",1);
-});
-</script>
-{% endblock %}
-
-{% block title %}Downloads - {{super()}} {% endblock %}
-
-
-{% block subtitle %}
-{{_("FileManager")}}
-{% endblock %}
-
-{% macro display_file(file) %}
- <li class="file">
- <input type="hidden" name="path" class="path" value="{{ file.path }}" />
- <input type="hidden" name="name" class="name" value="{{ file.name }}" />
- <span>
- <b>{{ file.name }}</b>
- <span class="buttons" style="opacity:0">
- <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/media/default/img/pencil.png" />
- &nbsp;&nbsp;
- <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/media/default/img/delete.png" />
- </span>
- </span>
- </li>
-{%- endmacro %}
-
-{% macro display_folder(fld, open = false) -%}
- <li class="folder">
- <input type="hidden" name="path" class="path" value="{{ fld.path }}" />
- <input type="hidden" name="name" class="name" value="{{ fld.name }}" />
- <span>
- <b>{{ fld.name }}</b>
- <span class="buttons" style="opacity:0">
- <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/media/default/img/pencil.png" />
- &nbsp;&nbsp;
- <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/media/default/img/delete.png" />
- &nbsp;&nbsp;
- <img title="{{_("Add subdirectory")}}" class="mkdir" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/media/default/img/add_folder.png" />
- </span>
- </span>
- {% if (fld.folders|length + fld.files|length) > 0 %}
- {% if open %}
- <ul>
- {% else %}
- <ul style="display:none">
- {% endif %}
- {% for child in fld.folders %}
- {{ display_folder(child) }}
- {% endfor %}
- {% for child in fld.files %}
- {{ display_file(child) }}
- {% endfor %}
- </ul>
- {% else %}
- <div style="display:none">{{ _("Folder is empty") }}</div>
- {% endif %}
- </li>
-{%- endmacro %}
-
-{% block content %}
-
-<div style="clear:both"><!-- --></div>
-
-<ul id="directories-list">
-{{ display_folder(root, true) }}
-</ul>
-
-{% include "default/rename_directory.html" %}
-
-{% endblock %}
diff --git a/module/web/templates/default/filemanager_ui.js b/module/web/templates/default/filemanager_ui.js
deleted file mode 100644
index ed64ab69d..000000000
--- a/module/web/templates/default/filemanager_ui.js
+++ /dev/null
@@ -1,291 +0,0 @@
-var load, rename_box, confirm_box;
-
-document.addEvent("domready", function() {
- load = new Fx.Tween($("load-indicator"), {link: "cancel"});
- load.set("opacity", 0);
-
- rename_box = new Fx.Tween($('rename_box'));
- confirm_box = new Fx.Tween($('confirm_box'));
- $('rename_reset').addEvent('click', function() {
- hide_rename_box()
- });
- $('delete_reset').addEvent('click', function() {
- hide_confirm_box()
- });
-
- /*$('filemanager_actions_list').getChildren("li").each(function(action) {
- var action_name = action.className;
- if(functions[action.className] != undefined)
- {
- action.addEvent('click', functions[action.className]);
- }
- });*/
-});
-
-function indicateLoad() {
- //$("load-indicator").reveal();
- load.start("opacity", 1)
-}
-
-function indicateFinish() {
- load.start("opacity", 0)
-}
-
-function indicateSuccess() {
- indicateFinish();
- notify.alert('{{_("Success")}}.', {
- 'className': 'success'
- });
-}
-
-function indicateFail() {
- indicateFinish();
- notify.alert('{{_("Failed")}}.', {
- 'className': 'error'
- });
-}
-
-function show_rename_box() {
- bg_show();
- $("rename_box").setStyle('display', 'block');
- rename_box.start('opacity', 1)
-}
-
-function hide_rename_box() {
- bg_hide();
- rename_box.start('opacity', 0).chain(function() {
- $('rename_box').setStyle('display', 'none');
- });
-}
-
-function show_confirm_box() {
- bg_show();
- $("confirm_box").setStyle('display', 'block');
- confirm_box.start('opacity', 1)
-}
-
-function hide_confirm_box() {
- bg_hide();
- confirm_box.start('opacity', 0).chain(function() {
- $('confirm_box').setStyle('display', 'none');
- });
-}
-
-var FilemanagerUI = new Class({
- initialize: function(url, type) {
- this.url = url;
- this.type = type;
- this.directories = [];
- this.files = [];
- this.parseChildren();
- },
-
- parseChildren: function() {
- $("directories-list").getChildren("li.folder").each(function(ele) {
- var path = ele.getElements("input.path")[0].get("value");
- var name = ele.getElements("input.name")[0].get("value");
- this.directories.push(new Item(this, path, name, ele))
- }.bind(this));
-
- $("directories-list").getChildren("li.file").each(function(ele) {
- var path = ele.getElements("input.path")[0].get("value");
- var name = ele.getElements("input.name")[0].get("value");
- this.files.push(new Item(this, path, name, ele))
- }.bind(this));
- }
-});
-
-var Item = new Class({
- initialize: function(ui, path, name, ele) {
- this.ui = ui;
- this.path = path;
- this.name = name;
- this.ele = ele;
- this.directories = [];
- this.files = [];
- this.actions = new Array();
- this.actions["delete"] = this.del;
- this.actions["rename"] = this.rename;
- this.actions["mkdir"] = this.mkdir;
- this.parseElement();
-
- var pname = this.ele.getElements("span")[0];
- this.buttons = new Fx.Tween(this.ele.getElements(".buttons")[0], {link: "cancel"});
- this.buttons.set("opacity", 0);
-
- pname.addEvent("mouseenter", function(e) {
- this.buttons.start("opacity", 1)
- }.bind(this));
-
- pname.addEvent("mouseleave", function(e) {
- this.buttons.start("opacity", 0)
- }.bind(this));
-
- },
-
- parseElement: function() {
- this.ele.getChildren('span span.buttons img').each(function(img) {
- img.addEvent('click', this.actions[img.className].bind(this));
- }, this);
-
- //click on the directory name must open the directory itself
- this.ele.getElements('b')[0].addEvent('click', this.toggle.bind(this));
-
- //iterate over child directories
- var uls = this.ele.getElements('ul');
- if(uls.length > 0)
- {
- uls[0].getChildren("li.folder").each(function(fld) {
- var path = fld.getElements("input.path")[0].get("value");
- var name = fld.getElements("input.name")[0].get("value");
- this.directories.push(new Item(this, path, name, fld));
- }.bind(this));
- uls[0].getChildren("li.file").each(function(fld) {
- var path = fld.getElements("input.path")[0].get("value");
- var name = fld.getElements("input.name")[0].get("value");
- this.files.push(new Item(this, path, name, fld));
- }.bind(this));
- }
- },
-
- reorderElements: function() {
- //TODO sort the main ul again (to keep data ordered after renaming something)
- },
-
- del: function(event) {
- $("confirm_form").removeEvents("submit");
- $("confirm_form").addEvent("submit", this.deleteDirectory.bind(this));
-
- $$("#confirm_form p").set('html', '{{_(("Are you sure you want to delete the selected item?"))}}');
-
- show_confirm_box();
- event.stop();
- },
-
- deleteDirectory: function(event) {
- hide_confirm_box();
- new Request.JSON({
- method: 'POST',
- url: "/json/filemanager/delete",
- data: {"path": this.path, "name": this.name},
- onSuccess: function(data) {
- if(data.response == "success")
- {
- new Fx.Tween(this.ele).start('opacity', 0);
- var ul = this.ele.parentNode;
- this.ele.dispose();
- //if this was the only child, add a "empty folder" div
- if(!ul.getChildren('li')[0])
- {
- var div = new Element("div", { 'html': '{{ _("Folder is empty") }}' });
- div.replaces(ul);
- }
-
- indicateSuccess();
- } else
- {
- //error from json code...
- indicateFail();
- }
- }.bind(this),
- onFailure: indicateFail
- }).send();
-
- event.stop();
- },
-
- rename: function(event) {
- $("rename_form").removeEvents("submit");
- $("rename_form").addEvent("submit", this.renameDirectory.bind(this));
-
- $("path").set("value", this.path);
- $("old_name").set("value", this.name);
- $("new_name").set("value", this.name);
-
- show_rename_box();
- event.stop();
- },
-
- renameDirectory: function(event) {
- hide_rename_box();
- new Request.JSON({
- method: 'POST',
- url: "/json/filemanager/rename",
- onSuccess: function(data) {
- if(data.response == "success")
- {
- this.name = $("new_name").get("value");
- this.ele.getElements("b")[0].set('html', $("new_name").get("value"));
- this.reorderElements();
- indicateSuccess();
- } else
- {
- //error from json code...
- indicateFail();
- }
- }.bind(this),
- onFailure: indicateFail
- }).send($("rename_form").toQueryString());
-
- event.stop();
- },
-
- mkdir: function(event) {
- new Request.JSON({
- method: 'POST',
- url: "/json/filemanager/mkdir",
- data: {"path": this.path + "/" + this.name, "name": '{{_("New folder")}}'},
- onSuccess: function(data) {
- if(data.response == "success")
- {
- new Request.HTML({
- method: 'POST',
- url: "/filemanager/get_dir",
- data: {"path": data.path, "name": data.name},
- onSuccess: function(li) {
- //add node as first child of ul
- var ul = this.ele.getChildren('ul')[0];
- if(!ul)
- {
- //remove the "Folder Empty" div
- this.ele.getChildren('div').dispose();
-
- //create new ul to contain subfolder
- ul = new Element("ul");
- ul.inject(this.ele, 'bottom');
- }
- li[0].inject(ul, 'top');
-
- //add directory as a subdirectory of the current item
- this.directories.push(new Item(this.ui, data.path, data.name, ul.firstChild));
- }.bind(this),
- onFailure: indicateFail
- }).send();
- indicateSuccess();
- } else
- {
- //error from json code...
- indicateFail();
- }
- }.bind(this),
- onFailure: indicateFail
- }).send();
-
- event.stop();
- },
-
- toggle: function() {
- var child = this.ele.getElement('ul');
- if(child == null)
- child = this.ele.getElement('div');
-
- if(child != null)
- {
- if (child.getStyle('display') == "block") {
- child.dissolve();
- } else {
- child.reveal();
- }
- }
- }
-});
diff --git a/module/web/templates/default/folder.html b/module/web/templates/default/folder.html
deleted file mode 100644
index b385e80cb..000000000
--- a/module/web/templates/default/folder.html
+++ /dev/null
@@ -1,15 +0,0 @@
-<li class="folder">
- <input type="hidden" name="path" class="path" value="{{ path }}" />
- <input type="hidden" name="name" class="name" value="{{ name }}" />
- <span>
- <b>{{ name }}</b>
- <span class="buttons" style="opacity:0">
- <img title="{{_("Rename Directory")}}" class="rename" style="cursor: pointer" height="12px" src="/media/default/img/pencil.png" />
- &nbsp;&nbsp;
- <img title="{{_("Delete Directory")}}" class="delete" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/media/default/img/delete.png" />
- &nbsp;&nbsp;
- <img title="{{_("Add subdirectory")}}" class="mkdir" style="margin-left: -10px; cursor: pointer" width="12px" height="12px" src="/media/default/img/add_folder.png" />
- </span>
- </span>
- <div style="display:none">{{ _("Folder is empty") }}</div>
-</li> \ No newline at end of file
diff --git a/module/web/templates/default/home.html b/module/web/templates/default/home.html
deleted file mode 100644
index 7359e326c..000000000
--- a/module/web/templates/default/home.html
+++ /dev/null
@@ -1,266 +0,0 @@
-{% extends 'default/base.html' %}
-{% block head %}
-
-<script type="text/javascript">
-
-var em;
-var operafix = (navigator.userAgent.toLowerCase().search("opera") >= 0);
-
-document.addEvent("domready", function(){
- em = new EntryManager();
-});
-
-var EntryManager = new Class({
- initialize: function(){
- this.json = new Request.JSON({
- url: "json/links",
- secure: false,
- async: true,
- onSuccess: this.update.bind(this),
- initialDelay: 0,
- delay: 2500,
- limit: 30000
- });
-
- this.ids = [{% for link in content %}
- {% if forloop.last %}
- {{ link.id }}
- {% else %}
- {{ link.id }},
- {% endif %}
- {% endfor %}];
-
- this.entries = [];
- this.container = $('LinksAktiv');
-
- this.parseFromContent();
-
- this.json.startTimer();
- },
- parseFromContent: function(){
- this.ids.each(function(id,index){
- var entry = new LinkEntry(id);
- entry.parse();
- this.entries.push(entry)
- }, this);
- },
- update: function(data){
-
- try{
- this.ids = this.entries.map(function(item){
- return item.fid
- });
-
- this.ids.filter(function(id){
- return !this.ids.contains(id)
- },data).each(function(id){
- var index = this.ids.indexOf(id);
- this.entries[index].remove();
- this.entries = this.entries.filter(function(item){return item.fid != this},id);
- this.ids = this.ids.erase(id)
- }, this);
-
- data.links.each(function(link, i){
- if (this.ids.contains(link.fid)){
-
- var index = this.ids.indexOf(link.fid);
- this.entries[index].update(link)
-
- }else{
- var entry = new LinkEntry(link.fid);
- entry.insert(link);
- this.entries.push(entry);
- this.ids.push(link.fid);
- this.container.adopt(entry.elements.tr,entry.elements.pgbTr);
- entry.fade.start('opacity', 1);
- entry.fadeBar.start('opacity', 1);
-
- }
- }, this)
- }catch(e){
- //alert(e)
- }
- }
-});
-
-
-var LinkEntry = new Class({
- initialize: function(id){
- this.fid = id;
- this.id = id;
- },
- parse: function(){
- this.elements = {
- tr: $("link_{id}".substitute({id: this.id})),
- name: $("link_{id}_name".substitute({id: this.id})),
- status: $("link_{id}_status".substitute({id: this.id})),
- info: $("link_{id}_info".substitute({id: this.id})),
- bleft: $("link_{id}_bleft".substitute({id: this.id})),
- percent: $("link_{id}_percent".substitute({id: this.id})),
- remove: $("link_{id}_remove".substitute({id: this.id})),
- pgbTr: $("link_{id}_pgb_tr".substitute({id: this.id})),
- pgb: $("link_{id}_pgb".substitute({id: this.id}))
- };
- this.initEffects();
- },
- insert: function(item){
- try{
-
- this.elements = {
- tr: new Element('tr', {
- 'html': '',
- 'styles':{
- 'opacity': 0
- }
- }),
- name: new Element('td', {
- 'html': item.name
- }),
- status: new Element('td', {
- 'html': item.statusmsg
- }),
- info: new Element('td', {
- 'html': item.info
- }),
- bleft: new Element('td', {
- 'html': humanFileSize(item.size)
- }),
- percent: new Element('span', {
- 'html': item.percent+ '% / '+ humanFileSize(item.size-item.bleft)
- }),
- remove: new Element('img',{
- 'src': 'media/default/img/control_cancel.png',
- 'styles':{
- 'vertical-align': 'middle',
- 'margin-right': '-20px',
- 'margin-left': '5px',
- 'margin-top': '-2px',
- 'cursor': 'pointer'
- }
- }),
- pgbTr: new Element('tr', {
- 'html':''
- }),
- pgb: new Element('div', {
- 'html': '&nbsp;',
- 'styles':{
- 'height': '4px',
- 'width': item.percent+'%',
- 'background-color': '#ddd'
- }
- })
- };
-
- this.elements.tr.adopt(this.elements.name,this.elements.status,this.elements.info,this.elements.bleft,new Element('td').adopt(this.elements.percent,this.elements.remove));
- this.elements.pgbTr.adopt(new Element('td',{'colspan':5}).adopt(this.elements.pgb));
- this.initEffects();
- }catch(e){
- alert(e)
- }
- },
- initEffects: function(){
- if(!operafix)
- this.bar = new Fx.Morph(this.elements.pgb, {unit: '%', duration: 5000, link: 'link', fps:30});
- this.fade = new Fx.Tween(this.elements.tr);
- this.fadeBar = new Fx.Tween(this.elements.pgbTr);
-
- this.elements.remove.addEvent('click', function(){
- new Request({method: 'get', url: '/json/abort_link/'+this.id}).send();
- }.bind(this));
-
- },
- update: function(item){
- this.elements.name.set('text', item.name);
- this.elements.status.set('text', item.statusmsg);
- this.elements.info.set('text', item.info);
- this.elements.bleft.set('text', item.format_size);
- this.elements.percent.set('text', item.percent+ '% / '+ humanFileSize(item.size-item.bleft));
- if(!operafix)
- {
- this.bar.start({
- 'width': item.percent,
- 'background-color': [Math.round(120/100*item.percent),100,100].hsbToRgb().rgbToHex()
- });
- }
- else
- {
- this.elements.pgb.set(
- 'styles', {
- 'height': '4px',
- 'width': item.percent+'%',
- 'background-color': [Math.round(120/100*item.percent),100,100].hsbToRgb().rgbToHex(),
- });
- }
- },
- remove: function(){
- this.fade.start('opacity',0).chain(function(){this.elements.tr.dispose();}.bind(this));
- this.fadeBar.start('opacity',0).chain(function(){this.elements.pgbTr.dispose();}.bind(this));
-
- }
- });
-</script>
-
-{% endblock %}
-
-{% block subtitle %}
-{{_("Active Downloads")}}
-{% endblock %}
-
-{% block menu %}
-<li class="selected">
- <a href="/" title=""><img src="/media/default/img/head-menu-home.png" alt="" /> {{_("Home")}}</a>
-</li>
-<li>
- <a href="/queue/" title=""><img src="/media/default/img/head-menu-queue.png" alt="" /> {{_("Queue")}}</a>
-</li>
-<li>
- <a href="/collector/" title=""><img src="/media/default/img/head-menu-collector.png" alt="" /> {{_("Collector")}}</a>
-</li>
-<li>
- <a href="/downloads/" title=""><img src="/media/default/img/head-menu-development.png" alt="" /> {{_("Downloads")}}</a>
-</li>
-{#<li>#}
-{# <a href="/filemanager/" title=""><img src="/media/default/img/head-menu-download.png" alt="" /> {{_("FileManager")}}</a>#}
-{#</li>#}
-<li class="right">
- <a href="/logs/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-index.png" alt="" />{{_("Logs")}}</a>
-</li>
-<li class="right">
- <a href="/settings/" class="action index" accesskey="x" rel="nofollow"><img src="/media/default/img/head-menu-config.png" alt="" />{{_("Config")}}</a>
-</li>
-{% endblock %}
-
-{% block content %}
-<table width="100%" class="queue">
- <thead>
- <tr class="header">
- <th>{{_("Name")}}</th>
- <th>{{_("Status")}}</th>
- <th>{{_("Information")}}</th>
- <th>{{_("Size")}}</th>
- <th>{{_("Progress")}}</th>
- </tr>
- </thead>
- <tbody id="LinksAktiv">
-
- {% for link in content %}
- <tr id="link_{{ link.id }}">
- <td id="link_{{ link.id }}_name">{{ link.name }}</td>
- <td id="link_{{ link.id }}_status">{{ link.status }}</td>
- <td id="link_{{ link.id }}_info">{{ link.info }}</td>
- <td id="link_{{ link.id }}_bleft">{{ link.format_size }}</td>
- <td>
- <span id="link_{{ link.id }}_percent">{{ link.percent }}% /{{ link.bleft }}</span>
- <img id="link_{{ link.id }}_remove" style="vertical-align: middle; margin-right: -20px; margin-left: 5px; margin-top: -2px; cursor:pointer;" src="media/default/img/control_cancel.png"/>
- </td>
- </tr>
- <tr id="link_{{ link.id }}_pgb_tr">
- <td colspan="5">
- <div id="link_{{ link.id }}_pgb" class="progressBar" style="background-color: green; height:4px; width: {{ link.percent }}%;">&nbsp;</div>
- </td>
- </tr>
- {% endfor %}
-
- </tbody>
-</table>
-{% endblock %} \ No newline at end of file
diff --git a/module/web/templates/default/info.html b/module/web/templates/default/info.html
deleted file mode 100644
index 77ae57376..000000000
--- a/module/web/templates/default/info.html
+++ /dev/null
@@ -1,81 +0,0 @@
-{% extends 'default/base.html' %}
-
-{% block head %}
- <script type="text/javascript">
- window.addEvent("domready", function() {
- var ul = new Element('ul#twitter_update_list');
- var script1 = new Element('script[src=http://twitter.com/javascripts/blogger.js][type=text/javascript]');
- var script2 = new Element('script[src=http://twitter.com/statuses/user_timeline/pyLoad.json?callback=twitterCallback2&count=6][type=text/javascript]');
- $("twitter").adopt(ul, script1, script2);
- });
- </script>
-{% endblock %}
-
-{% block title %}{{ _("Information") }} - {{ super() }} {% endblock %}
-{% block subtitle %}{{ _("Information") }}{% endblock %}
-
-{% block content %}
- <h3>{{ _("News") }}</h3>
- <div id="twitter"></div>
-
- <h3>{{ _("Support") }}</h3>
-
- <ul>
- <li style="font-weight:bold;">
- <a href="http://pyload.org/wiki" target="_blank">Wiki</a>
- &nbsp;&nbsp;|&nbsp;&nbsp;
- <a href="http://forum.pyload.org/" target="_blank">Forum</a>
- &nbsp;&nbsp;|&nbsp;&nbsp;
- <a href="http://pyload.org/irc/" target="_blank">Chat</a>
- </li>
- <li style="font-weight:bold;"><a href="http://docs.pyload.org" target="_blank">Documentation</a></li>
- <li style="font-weight:bold;"><a href="https://bitbucket.org/spoob/pyload/overview" target="_blank">Development</a></li>
- <li style="font-weight:bold;"><a href="https://bitbucket.org/spoob/pyload/issues?status=new&status=open" target="_blank">Issue Tracker</a></li>
-
- </ul>
-
- <h3>{{ _("System") }}</h3>
- <table class="system">
- <tr>
- <td>{{ _("Python:") }}</td>
- <td>{{ python }}</td>
- </tr>
- <tr>
- <td>{{ _("OS:") }}</td>
- <td>{{ os }}</td>
- </tr>
- <tr>
- <td>{{ _("pyLoad version:") }}</td>
- <td>{{ version }}</td>
- </tr>
- <tr>
- <td>{{ _("Installation Folder:") }}</td>
- <td>{{ folder }}</td>
- </tr>
- <tr>
- <td>{{ _("Config Folder:") }}</td>
- <td>{{ config }}</td>
- </tr>
- <tr>
- <td>{{ _("Download Folder:") }}</td>
- <td>{{ download }}</td>
- </tr>
- <tr>
- <td>{{ _("Free Space:") }}</td>
- <td>{{ freespace }}</td>
- </tr>
- <tr>
- <td>{{ _("Language:") }}</td>
- <td>{{ language }}</td>
- </tr>
- <tr>
- <td>{{ _("Webinterface Port:") }}</td>
- <td>{{ webif }}</td>
- </tr>
- <tr>
- <td>{{ _("Remote Interface Port:") }}</td>
- <td>{{ remote }}</td>
- </tr>
- </table>
-
-{% endblock %} \ No newline at end of file
diff --git a/module/web/templates/default/login.html b/module/web/templates/default/login.html
deleted file mode 100644
index 9e91ad309..000000000
--- a/module/web/templates/default/login.html
+++ /dev/null
@@ -1,36 +0,0 @@
-{% extends 'default/base.html' %}
-
-{% block title %}{{_("Login")}} - {{super()}} {% endblock %}
-
-{% block content %}
-
-<div class="centeralign">
-<form action="" method="post" accept-charset="utf-8" id="login">
- <div class="no">
- <input type="hidden" name="do" value="login" />
- <fieldset>
- <legend>Login</legend>
- <label>
- <span>{{_("Username")}}</span>
- <input type="text" size="20" name="username" />
- </label>
- <br />
- <label>
- <span>{{_("Password")}}</span>
- <input type="password" size="20" name="password" />
- </label>
- <br />
- <input type="submit" value="Login" class="button" />
- </fieldset>
- </div>
-</form>
-
-{% if errors %}
-<p>{{_("Your username and password didn't match. Please try again.")}}</p>
- {{ _("To reset your login data or add an user run:") }} <b> python pyLoadCore.py -u</b>
-{% endif %}
-
-</div>
-<br>
-
-{% endblock %}
diff --git a/module/web/templates/default/logout.html b/module/web/templates/default/logout.html
deleted file mode 100644
index d3f07472b..000000000
--- a/module/web/templates/default/logout.html
+++ /dev/null
@@ -1,9 +0,0 @@
-{% extends 'default/base.html' %}
-
-{% block head %}
-<meta http-equiv="refresh" content="3; url=/">
-{% endblock %}
-
-{% block content %}
-<p><b>{{_("You were successfully logged out.")}}</b></p>
-{% endblock %} \ No newline at end of file
diff --git a/module/web/templates/default/logs.html b/module/web/templates/default/logs.html
deleted file mode 100644
index d6288df0e..000000000
--- a/module/web/templates/default/logs.html
+++ /dev/null
@@ -1,41 +0,0 @@
-{% extends 'default/base.html' %}
-
-{% block title %}{{_("Logs")}} - {{super()}} {% endblock %}
-{% block subtitle %}{{_("Logs")}}{% endblock %}
-{% block head %}
-<link rel="stylesheet" type="text/css" href="/media/default/css/log.css"/>
-{% endblock %}
-
-{% block content %}
-<div style="clear: both;"></div>
-
-<div class="logpaginator"><a href="{{ "/logs/1" }}">&lt;&lt; {{_("Start")}}</a> <a href="{{ "/logs/" + iprev|string }}">&lt; {{_("prev")}}</a> <a href="{{ "/logs/" + inext|string }}">{{_("next")}} &gt;</a> <a href="/logs/">{{_("End")}} &gt;&gt;</a></div>
-<div class="logperpage">
- <form id="logform1" action="" method="POST">
- <label for="reversed">Reversed:</label>
- <input type="checkbox" name="reversed" onchange="this.form.submit();" {% if reversed %} checked="checked" {% endif %} />&nbsp;
- <label for="perpage">Lines per page:</label>
- <select name="perpage" onchange="this.form.submit();">
- {% for value in perpage_p %}
- <option value="{{value.0}}"{% if value.0 == perpage %} selected="selected" {% endif %}>{{value.1}}</option>
- {% endfor %}
- </select>
- </form>
-</div>
-<div class="logwarn">{{warning}}</div>
-<div style="clear: both;"></div>
-<div class="logdiv">
- <table class="logtable" cellpadding="0" cellspacing="0">
- {% for line in log %}
- <tr><td class="logline">{{line.line}}</td><td>{{line.date}}</td><td class="loglevel">{{line.level}}</td><td>{{line.message}}</td></tr>
- {% endfor %}
- </table>
-</div>
-<div class="logform">
-<form id="logform2" action="" method="POST">
- <label for="from">Jump to time:</label><input type="text" name="from" size="15" value="{{from}}"/>
- <input type="submit" value="ok" />
-</form>
-</div>
-<div style="clear: both; height: 10px;">&nbsp; </div>
-{% endblock %} \ No newline at end of file
diff --git a/module/web/templates/default/pathchooser.html b/module/web/templates/default/pathchooser.html
deleted file mode 100644
index d00637055..000000000
--- a/module/web/templates/default/pathchooser.html
+++ /dev/null
@@ -1,76 +0,0 @@
-<html>
-<head>
- <script class="javascript">
- function chosen()
- {
- opener.ifield.value = document.forms[0].p.value;
- close();
- }
- function exit()
- {
- close();
- }
- function setInvalid() {
- document.forms[0].send.disabled = 'disabled';
- document.forms[0].p.style.color = '#FF0000';
- }
- function setValid() {
- document.forms[0].send.disabled = '';
- document.forms[0].p.style.color = '#000000';
- }
- function setFile(file)
- {
- document.forms[0].p.value = file;
- setValid();
-
- }
- </script>
- <link rel="stylesheet" type="text/css" href="/media/default/css/pathchooser.css"/>
-</head>
-<body{% if type == 'file' %}{% if not oldfile %} onload="setInvalid();"{% endif %}{% endif %}>
-<center>
- <div id="paths">
- <form method="get" action="?" onSubmit="chosen();" onReset="exit();">
- <input type="text" name="p" value="{{ oldfile|default(cwd) }}" size="60" onfocus="setValid();">
- <input type="submit" value="Ok" name="send">
- </form>
-
- {% if type == 'folder' %}
- <span class="path_abs_rel">{{_("Path")}}: <a href="{{ "/pathchooser" + cwd|path_make_absolute|quotepath }}"{% if absolute %} style="text-decoration: underline;"{% endif %}>{{_("absolute")}}</a> | <a href="{{ "/pathchooser/" + cwd|path_make_relative|quotepath }}"{% if not absolute %} style="text-decoration: underline;"{% endif %}>{{_("relative")}}</a></span>
- {% else %}
- <span class="path_abs_rel">{{_("Path")}}: <a href="{{ "/filechooser/" + cwd|path_make_absolute|quotepath }}"{% if absolute %} style="text-decoration: underline;"{% endif %}>{{_("absolute")}}</a> | <a href="{{ "/filechooser/" + cwd|path_make_relative|quotepath }}"{% if not absolute %} style="text-decoration: underline;"{% endif %}>{{_("relative")}}</a></span>
- {% endif %}
- </div>
- <table border="0" cellspacing="0" cellpadding="3">
- <tr>
- <th>{{_("name")}}</th>
- <th>{{_("size")}}</th>
- <th>{{_("type")}}</th>
- <th>{{_("last modified")}}</th>
- </tr>
- {% if parentdir %}
- <tr>
- <td colspan="4">
- <a href="{% if type == 'folder' %}{{ "/pathchooser/" + parentdir|quotepath }}{% else %}{{ "/filechooser/" + parentdir|quotepath }}{% endif %}"><span class="parentdir">{{_("parent directory")}}</span></a>
- </td>
- </tr>
- {% endif %}
-{% for file in files %}
- <tr>
- {% if type == 'folder' %}
- <td class="name">{% if file.type == 'dir' %}<a href="{{ "/pathchooser/" + file.fullpath|quotepath }}" title="{{ file.fullpath }}"><span class="path_directory">{{ file.name|truncate(25) }}</span></a>{% else %}<span class="path_file" title="{{ file.fullpath }}">{{ file.name|truncate(25) }}{% endif %}</span></td>
- {% else %}
- <td class="name">{% if file.type == 'dir' %}<a href="{{ "/filechooser/" + file.fullpath|quotepath }}" title="{{ file.fullpath }}"><span class="file_directory">{{ file.name|truncate(25) }}</span></a>{% else %}<a href="#" onclick="setFile('{{ file.fullpath }}');" title="{{ file.fullpath }}"><span class="file_file">{{ file.name|truncate(25) }}{% endif %}</span></a></td>
- {% endif %}
- <td class="size">{{ file.size|float|filesizeformat }}</td>
- <td class="type">{% if file.type == 'dir' %}directory{% else %}{{ file.ext|default("file") }}{% endif %}</td>
- <td class="mtime">{{ file.modified|date("d.m.Y - H:i:s") }}</td>
- <tr>
-<!-- <tr>
- <td colspan="4">{{_("no content")}}</td>
- </tr> -->
-{% endfor %}
- </table>
- </center>
-</body>
-</html> \ No newline at end of file
diff --git a/module/web/templates/default/queue.html b/module/web/templates/default/queue.html
deleted file mode 100644
index 046abbe49..000000000
--- a/module/web/templates/default/queue.html
+++ /dev/null
@@ -1,104 +0,0 @@
-{% extends 'default/base.html' %}
-{% block head %}
-
-<script type="text/javascript" src="/media/js/package_ui.js"></script>
-
-<script type="text/javascript">
-
-document.addEvent("domready", function(){
- var pUI = new PackageUI("url", {{ target }});
-});
-</script>
-{% endblock %}
-
-{% if target %}
- {% set name = _("Queue") %}
-{% else %}
- {% set name = _("Collector") %}
-{% endif %}
-
-{% block title %}{{name}} - {{super()}} {% endblock %}
-{% block subtitle %}{{name}}{% endblock %}
-
-{% block pageactions %}
-<ul id="page-actions-more">
- <li id="del_finished"><a style="padding: 0; font-weight: bold;" href="#">{{_("Delete Finished")}}</a></li>
- <li id="restart_failed"><a style="padding: 0; font-weight: bold;" href="#">{{_("Restart Failed")}}</a></li>
-</ul>
-{% endblock %}
-
-{% block content %}
-{% autoescape true %}
-
-<ul id="package-list" style="list-style: none; padding-left: 0; margin-top: -10px;">
-{% for package in content %}
- <li>
-<div id="package_{{package.pid}}" class="package">
- <div class="order" style="display: none;">{{ package.order }}</div>
-
- <div class="packagename" style="cursor: pointer">
- <img class="package_drag" src="/media/default/img/folder.png" style="cursor: move; margin-bottom: -2px">
- <span class="name">{{package.name}}</span>
- &nbsp;&nbsp;
- <span class="buttons" style="opacity:0">
- <img title="{{_("Delete Package")}}" style="cursor: pointer" width="12px" height="12px" src="/media/default/img/delete.png" />
- &nbsp;&nbsp;
- <img title="{{_("Restart Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/media/default/img/arrow_refresh.png" />
- &nbsp;&nbsp;
- <img title="{{_("Edit Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/media/default/img/pencil.png" />
- &nbsp;&nbsp;
- <img title="{{_("Move Package")}}" style="margin-left: -10px; cursor: pointer" height="12px" src="/media/default/img/package_go.png" />
- </span>
- </div>
- {% set progress = (package.linksdone * 100) / package.linkstotal %}
-
- <div id="progress" style="border-radius: 4px; border: 1px solid #AAAAAA; width: 50%; height: 1em">
- <div style="width: {{ progress }}%; height: 100%; background-color: #add8e6;"></div>
- <label style="font-size: 0.8em; font-weight: bold; padding-left: 5px; position: relative; top: -17px">
- {{ package.sizedone|formatsize }} / {{ package.sizetotal|formatsize }}</label>
- <label style="font-size: 0.8em; font-weight: bold; padding-right: 5px ;float: right; position: relative; top: -17px">
- {{ package.linksdone }} / {{ package.linkstotal }}</label>
- </div>
- <div style="clear: both; margin-bottom: -10px"></div>
-
- <div id="children_{{package.pid}}" style="display: none;" class="children">
- <span class="child_secrow">{{_("Folder:")}} <span class="folder">{{package.folder}}</span> | {{_("Password:")}} <span class="password">{{package.password}}</span></span>
- <ul id="sort_children_{{package.pid}}" style="list-style: none; padding-left: 0">
- </ul>
- </div>
-</div>
- </li>
-{% endfor %}
-</ul>
-{% endautoescape %}
-{% endblock %}
-
-{% block hidden %}
-<div id="pack_box" class="window_box" style="z-index: 2">
- <form id="pack_form" action="/json/edit_package" method="POST" enctype="multipart/form-data">
- <h1>{{_("Edit Package")}}</h1>
- <p>{{_("Edit the package detais below.")}}</p>
- <input name="pack_id" id="pack_id" type="hidden" value=""/>
- <label for="pack_name">{{_("Name")}}
- <span class="small">{{_("The name of the package.")}}</span>
- </label>
- <input id="pack_name" name="pack_name" type="text" size="20" />
-
- <label for="pack_folder">{{_("Folder")}}
- <span class="small">{{_("Name of subfolder for these downloads.")}}</span>
- </label>
- <input id="pack_folder" name="pack_folder" type="text" size="20" />
-
- <label for="pack_pws">{{_("Password")}}
- <span class="small">{{_("List of passwords used for unrar.")}}</span>
- </label>
- <textarea rows="3" name="pack_pws" id="pack_pws"></textarea>
-
- <button type="submit">{{_("Submit")}}</button>
- <button id="pack_reset" style="margin-left: 0" type="reset" >{{_("Reset")}}</button>
- <div class="spacer"></div>
-
- </form>
-
-</div>
-{% endblock %} \ No newline at end of file
diff --git a/module/web/templates/default/settings.html b/module/web/templates/default/settings.html
deleted file mode 100644
index a4443025a..000000000
--- a/module/web/templates/default/settings.html
+++ /dev/null
@@ -1,204 +0,0 @@
-{% extends 'default/base.html' %}
-
-{% block title %}{{ _("Config") }} - {{ super() }} {% endblock %}
-{% block subtitle %}{{ _("Config") }}{% endblock %}
-
-{% block head %}
- <script type="text/javascript" src="/media/js/tinytab_static.js"></script>
- <script type="text/javascript" src="/media/js/MooDropMenu_static.js"></script>
- <script type="text/javascript" src="/media/js/settings.js"></script>
-
-{% endblock %}
-
-{% block content %}
-
- <ul id="toptabs" class="tabs">
- <li><a class="selected" href="#">{{ _("General") }}</a></li>
- <li><a href="#">{{ _("Plugins") }}</a></li>
- <li><a href="#">{{ _("Accounts") }}</a></li>
- </ul>
-
- <div id="tabsback" style="height: 20px; padding-left: 150px; color: white; font-weight: bold;">
-
- </div>
-
- <span id="tabs-body">
- <!-- General -->
- <span id="general" class="active tabContent">
- <ul class="nav tabs">
- <li class>
- <a>Menu</a>
- <ul id="general-menu">
- {% for entry,name in conf.general %}
- <nobr>
- <li id="general|{{ entry }}">{{ name }}</li>
- </nobr>
- <br>
- {% endfor %}
- </ul>
- </li>
- </ul>
-
- <form id="general_form" action="" method="POST" autocomplete="off">
- <span id="general_form_content">
- <br>
- <h3>&nbsp;&nbsp; {{ _("Choose a section from the menu") }}</h3>
- <br>
- </span>
-
- <input id="general|submit" class="styled_button" type="submit" value="{{_("Submit")}}"/>
- </form>
- </span>
-
- <!-- Plugins -->
- <span id="plugins" class="tabContent">
- <ul class="nav tabs">
- <li class>
- <a>Menu</a>
- <ul id="plugin-menu">
- {% for entry,name in conf.plugin %}
- <nobr>
- <li id="plugin|{{ entry }}">{{ name }}</li>
- </nobr>
- <br>
- {% endfor %}
- </ul>
- </li>
- </ul>
-
-
- <form id="plugin_form" action="" method="POST" autocomplete="off">
-
- <span id="plugin_form_content">
- <br>
- <h3>&nbsp;&nbsp; {{ _("Choose a section from the menu") }}</h3>
- <br>
- </span>
- <input id="plugin|submit" class="styled_button" type="submit" value="{{_("Submit")}}"/>
- </form>
-
- </span>
-
- <!-- Accounts -->
- <span id="accounts" class="tabContent">
- <form id="account_form" action="/json/update_accounts" method="POST">
-
- <table class="settable wide">
-
- <thead>
- <tr>
- <th>{{ _("Plugin") }}</th>
- <th>{{ _("Name") }}</th>
- <th>{{ _("Password") }}</th>
- <th>{{ _("Status") }}</th>
- <th>{{ _("Premium") }}</th>
- <th>{{ _("Valid until") }}</th>
- <th>{{ _("Traffic left") }}</th>
- <th>{{ _("Time") }}</th>
- <th>{{ _("Max Parallel") }}</th>
- <th>{{ _("Delete?") }}</th>
- </tr>
- </thead>
-
-
- {% for account in conf.accs %}
- {% set plugin = account.type %}
- <tr>
- <td>
- <span style="padding:5px">{{ plugin }}</span>
- </td>
-
- <td><label for="{{plugin}}|password;{{account.login}}"
- style="color:#424242;">{{ account.login }}</label></td>
- <td>
- <input id="{{plugin}}|password;{{account.login}}"
- name="{{plugin}}|password;{{account.login}}"
- type="password" value="{{account.password}}" size="12"/>
- </td>
- <td>
- {% if account.valid %}
- <span style="font-weight: bold; color: #006400;">
- {{ _("valid") }}
- {% else %}
- <span style="font-weight: bold; color: #8b0000;">
- {{ _("not valid") }}
- {% endif %}
- </span>
- </td>
- <td>
- {% if account.premium %}
- <span style="font-weight: bold; color: #006400;">
- {{ _("yes") }}
- {% else %}
- <span style="font-weight: bold; color: #8b0000;">
- {{ _("no") }}
- {% endif %}
- </span>
- </td>
- <td>
- <span style="font-weight: bold;">
- {{ account.validuntil }}
- </span>
- </td>
- <td>
- <span style="font-weight: bold;">
- {{ account.trafficleft }}
- </span>
- </td>
- <td>
- <input id="{{plugin}}|time;{{account.login}}"
- name="{{plugin}}|time;{{account.login}}" type="text"
- size="7" value="{{account.time}}"/>
- </td>
- <td>
- <input id="{{plugin}}|limitdl;{{account.login}}"
- name="{{plugin}}|limitdl;{{account.login}}" type="text"
- size="2" value="{{account.limitdl}}"/>
- </td>
- <td>
- <input id="{{plugin}}|delete;{{account.login}}"
- name="{{plugin}}|delete;{{account.login}}" type="checkbox"
- value="True"/>
- </td>
- </tr>
- {% endfor %}
- </table>
-
- <button id="account_submit" type="submit" class="styled_button">{{_("Submit")}}</button>
- <button id="account_add" style="margin-left: 0" type="submit" class="styled_button">{{_("Add")}}</button>
- </form>
- </span>
- </span>
-{% endblock %}
-{% block hidden %}
-<div id="account_box" class="window_box" style="z-index: 2">
-<form id="add_account_form" action="/json/add_account" method="POST" enctype="multipart/form-data">
-<h1>{{_("Add Account")}}</h1>
-<p>{{_("Enter your account data to use premium features.")}}</p>
-<label for="account_login">{{_("Login")}}
-<span class="small">{{_("Your username.")}}</span>
-</label>
-<input id="account_login" name="account_login" type="text" size="20" />
-
-<label for="account_password">{{_("Password")}}
-<span class="small">{{_("The password for this account.")}}</span>
-</label>
-<input id="account_password" name="account_password" type="password" size="20" />
-
-<label for="account_type">{{_("Type")}}
-<span class="small">{{_("Choose the hoster for your account.")}}</span>
-</label>
- <select name=account_type id="account_type">
- {% for type in types|sort %}
- <option value="{{ type }}">{{ type }}</option>
- {% endfor %}
- </select>
-
-<button id="account_add_button" type="submit">{{_("Add")}}</button>
-<button id="account_reset" style="margin-left: 0" type="reset">{{_("Reset")}}</button>
-<div class="spacer"></div>
-
-</form>
-
-</div>
-{% endblock %} \ No newline at end of file
diff --git a/module/web/templates/default/settings_item.html b/module/web/templates/default/settings_item.html
deleted file mode 100644
index 813383343..000000000
--- a/module/web/templates/default/settings_item.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<table class="settable">
- {% if section.outline %}
- <tr><th colspan="2">{{ section.outline }}</th></tr>
- {% endif %}
- {% for okey, option in section.iteritems() %}
- {% if okey not in ("desc","outline") %}
- <tr>
- <td><label for="{{skey}}|{{okey}}"
- style="color:#424242;">{{ option.desc }}:</label></td>
- <td>
- {% if option.type == "bool" %}
- <select id="{{skey}}|{{okey}}" name="{{skey}}|{{okey}}">
- <option {% if option.value %} selected="selected"
- {% endif %}value="True">{{ _("on") }}</option>
- <option {% if not option.value %} selected="selected"
- {% endif %}value="False">{{ _("off") }}</option>
- </select>
- {% elif ";" in option.type %}
- <select id="{{skey}}|{{okey}}" name="{{skey}}|{{okey}}">
- {% for entry in option.list %}
- <option {% if option.value == entry %}
- selected="selected" {% endif %}>{{ entry }}</option>
- {% endfor %}
- </select>
- {% elif option.type == "folder" %}
- <input name="{{skey}}|{{okey}}" type="text"
- id="{{skey}}|{{okey}}" value="{{option.value}}"/>
- <input name="browsebutton" type="button"
- onclick="ifield = document.getElementById('{{skey}}|{{okey}}'); pathchooser = window.open('{% if option.value %}{{ "/pathchooser/" + option.value|quotepath }}{% else %}{{ pathroot }}{% endif %}', 'pathchooser', 'scrollbars=yes,toolbar=no,menubar=no,statusbar=no,width=650,height=300'); pathchooser.ifield = ifield; window.ifield = ifield;"
- value="{{_("Browse")}}"/>
- {% elif option.type == "file" %}
- <input name="{{skey}}|{{okey}}" type="text"
- id="{{skey}}|{{okey}}" value="{{option.value}}"/>
- <input name="browsebutton" type="button"
- onclick="ifield = document.getElementById('{{skey}}|{{okey}}'); filechooser = window.open('{% if option.value %}{{ "/filechooser/" + option.value|quotepath }}{% else %}{{ fileroot }}{% endif %}', 'filechooser', 'scrollbars=yes,toolbar=no,menubar=no,statusbar=no,width=650,height=300'); filechooser.ifield = ifield; window.ifield = ifield;"
- value="{{_("Browse")}}"/>
- {% elif option.type == "password" %}
- <input id="{{skey}}|{{okey}}" name="{{skey}}|{{okey}}"
- type="password" value="{{option.value}}"/>
- {% else %}
- <input id="{{skey}}|{{okey}}" name="{{skey}}|{{okey}}"
- type="text" value="{{option.value}}"/>
- {% endif %}
- </td>
- </tr>
- {% endif %}
- {% endfor %}
-</table> \ No newline at end of file
diff --git a/module/web/templates/default/setup.html b/module/web/templates/default/setup.html
deleted file mode 100644
index 39ef6f1e8..000000000
--- a/module/web/templates/default/setup.html
+++ /dev/null
@@ -1,13 +0,0 @@
-{% extends 'default/base.html' %}
-
-{% block title %}{{ _("Setup") }} - {{ super() }} {% endblock %}
-{% block subtitle %}{{ _("Setup") }}{% endblock %}
-{% block headpanel %}Welcome to pyLoad{% endblock %}
-{% block menu %}
- <li style="height: 25px"> <!-- Needed to get enough margin -->
- </li>
-{% endblock %}
-
-{% block content %}
- Comming Soon.
-{% endblock %} \ No newline at end of file
diff --git a/module/web/templates/default/window.html b/module/web/templates/default/window.html
deleted file mode 100644
index a11323fe0..000000000
--- a/module/web/templates/default/window.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<iframe id="upload_target" name="upload_target" src="" style="display: none; width:0;height:0"></iframe>
-
-<div id="add_box" class="window_box">
-<form id="add_form" action="/json/add_package" method="POST" enctype="multipart/form-data">
-<h1>{{_("Add Package")}}</h1>
-<p>{{_("Paste your links or upload a container.")}}</p>
-<label for="add_name">{{_("Name")}}
-<span class="small">{{_("The name of the new package.")}}</span>
-</label>
-<input id="add_name" name="add_name" type="text" size="20" />
-
-<label for="add_links">{{_("Links")}}
-<span class="small">{{_("Paste your links here or any text and press the filter button.")}}</span>
-<span class="small"> {{ _("Filter urls") }}
-<img alt="URIParsing" Title="Parse Uri" src="/media/default/img/parseUri.png" style="cursor:pointer; vertical-align: text-bottom;" onclick="parseUri()"/>
-</span>
-
-</label>
-<textarea rows="5" name="add_links" id="add_links"></textarea>
-
-<label for="add_password">{{_("Password")}}
- <span class="small">{{_("Password for RAR-Archive")}}</span>
-</label>
-<input id="add_password" name="add_password" type="text" size="20">
-
-<label>{{_("File")}}
-<span class="small">{{_("Upload a container.")}}</span>
-</label>
-<input type="file" name="add_file" id="add_file"/>
-
-<label for="add_dest">{{_("Destination")}}
-</label>
-<span class="cont">
- {{_("Queue")}}
- <input type="radio" name="add_dest" id="add_dest" value="1" checked="checked"/>
- {{_("Collector")}}
- <input type="radio" name="add_dest" id="add_dest2" value="0"/>
-</span>
-
-<button type="submit">{{_("Add Package")}}</button>
-<button id="add_reset" style="margin-left:0;" type="reset">{{_("Reset")}}</button>
-<div class="spacer"></div>
-
-</form>
-
-</div> \ No newline at end of file
diff --git a/module/web/utils.py b/module/web/utils.py
deleted file mode 100644
index a89c87558..000000000
--- a/module/web/utils.py
+++ /dev/null
@@ -1,137 +0,0 @@
-#!/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 plrogram; if not, see <http://www.gnu.org/licenses/>.
-
- @author: RaNaN
-"""
-from bottle import request, HTTPError, redirect, ServerAdapter
-
-from webinterface import env, TEMPLATE
-
-from module.Api import has_permission, PERMS, ROLE
-
-def render_to_response(name, args={}, proc=[]):
- for p in proc:
- args.update(p())
-
- t = env.get_template(TEMPLATE + "/" + name)
- return t.render(**args)
-
-
-def parse_permissions(session):
- perms = dict([(x, False) for x in dir(PERMS) if not x.startswith("_")])
- perms["ADMIN"] = False
- perms["is_admin"] = False
-
- if not session.get("authenticated", False):
- return perms
-
- if session.get("role") == ROLE.ADMIN:
- for k in perms.iterkeys():
- perms[k] = True
-
- elif session.get("perms"):
- p = session.get("perms")
- get_permission(perms, p)
-
- return perms
-
-
-def permlist():
- return [x for x in dir(PERMS) if not x.startswith("_") and x != "ALL"]
-
-
-def get_permission(perms, p):
- """Returns a dict with permission key
-
- :param perms: dictionary
- :param p: bits
- """
- for name in permlist():
- perms[name] = has_permission(p, getattr(PERMS, name))
-
-
-def set_permission(perms):
- """generates permission bits from dictionary
-
- :param perms: dict
- """
- permission = 0
- for name in dir(PERMS):
- if name.startswith("_"): continue
-
- if name in perms and perms[name]:
- permission |= getattr(PERMS, name)
-
- return permission
-
-
-def set_session(request, info):
- s = request.environ.get('beaker.session')
- s["authenticated"] = True
- s["user_id"] = info["id"]
- s["name"] = info["name"]
- s["role"] = info["role"]
- s["perms"] = info["permission"]
- s["template"] = info["template"]
- s.save()
-
- return s
-
-
-def parse_userdata(session):
- return {"name": session.get("name", "Anonymous"),
- "is_admin": True if session.get("role", 1) == 0 else False,
- "is_authenticated": session.get("authenticated", False)}
-
-
-def login_required(perm=None):
- def _dec(func):
- def _view(*args, **kwargs):
- s = request.environ.get('beaker.session')
- if s.get("name", None) and s.get("authenticated", False):
- if perm:
- perms = parse_permissions(s)
- if perm not in perms or not perms[perm]:
- if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
- return HTTPError(403, "Forbidden")
- else:
- return redirect("/nopermission")
-
- return func(*args, **kwargs)
- else:
- if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
- return HTTPError(403, "Forbidden")
- else:
- return redirect("/login")
-
- return _view
-
- return _dec
-
-
-def toDict(obj):
- ret = {}
- for att in obj.__slots__:
- ret[att] = getattr(obj, att)
- return ret
-
-
-class CherryPyWSGI(ServerAdapter):
- def run(self, handler):
- from wsgiserver import CherryPyWSGIServer
-
- server = CherryPyWSGIServer((self.host, self.port), handler)
- server.start()
diff --git a/module/web/webinterface.py b/module/web/webinterface.py
deleted file mode 100644
index ec8b2e56c..000000000
--- a/module/web/webinterface.py
+++ /dev/null
@@ -1,157 +0,0 @@
-#!/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
-"""
-
-import sys
-import module.common.pylgettext as gettext
-
-import os
-from os.path import join, abspath, dirname, exists
-from os import makedirs
-
-PROJECT_DIR = abspath(dirname(__file__))
-PYLOAD_DIR = abspath(join(PROJECT_DIR, "..", ".."))
-
-sys.path.append(PYLOAD_DIR)
-
-from module import InitHomeDir
-from module.utils import decode, formatSize
-
-import bottle
-from bottle import run, app
-
-from jinja2 import Environment, FileSystemLoader, PrefixLoader, FileSystemBytecodeCache
-from middlewares import StripPathMiddleware, GZipMiddleWare, PrefixMiddleware
-
-SETUP = None
-PYLOAD = None
-
-from module.web import ServerThread
-
-if not ServerThread.core:
- if ServerThread.setup:
- SETUP = ServerThread.setup
- config = SETUP.config
- else:
- raise Exception("Could not access pyLoad Core")
-else:
- PYLOAD = ServerThread.core.api
- config = ServerThread.core.config
-
-from module.common.JsEngine import JsEngine
-
-JS = JsEngine()
-
-TEMPLATE = config.get('webinterface', 'template')
-DL_ROOT = config.get('general', 'download_folder')
-LOG_ROOT = config.get('log', 'log_folder')
-PREFIX = config.get('webinterface', 'prefix')
-
-if PREFIX:
- PREFIX = PREFIX.rstrip("/")
- if not PREFIX.startswith("/"):
- PREFIX = "/" + PREFIX
-
-DEBUG = config.get("general", "debug_mode") or "-d" in sys.argv or "--debug" in sys.argv
-bottle.debug(DEBUG)
-
-cache = join("tmp", "jinja_cache")
-if not exists(cache):
- makedirs(cache)
-
-bcc = FileSystemBytecodeCache(cache, '%s.cache')
-loader = PrefixLoader({
- "default": FileSystemLoader(join(PROJECT_DIR, "templates", "default")),
- 'js': FileSystemLoader(join(PROJECT_DIR, 'media', 'js'))
-})
-
-env = Environment(loader=loader, extensions=['jinja2.ext.i18n', 'jinja2.ext.autoescape'], trim_blocks=True, auto_reload=False,
- bytecode_cache=bcc)
-
-from filters import quotepath, path_make_relative, path_make_absolute, truncate, date
-
-env.filters["quotepath"] = quotepath
-env.filters["truncate"] = truncate
-env.filters["date"] = date
-env.filters["path_make_relative"] = path_make_relative
-env.filters["path_make_absolute"] = path_make_absolute
-env.filters["decode"] = decode
-env.filters["type"] = lambda x: str(type(x))
-env.filters["formatsize"] = formatSize
-env.filters["getitem"] = lambda x, y: x.__getitem__(y)
-if PREFIX:
- env.filters["url"] = lambda x: x
-else:
- env.filters["url"] = lambda x: PREFIX + x if x.startswith("/") else x
-
-gettext.setpaths([join(os.sep, "usr", "share", "pyload", "locale"), None])
-translation = gettext.translation("django", join(PYLOAD_DIR, "locale"),
- languages=[config.get("general", "language"), "en"],fallback=True)
-translation.install(True)
-env.install_gettext_translations(translation)
-
-from beaker.middleware import SessionMiddleware
-
-session_opts = {
- 'session.type': 'file',
- 'session.cookie_expires': False,
- 'session.data_dir': './tmp',
- 'session.auto': False
-}
-
-web = StripPathMiddleware(SessionMiddleware(app(), session_opts))
-web = GZipMiddleWare(web)
-
-if PREFIX:
- web = PrefixMiddleware(web, prefix=PREFIX)
-
-import pyload_app
-import json_app
-import cnl_app
-import api_app
-
-def run_simple(host="0.0.0.0", port="8000"):
- run(app=web, host=host, port=port, quiet=True)
-
-
-def run_lightweight(host="0.0.0.0", port="8000"):
- run(app=web, host=host, port=port, quiet=True, server="bjoern")
-
-
-def run_threaded(host="0.0.0.0", port="8000", theads=3, cert="", key=""):
- from wsgiserver import CherryPyWSGIServer
-
- if cert and key:
- CherryPyWSGIServer.ssl_certificate = cert
- CherryPyWSGIServer.ssl_private_key = key
-
- CherryPyWSGIServer.numthreads = theads
-
- from utils import CherryPyWSGI
-
- run(app=web, host=host, port=port, server=CherryPyWSGI, quiet=True)
-
-
-def run_fcgi(host="0.0.0.0", port="8000"):
- from bottle import FlupFCGIServer
-
- run(app=web, host=host, port=port, server=FlupFCGIServer, quiet=True)
-
-
-if __name__ == "__main__":
- run(app=web, port=8001)