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