1
2 '''
3 # DESCRIPTION:
4 # Entropy Object Oriented Interface
5
6 Copyright (C) 2007-2009 Fabio Erculiani
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 '''
22
23 from __future__ import with_statement
24 import os
25 from entropy.core import Singleton
26 from entropy.exceptions import *
27 from entropy.const import etpConst, etpCache, const_setup_file, const_setup_perms
28 from entropy.i18n import _
29
31
32 ssl_connection = True
33 - def __init__(self, EquoInstance, quiet = True, show_progress = False):
34
35 from entropy.client.interfaces import Client as Cl
36 if not isinstance(EquoInstance,Cl):
37 mytxt = _("A valid Client based instance is needed")
38 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
39
40 import socket, threading
41 self.socket, self.threading = socket, threading
42 import struct
43 self.struct = struct
44 self.Entropy = EquoInstance
45 self.store = AuthStore()
46 self.quiet = quiet
47 self.show_progress = show_progress
48 self.UGCCache = Cache(self)
49 self.TxLocks = {}
50
74
86
88
89 aware = self.UGCCache._get_live_cache_item(repository, 'is_repository_eapi3_aware')
90 if aware != None:
91 return aware
92
93 srv = self.get_service_connection(repository, check = False, timeout = 3)
94 if srv == None:
95 aware = False
96 else:
97 session = srv.open_session()
98 if session != None:
99 srv.close_session(session)
100 srv.disconnect()
101 aware = True
102 else:
103 aware = False
104
105 self.UGCCache._set_live_cache_item(repository, 'is_repository_eapi3_aware', aware)
106 return aware
107
110
113
114 - def do_login(self, repository, force = False):
115
116 login_data = self.read_login(repository)
117 if (login_data != None) and not force:
118 return True,_('ok')
119
120 aware = self.is_repository_eapi3_aware(repository)
121 if not aware:
122 return False,_('repository does not support EAPI3')
123
124 def fake_callback(*args,**kwargs):
125 return True
126
127 attempts = 3
128 while attempts:
129
130
131 input_params = [
132 ('username',_('Username'),fake_callback,False),
133 ('password',_('Password'),fake_callback,True)
134 ]
135 login_data = self.Entropy.inputBox(
136 "%s %s %s" % (_('Please login against'),repository,_('repository'),),
137 input_params,
138 cancel_button = True
139 )
140 if not login_data:
141 return False,_('login abort')
142
143
144 srv = self.get_service_connection(repository)
145 if srv == None:
146 return False,_('connection issues')
147 session = srv.open_session()
148 login_status, login_msg = srv.CmdInterface.service_login(login_data['username'], login_data['password'], session)
149 if not login_status:
150 srv.close_session(session)
151 srv.disconnect()
152 self.Entropy.askQuestion("%s: %s" % (_("Access denied. Login failed"),login_msg,), responses = ["Ok"])
153 attempts -= 1
154 continue
155
156
157 srv.close_session(session)
158 srv.disconnect()
159 rc = self.Entropy.askQuestion(_("Login successful. Do you want to save these credentials ?"))
160 save = False
161 if rc == "Yes": save = True
162 self.store.store_login(login_data['username'], login_data['password'], repository, save = save)
163 return True,_('ok')
164
165
166 - def login(self, repository, force = False):
173
174
175 - def logout(self, repository):
177
178 - def do_cmd(self, repository, login_required, func, args, kwargs):
179
180 if not self.TxLocks.has_key(repository):
181 self.TxLocks[repository] = self.threading.Lock()
182
183 with self.TxLocks[repository]:
184
185 if login_required:
186 status, err_msg = self.do_login(repository)
187 if not status:
188 return False,err_msg
189
190 srv = self.get_service_connection(repository)
191 if srv == None:
192 return False,'no connection'
193 session = srv.open_session()
194 if session == None:
195 return False,'no session'
196 args.insert(0,session)
197
198 if login_required:
199 stored_pass = False
200 while 1:
201
202 login_data = self.read_login(repository)
203 if login_data == None:
204 status, msg = self.login(repository)
205 if not status: return status, msg
206 username, password = self.read_login(repository)
207 else:
208 stored_pass = True
209 username, password = login_data
210 logged, error = srv.CmdInterface.service_login(username, password, session)
211 if not logged:
212 if stored_pass:
213 stored_pass = False
214 self.remove_login(repository)
215 continue
216 srv.close_session(session)
217 srv.disconnect()
218 return logged, error
219 break
220
221 try:
222 cmd_func = getattr(srv.CmdInterface, func)
223 except AttributeError:
224 return False, 'local function not available'
225 rslt = cmd_func(*args,**kwargs)
226 try:
227 srv.close_session(session)
228 srv.disconnect()
229 except ConnectionError:
230 return False, 'no connection'
231
232 return rslt
233
236
239
241 return self.do_cmd(repository, False, "ugc_get_documents_by_identifiers", [identifiers], {})
242
246
250
254
255 - def add_vote(self, repository, pkgkey, vote):
256 data = self.do_cmd(repository, True, "ugc_do_vote", [pkgkey, vote], {})
257 if isinstance(data,tuple): voted, add_err_msg = data
258 else: return False,'wrong server answer'
259 if voted: self.get_vote(repository, pkgkey)
260 return voted, add_err_msg
261
262 - def get_vote(self, repository, pkgkey):
263 vote, err_msg = self.do_cmd(repository, False, "ugc_get_vote", [pkgkey], {})
264 if isinstance(vote,float):
265 mydict = {pkgkey: vote}
266 self.UGCCache.update_vote_cache(repository, mydict)
267 return vote, err_msg
268
274
277
279 data = self.do_cmd(repository, False, "ugc_get_downloads", [pkgkey], {})
280 if isinstance(data,tuple): downloads, err_msg = data
281 else: return False,'wrong server answer'
282 if downloads:
283 mydict = {pkgkey: downloads}
284 self.UGCCache.update_downloads_cache(repository, mydict)
285 return downloads, err_msg
286
292
294 return self.do_cmd(repository, False, "ugc_do_download_stats", [pkgkeys], {})
295
296 - def send_file(self, repository, pkgkey, file_path, title, description, keywords):
299
303
304 - def send_image(self, repository, pkgkey, image_path, title, description, keywords):
307
311
312 - def send_youtube_video(self, repository, pkgkey, video_path, title, description, keywords):
315
319
320 - def get_docs(self, repository, pkgkey):
321 data = self.do_cmd(repository, False, "ugc_get_docs", [pkgkey], {})
322 if isinstance(data,tuple): docs_data, err_msg = data
323 else: return False,'wrong server answer'
324 if err_msg == 'ok':
325 self.UGCCache.update_alldocs_cache(pkgkey, repository, docs_data)
326 return docs_data, err_msg
327
329 if ugc_type == etpConst['ugc_doctypes']['generic_file']:
330 return self.send_file(repository, pkgkey, data, title, description, keywords)
331 elif ugc_type == etpConst['ugc_doctypes']['image']:
332 return self.send_image(repository, pkgkey, data, title, description, keywords)
333 elif ugc_type == etpConst['ugc_doctypes']['youtube_video']:
334 return self.send_youtube_video(repository, pkgkey, data, title, description, keywords)
335 elif ugc_type == etpConst['ugc_doctypes']['comments']:
336 return self.add_comment(repository, pkgkey, description, title, keywords)
337 return None,'type not supported locally'
338
349
352
353
355
356 access_file = etpConst['ugc_accessfile']
358
359 from xml.dom import minidom
360 from xml.parsers import expat
361 self.expat = expat
362 self.minidom = minidom
363 self.setup_store_paths()
364 try:
365 self.setup_permissions()
366 except IOError:
367 pass
368 self.store = {}
369 try:
370 self.xmldoc = self.minidom.parse(self.access_file)
371 except (self.expat.ExpatError,IOError,):
372 self.xmldoc = None
373 if self.xmldoc != None:
374 try:
375 self.parse_document()
376 except self.expat.ExpatError:
377 self.xmldoc = None
378 self.store = {}
379
381 myhome = os.getenv("HOME")
382 if myhome != None:
383 if os.path.isdir(myhome) and os.access(myhome,os.W_OK):
384 self.access_file = os.path.join(myhome,".config/entropy",os.path.basename(self.access_file))
385 self.access_dir = os.path.dirname(self.access_file)
386
398
400 self.store.clear()
401 store = self.xmldoc.getElementsByTagName("store")[0]
402 repositories = store.getElementsByTagName("repository")
403 for repository in repositories:
404 repoid = repository.getAttribute("id")
405 if not repoid: continue
406 username = repository.getElementsByTagName("username")[0].firstChild.data.strip()
407 password = repository.getElementsByTagName("password")[0].firstChild.data.strip()
408 self.store[repoid] = {'username': username, 'password': password}
409
410 - def store_login(self, username, password, repository, save = True):
411 self.store[repository] = {'username': username, 'password': password}
412 if save:
413 self.save_store()
414
416
417 self.xmldoc = self.minidom.Document()
418 store = self.xmldoc.createElement("store")
419
420 for repository in self.store:
421 repo = self.xmldoc.createElement("repository")
422 repo.setAttribute('id',repository)
423
424 username = self.xmldoc.createElement("username")
425 username_value = self.xmldoc.createTextNode(self.store[repository]['username'])
426 username.appendChild(username_value)
427 repo.appendChild(username)
428
429 password = self.xmldoc.createElement("password")
430 password_value = self.xmldoc.createTextNode(self.store[repository]['password'])
431 password.appendChild(password_value)
432 repo.appendChild(password)
433 store.appendChild(repo)
434
435 self.xmldoc.appendChild(store)
436 f = open(self.access_file,"w")
437 f.writelines(self.xmldoc.toprettyxml(indent=" "))
438 f.flush()
439 f.close()
440 self.setup_permissions()
441 self.parse_document()
442
448
452
454
456
457 if not isinstance(UGCClientInstance,Client):
458 mytxt = _("A valid UGC Client interface based instance is needed")
459 raise IncorrectParameter("IncorrectParameter: %s" % (mytxt,))
460
461 import threading
462 import entropy.dump as dumpTools
463 self.CacheLock = threading.Lock()
464 self.dumpTools = dumpTools
465 self.Service = UGCClientInstance
466 self.xcache = {}
467
472
474 if repository not in self.xcache:
475 self.xcache[repository] = {}
476 if type(obj) in (list,tuple,):
477 my_obj = obj[:]
478 elif type(obj) in (set,frozenset,dict,):
479 my_obj = obj.copy()
480 else:
481 my_obj = obj
482 self.xcache[repository][item] = my_obj
483
490
493
496
498 return self._get_downloads_cache_dir(repository)
499
501 return self._get_alldocs_cache_dir(repository)+"/"+pkgkey
502
505
508
511
514
517
519 return 'get_package_alldocs_cache_'+repository
520
522 cache_file = os.path.join(etpConst['dumpstoragedir'],self._get_store_cache_file(iddoc, repository, doc_url))
523 cache_dir = os.path.dirname(cache_file)
524
525 try:
526 if not os.path.isdir(cache_dir):
527 os.makedirs(cache_dir,0775)
528 if etpConst['entropygid'] != None:
529 const_setup_perms(cache_dir,etpConst['entropygid'])
530 except OSError:
531 raise PermissionDenied("PermissionDenied: %s %s" % (_("Cannot setup cache directory"),cache_dir,))
532 if not os.access(cache_dir,os.W_OK):
533 raise PermissionDenied("PermissionDenied: %s %s" % (_("Cannot write to cache directory"),cache_dir,))
534
535 if os.path.isfile(cache_file) or os.path.islink(cache_file):
536 try:
537 os.remove(cache_file)
538 except OSError:
539 raise PermissionDenied("PermissionDenied: %s %s" % (_("Cannot remove cache file"),cache_file,))
540
541 fetcher = self.Service.Entropy.urlFetcher(doc_url, cache_file, resume = False)
542 rc = fetcher.download()
543 if rc in ("-1","-2","-3","-4"): return None
544 if not os.path.isfile(cache_file): return None
545
546 try:
547 os.chmod(cache_file,0664)
548 if etpConst['entropygid'] != None:
549 os.chown(cache_file,-1,etpConst['entropygid'])
550 except OSError:
551 raise PermissionDenied("PermissionDenied: %s %s" % (_("Cannot write to cache file"),cache_file,))
552
553 del fetcher
554 return cache_file
555
557 cache_file = os.path.join(etpConst['dumpstoragedir'],self._get_store_cache_file(iddoc, repository, doc_url))
558 if os.path.isfile(cache_file) and os.access(cache_file,os.R_OK):
559 return cache_file
560
568
576
579
584
589
594
600
602 cache_key = self._get_vote_cache_key(repository)
603 cached = self._get_live_cache_item(repository, cache_key)
604 if cached != None:
605 return cached
606 with self.CacheLock:
607 cache_file = self._get_vote_cache_file(repository)
608 try:
609 data = self.dumpTools.loadobj(cache_file)
610 if data != None:
611 self._set_live_cache_item(repository, cache_key, data)
612 except (IOError,EOFError,OSError):
613 data = None
614 return data
615
617 cache_key = self._get_downloads_cache_key(repository)
618 cached = self._get_live_cache_item(repository, cache_key)
619 if cached != None:
620 return cached
621 with self.CacheLock:
622 cache_file = self._get_downloads_cache_file(repository)
623 try:
624 data = self.dumpTools.loadobj(cache_file)
625 if data != None:
626 self._set_live_cache_item(repository, cache_key, data)
627 except (IOError,EOFError,OSError):
628 data = None
629 return data
630
632 cache_key = self._get_alldocs_cache_key(repository)
633 cached = self._get_live_cache_item(repository, cache_key)
634 if isinstance(cached,dict):
635 if cached.has_key(pkgkey): return cached[pkgkey]
636 else:
637 cached = {}
638 with self.CacheLock:
639 cache_file = self._get_alldocs_cache_file(pkgkey, repository)
640 try:
641 data = self.dumpTools.loadobj(cache_file)
642 if data != None:
643 cached[pkgkey] = data
644 self._set_live_cache_item(repository, cache_key, cached)
645 except (IOError,EOFError,OSError):
646 data = None
647 return data
648
653
655 with self.CacheLock:
656 self._clear_live_cache_item(repository, self._get_downloads_cache_key(repository))
657 self.dumpTools.dumpobj(self._get_downloads_cache_file(repository), down_dict)
658
660 with self.CacheLock:
661 self._clear_live_cache_item(repository, self._get_alldocs_cache_key(repository))
662 self.dumpTools.dumpobj(self._get_alldocs_cache_file(pkgkey, repository), alldocs_dict)
663
673
686