Updated docs
[diaspy.git] / diaspy / connection.py
CommitLineData
a29d3526
MM
1#!/usr/bin/env python
2
36f274c0
MM
3
4"""This module abstracts connection to pod.
5"""
6
7
b74513c5 8import json
8993810c
MM
9import re
10import requests
ff3d2ab4 11import warnings
8993810c 12
2cf8467c
MM
13from diaspy import errors
14
a29d3526 15
36f274c0 16DEBUG = True
178faa46
MM
17
18
a29d3526 19class Connection():
1cff2093 20 """Object representing connection with the pod.
a29d3526 21 """
27a28aaf 22 _token_regex = re.compile(r'content="(.*?)"\s+name="csrf-token')
39af9756
MM
23 _userinfo_regex = re.compile(r'window.current_user_attributes = ({.*})')
24 # this is for older version of D*
25 _userinfo_regex_2 = re.compile(r'gon.user=({.*});gon.preloads')
27a28aaf 26
36f274c0 27 def __init__(self, pod, username, password, schema='https'):
8993810c
MM
28 """
29 :param pod: The complete url of the diaspora pod to use.
30 :type pod: str
31 :param username: The username used to log in.
32 :type username: str
33 :param password: The password used to log in.
34 :type password: str
35 """
a29d3526 36 self.pod = pod
2cf8467c 37 self._session = requests.Session()
36f274c0 38 self._login_data = {'user[remember_me]': 1, 'utf8': '✓'}
5de52803 39 self._userdata = {}
2cf8467c 40 self._token = ''
5de52803 41 self._diaspora_session = ''
36f274c0 42 self._cookies = self._fetchcookies()
2cf8467c 43 try:
36f274c0
MM
44 #self._setlogin(username, password)
45 self._login_data = {'user[username]': username,
46 'user[password]': password,
47 'authenticity_token': self._fetchtoken()}
48 success = True
75a456f4
MM
49 except requests.exceptions.MissingSchema:
50 self.pod = '{0}://{1}'.format(schema, self.pod)
51 warnings.warn('schema was missing')
36f274c0 52 success = False
2cf8467c
MM
53 finally:
54 pass
55 try:
36f274c0
MM
56 if not success:
57 self._login_data = {'user[username]': username,
58 'user[password]': password,
59 'authenticity_token': self._fetchtoken()}
2cf8467c
MM
60 except Exception as e:
61 raise errors.LoginError('cannot create login data (caused by: {0})'.format(e))
8993810c 62
36f274c0
MM
63 def _fetchcookies(self):
64 request = self.get('stream')
65 return request.cookies
66
d4bd92cd
MM
67 def __repr__(self):
68 """Returns token string.
69 It will be easier to change backend if programs will just use:
70 repr(connection)
71 instead of calling a specified method.
72 """
cf6a800f 73 return self._fetchtoken()
d4bd92cd 74
36f274c0 75 def get(self, string, headers={}, params={}, direct=False, **kwargs):
8993810c
MM
76 """This method gets data from session.
77 Performs additional checks if needed.
78
79 Example:
7a31b4aa 80 To obtain 'foo' from pod one should call `get('foo')`.
8993810c
MM
81
82 :param string: URL to get without the pod's URL and slash eg. 'stream'.
83 :type string: str
73a9e0d3
MM
84 :param direct: if passed as True it will not be expanded
85 :type direct: bool
8993810c 86 """
73a9e0d3
MM
87 if not direct: url = '{0}/{1}'.format(self.pod, string)
88 else: url = string
36f274c0 89 return self._session.get(url, params=params, headers=headers, **kwargs)
8993810c 90
36f274c0 91 def post(self, string, data, headers={}, params={}, **kwargs):
8993810c
MM
92 """This method posts data to session.
93 Performs additional checks if needed.
94
95 Example:
7a31b4aa 96 To post to 'foo' one should call `post('foo', data={})`.
8993810c
MM
97
98 :param string: URL to post without the pod's URL and slash eg. 'status_messages'.
99 :type string: str
100 :param data: Data to post.
101 :param headers: Headers (optional).
102 :type headers: dict
103 :param params: Parameters (optional).
104 :type params: dict
105 """
106 string = '{0}/{1}'.format(self.pod, string)
36f274c0 107 request = self._session.post(string, data, headers=headers, params=params, **kwargs)
8993810c
MM
108 return request
109
36f274c0 110 def put(self, string, data=None, headers={}, params={}, **kwargs):
178faa46
MM
111 """This method PUTs to session.
112 """
113 string = '{0}/{1}'.format(self.pod, string)
36f274c0
MM
114 if data is not None: request = self._session.put(string, data, headers=headers, params=params, **kwargs)
115 else: request = self._session.put(string, headers=headers, params=params, **kwargs)
178faa46
MM
116 return request
117
36f274c0 118 def delete(self, string, data, headers={}, **kwargs):
8993810c
MM
119 """This method lets you send delete request to session.
120 Performs additional checks if needed.
121
122 :param string: URL to use.
123 :type string: str
124 :param data: Data to use.
125 :param headers: Headers to use (optional).
126 :type headers: dict
127 """
128 string = '{0}/{1}'.format(self.pod, string)
36f274c0 129 request = self._session.delete(string, data=data, headers=headers, **kwargs)
8993810c
MM
130 return request
131
132 def _setlogin(self, username, password):
133 """This function is used to set data for login.
b0b4c46d 134
8993810c
MM
135 .. note::
136 It should be called before _login() function.
137 """
2cf8467c
MM
138 self._login_data = {'user[username]': username,
139 'user[password]': password,
140 'authenticity_token': self._fetchtoken()}
8993810c 141
385e7ebe
MM
142 def _login(self):
143 """Handles actual login request.
144 Raises LoginError if login failed.
145 """
146 request = self.post('users/sign_in',
36f274c0
MM
147 data=self._login_data,
148 allow_redirects=False)
149 if request.status_code != 302:
2cf8467c 150 raise errors.LoginError('{0}: login failed'.format(request.status_code))
385e7ebe 151
36f274c0 152 def login(self, remember_me=1):
385e7ebe
MM
153 """This function is used to log in to a pod.
154 Will raise LoginError if password or username was not specified.
8993810c 155 """
2cf8467c
MM
156 if not self._login_data['user[username]'] or not self._login_data['user[password]']:
157 raise errors.LoginError('username and/or password is not specified')
36f274c0
MM
158 self._login_data['user[remember_me]'] = remember_me
159 status = self._login()
2cf8467c 160 self._login_data = {}
f3b7fd44 161 return self
385e7ebe 162
63cc182d
MM
163 def logout(self):
164 """Logs out from a pod.
165 When logged out you can't do anything.
166 """
167 self.get('users/sign_out')
b0b4c46d 168 self.token = ''
63cc182d 169
2cf8467c 170 def podswitch(self, pod, username, password):
7a31b4aa 171 """Switches pod from current to another one.
385e7ebe
MM
172 """
173 self.pod = pod
2cf8467c 174 self._setlogin(username, password)
385e7ebe 175 self._login()
8993810c 176
b0b4c46d
MM
177 def _fetchtoken(self):
178 """This method tries to get token string needed for authentication on D*.
179
180 :returns: token string
181 """
182 request = self.get('stream')
183 token = self._token_regex.search(request.text).group(1)
2cf8467c 184 self._token = token
b0b4c46d 185 return token
2ec93347 186
2b6b487e 187 def get_token(self, fetch=True):
385e7ebe 188 """This function returns a token needed for authentication in most cases.
2cf8467c 189 **Notice:** using repr() is recommended method for getting token.
a29d3526 190
b0b4c46d
MM
191 Each time it is run a _fetchtoken() is called and refreshed token is stored.
192
193 It is more safe to use than _fetchtoken().
ff3d2ab4 194 By setting new you can request new token or decide to get stored one.
2b6b487e 195 If no token is stored new one will be fetched anyway.
a29d3526 196
8993810c
MM
197 :returns: string -- token used to authenticate
198 """
b0b4c46d 199 try:
36f274c0 200 if fetch or not self._token: self._fetchtoken()
b0b4c46d 201 except requests.exceptions.ConnectionError as e:
ff3d2ab4 202 warnings.warn('{0} was cought: reusing old token'.format(e))
b0b4c46d 203 finally:
2cf8467c
MM
204 if not self._token: raise errors.TokenError('cannot obtain token and no previous token found for reuse')
205 return self._token
5de52803
MM
206
207 def getSessionToken(self):
208 """Returns session token string (_diaspora_session).
209 """
210 return self._diaspora_session
39af9756
MM
211
212 def getUserData(self):
213 """Returns user data.
214 """
215 request = self.get('bookmarklet')
216 userdata = self._userinfo_regex.search(request.text)
217 if userdata is None: userdata = self._userinfo_regex_2.search(request.text)
218 if userdata is None: raise errors.DiaspyError('cannot find user data')
219 userdata = userdata.group(1)
220 return json.loads(userdata)