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