-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathtracker.py
More file actions
134 lines (118 loc) · 5.08 KB
/
tracker.py
File metadata and controls
134 lines (118 loc) · 5.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# -*- coding: utf-8 -*-
import contextlib
import re
import os
import time
from http.cookiejar import MozillaCookieJar
import requests
import appdirs
from . import __version__ as version, __title__ as title
from .config import config
from .logging import log
config.register('Tracker', 'announce_url',
"Please enter your personal announce URL")
config.register('Tracker', 'username', "Username", ask=True)
config.register('Tracker', 'password', "Password", ask=True, getpass=True)
config.register('Tracker', 'domain',
"Please enter the tracker's domain, e.g. 'mydomain.net'")
class TrackerException(Exception):
pass
class Tracker():
headers = {'User-Agent': '{}/{}'.format(title, version)}
def _login(self, session, _tries=0):
maxtries = 30
domain = config.get('Tracker', 'domain')
login_url = "https://{}/login.php".format(domain)
payload = {'username': config.get('Tracker', 'username'),
'password': config.get('Tracker', 'password'),
'keeplogged': "1",
'login': "Log in!"}
resp = session.post(login_url, data=payload)
resp.raise_for_status()
if 'href="login.php"' in resp.text:
if 'id="loginform"' in resp.text:
raise TrackerException("Login failed (wrong credentials?)")
elif 'You are banned' in resp.text:
raise TrackerException(
"Login failed (login attempts exceeded)")
else:
# We encountered the login bug that sends you to "/" (which
# doesn't contain the login form) without logging you in
# todo: convert this to a retry decorator
if _tries < maxtries:
backoff = min(2**_tries/100, 5.)
log.info('Encountered login bug; trying again after '
'{}s back-off'.format(backoff))
time.sleep(backoff)
self._login(session, _tries=_tries+1)
else:
log.notice('Encountered login bug; '
'giving up after '
'{} login attempts'.format(_tries))
raise TrackerException("Login failed (server login bug)")
elif 'logout.php' in resp.text:
# Login successful, find and remember logout URL
match = re.search(r"logout\.php\?auth=[0-9a-f]{32}", resp.text)
if match:
self._logout_url = "https://{}/{}".format(
domain, match.group(0))
else:
raise TrackerException("Couldn't find logout URL")
else:
log.error(resp.text)
raise TrackerException("Couldn't determine login status from HTML")
def _logout(self, session):
logout_url = getattr(self, '_logout_url')
if logout_url:
delattr(self, '_logout_url')
resp = session.get(logout_url)
if 'logout.php' in resp.text:
raise TrackerException("Logout failed")
else:
raise TrackerException("No logout URL: Unable to logout")
@contextlib.contextmanager
def login(self):
log.notice("Logging in {} to {}",
config.get('Tracker', 'username'),
config.get('Tracker', 'domain'))
cj_path = os.path.join(appdirs.user_cache_dir(title.lower()),
'tracker_cookies.txt')
with requests.Session() as s:
s.cookies = MozillaCookieJar(cj_path)
try:
s.cookies.load()
except FileNotFoundError:
s.cookies.save()
s.headers.update(self.headers)
self._login(s)
yield s
self._logout(s)
s.cookies.save()
log.notice("Logged out {} of ",
config.get('Tracker', 'username'),
config.get('Tracker', 'domain'))
def upload(self, **kwargs):
url = "https://{}/upload.php".format(config.get('Tracker', 'domain'))
with self.login() as session:
log.notice("Posting submission")
resp = session.post(url, **kwargs)
resp.raise_for_status()
# TODO: Catch this somehow:
# <p style="color: red;text-align:center;">You must enter at least
# one tag. Maximum length is 200 characters.</p>
if resp.history:
# todo: check if url is good, might have been logged out
# (unlikely)
return resp.url
else:
log.error('Response: %s' % resp)
err_match = re.search(r''.join(
(r'(No torrent file uploaded.*?)',
re.escape(r'</p>'))),
resp.text)
if err_match:
log.error('Error: %s' % err_match.group(1))
else:
log.debug(resp.text)
log.error('Unknown error')
raise TrackerException('Failed to upload submission')