1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """
21 Checks for updates on the internet
22 """
23
24 from urllib import quote
25
26 from elisa.core import common
27 from elisa.core.utils import defer, misc
28 from elisa.core.utils.internet import get_page
29 from elisa.core.log import Loggable
30 from elisa.core.plugin import Plugin
31 from elisa.core.components.message import Message
32
33 from twisted.internet import reactor, error, task
34
35 from distutils.version import LooseVersion
36
37
38 """
39 the url used for lookup
40 """
41 HOST = "www.moovida.com"
42 UPDATES_URL = "http://" + HOST + "/updates/updates/?" \
43 "install_date=%(date)s&version=%(version)s&os=%(os)s" \
44 "&user_id=%(user_id)s&aen=%(aen)s&entity_id=%(entity_id)s" \
45 "&referrer=%(referrer)s&traffic_unit=%(traffic_unit)s"
46
47
50
51
53
54 """
55 Message sent when plugins updates and/or new recommended plugins are
56 available for download from the plugin repository.
57
58 @ivar available_updates: the list of plugins available for update
59 @type available_updates: C{list} of C{dict}
60 @ivar new_recommended: the list of new recommended plugins available
61 @type new_recommended: C{list} of C{dict}
62 """
63
64 - def __init__(self, available_updates, new_recommended):
68
69
71
72 """
73 Helper Class for simple look up of updates on the remote elisa server.
74 """
75
76
77 check_interval = 86400
78
79 - def __init__(self, install_date, user_id, version, **extra_affiliation_params):
91
93 """
94 Parse the given data into a dictionary. The syntax for result has to be
95 a key-value pair per line separated by a colon (':'). Spaces at the
96 beginning or the end are stripped from both the key and the value.
97 For instance::
98
99 foo: bar
100 test: partial
101 maybe : maybe not
102
103 @param result: the result to parse
104 @type result: C{str}
105
106 @return: a dictionary containing the key-value pairs
107 @rtype: C{dict}
108 """
109 result_dict = {}
110 for line in result.splitlines():
111 try:
112 key, value = line.split(':', 1)
113 except ValueError:
114 self.warning("Could not split '%s'" % line)
115 continue
116
117 result_dict[key.strip()] = value.strip()
118
119 return result_dict
120
121 - def _get(self, uri):
125
127 """
128 Request for update data and parse it
129 """
130 request_params = {'user_id': quote(self.user_id),
131 'version': quote(self.version),
132 'date': quote(self.install_date),
133 'os': quote(self.operating_system)}
134 request_params.update(self.extra_affiliation_params)
135
136 request_url = UPDATES_URL % request_params
137
138 dfr = self._get(request_url)
139 dfr.addCallback(self.parse_result)
140 return dfr
141
142 - def start(self, callback):
143 """
144 Sets up an automatic loop of update url calls starting right now.
145 Everytime a result is received the callback is triggered with a
146 dictionary of the parsed result as argument. The next iteration is done
147 every L{check_interval}-seconds.
148
149 @raises AlreadyRunning: if the method was already called before. It is only
150 allowed to call this method once. It is mandatory to call L{stop} before
151 calling start again.
152 """
153 if self._pending_call is not None or self._current_dfr is not None:
154 raise AlreadyRunning("Don't call me twice")
155
156 self._auto_request(callback)
157
159 if not result:
160 self.warning("response of the Server was empty!")
161 return
162
163 dfr = callback(result)
164 return dfr
165
173
197
198 def iterate_plugins(plugins, installed,
199 available_updates, new_recommended):
200 for plugin_dict in plugins:
201 plugin = Plugin.from_dict(plugin_dict)
202 if plugin.name not in installed and \
203 plugin_dict['quality'] == 'recommended' and \
204 plugin.runs_on_current_platform():
205 new_recommended.append(plugin_dict)
206 elif plugin.name in installed:
207 current_version = \
208 get_current_plugin_version(plugin.name)
209 if plugin.version > current_version:
210 available_updates.append(plugin_dict)
211 yield None
212
213 def send_message(result, available_updates, new_recommended):
214 if len(available_updates) > 0 or len(new_recommended) > 0:
215 msg = AvailablePluginUpdatesMessage(available_updates,
216 new_recommended)
217 common.application.bus.send_message(msg)
218
219 updates_dfr = task.coiterate(iterate_plugins(plugins, installed,
220 available_updates,
221 new_recommended))
222 updates_dfr.addCallback(send_message,
223 available_updates, new_recommended)
224 return updates_dfr
225
226 def failed_update(failure):
227
228 return None
229
230 dfr = plugin_registry.update_cache()
231 dfr.addCallback(get_updates_and_new_recommended_list)
232 dfr.addErrback(failed_update)
233 return dfr
234
236 pending = reactor.callLater(self.check_interval,
237 self._auto_request, callback)
238 self._pending_call = pending
239
241 self._current_dfr = dfr = self.request()
242 dfr.addCallback(self._got_response, callback)
243 dfr.addErrback(self._response_failed)
244 config = common.application.config
245 should_update_plugin_cache = \
246 config.get_option('update_plugin_cache', section='plugin_registry')
247 if should_update_plugin_cache:
248 dfr.addCallback(self._update_plugin_cache)
249 dfr.addCallback(self._reset_pending_call, callback)
250
252 """
253 Stop any pending loop or http calls
254 """
255 try:
256 self._pending_call.cancel()
257 except (AttributeError, error.AlreadyCalled, error.AlreadyCancelled):
258 pass
259
260 try:
261 self._current_dfr.cancel()
262 except (AttributeError, defer.AlreadyCalledError):
263 pass
264
265 self._pending_call = None
266 self._current_dfr = None
267