1
# Miro - an RSS based video player application
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
# In addition, as a special exception, the copyright holders give
19
# permission to link the code of portions of this program with the OpenSSL
22
# You must obey the GNU General Public License in all respects for all of
23
# the code used other than OpenSSL. If you modify file(s) with this
24
# exception, you may extend this exception to your version of the file(s),
25
# but you are not obligated to do so. If you do not wish to do so, delete
26
# this exception statement from your version. If you delete this exception
27
# statement from all source files in the program, then also delete it here.
31
from miro import config
32
from miro import dialogs
33
from miro import prefs
35
from miro.httpauthtools import HTTPPasswordList, decode_auth_header
37
def find_http_auth(url, auth_header=None):
38
"""Find an HTTPAuthPassword object stored in the passwords database.
40
This method searches the database for already entered passwords. It
41
will find a string to use for the Authorization header or None.
43
:param url: request URL
44
:param auth_header: optional www-authenticate header to use to search for.
45
This allows us to better match basic auth passwords
48
if auth_header is not None:
49
auth_scheme, realm, domain = decode_auth_header(auth_header)
52
return password_list.find(url, realm)
54
class CallbackTracker(object):
55
"""Used internally to track callbacks for dialogs for ask_for_http_auth().
57
This class allows us to only pop up one dialog for each auth request.
60
self.callback_map = {}
62
def has_callback(self, url, realm):
64
return key in self.callback_map
66
def add_callback(self, callback, url, realm):
68
callbacks = self.callback_map.setdefault(key, [])
69
callbacks.append(callback)
71
def run_callbacks(self, url, realm, username, password, auth_header):
73
auth = password_list.add(username, password, url, auth_header)
74
for callback in self.callback_map[key]:
76
del self.callback_map[key]
78
def run_canceled_callbacks(self, url, realm):
80
for callback in self.callback_map[key]:
82
del self.callback_map[key]
84
def ask_for_http_auth(callback, url, auth_header, location):
85
"""Ask the user for a username and password to login to a site.
87
:param callback: will be called with a HTTPAuthPassword to use, or None
88
:param url: URL for the request
89
:param auth_header: www-authenticate header we got from the server
90
:param location: human readable text of what's requesting authorization
94
auth_scheme, realm, domain = decode_auth_header(auth_header)
96
def handleLoginResponse(dialog):
97
if dialog.choice == dialogs.BUTTON_OK:
98
callback_tracker.run_callbacks(url, realm, dialog.username,
99
dialog.password, auth_header)
101
callback_tracker.run_canceled_callbacks(url, realm)
103
run_dialog = (not callback_tracker.has_callback(url, realm))
104
callback_tracker.add_callback(callback, url, realm)
106
dialogs.HTTPAuthDialog(location, realm).run(handleLoginResponse)
110
password_list.remove(auth)
112
def remove_by_url_and_realm(url, realm):
114
auth = password_list.find(url, realm)
116
password_list.remove(auth)
119
callback_tracker = None
123
global callback_tracker
124
password_list = HTTPPasswordList()
125
callback_tracker = CallbackTracker()
127
def _default_password_file():
128
support_dir = config.get(prefs.SUPPORT_DIRECTORY)
129
return os.path.join(support_dir, 'httpauth')
131
def restore_from_file(path=None):
133
path = _default_password_file()
134
password_list.restore_from_file(path)
136
def write_to_file(path=None):
138
path = _default_password_file()
139
password_list.write_to_file(path)
142
"""Get the current list of all HTTPAuthPassword objects."""
144
return password_list.passwords
146
def add_change_callback(callback):
147
"""Register to get changes to the list of HTTPAuthPassword objects
149
The callback will be called with the entire list of passwords whenever it
152
:returns: a callback handle that can be passed to remove_change_callback
155
def callback_wrapper(obj, passwords):
157
return password_list.connect("passwords-updated", callback_wrapper)
159
def remove_change_callback(callback_handle):
161
password_list.disconnect(callback_handle)