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 import shutil
26 from entropy.services.skel import SocketCommands
27 from entropy.const import etpConst
28 from entropy.services.ugc.interfaces import Server
29 from entropy.misc import EmailSender
30
31 -class UGC(SocketCommands):
32
33 import entropy.dump as dumpTools
34 import entropy.tools as entropyTools
35 - def __init__(self, HostInterface, connection_data, store_path, store_url):
36
37 SocketCommands.__init__(self, HostInterface, inst_name = "ugc-commands")
38 self.connection_data = connection_data.copy()
39 self.store_path = store_path
40 self.store_url = store_url
41 self.DOC_TYPES = etpConst['ugc_doctypes'].copy()
42 self.SUPPORTED_DOCFILE_TYPES = [
43 self.DOC_TYPES['image'],
44 self.DOC_TYPES['generic_file'],
45 self.DOC_TYPES['youtube_video'],
46 ]
47 self.raw_commands = [
48 'ugc:add_comment', 'ugc:edit_comment',
49 'ugc:register_stream','ugc:do_download_stats',
50 'ugc:report_error'
51 ]
52
53 self.valid_commands = {
54 'ugc:get_comments': {
55 'auth': False,
56 'built_in': False,
57 'cb': self.docmd_get_comments,
58 'args': ["myargs"],
59 'as_user': False,
60 'desc': "get the comments of the provided package key",
61 'syntax': "<SESSION_ID> ugc:get_comments app-foo/foo",
62 'from': unicode(self),
63 },
64 'ugc:get_comments_by_identifiers': {
65 'auth': False,
66 'built_in': False,
67 'cb': self.docmd_get_comments_by_identifiers,
68 'args': ["myargs"],
69 'as_user': False,
70 'desc': "get the comments belonging to the provided identifiers",
71 'syntax': "<SESSION_ID> ugc:get_comments_by_identifiers <identifier1> <identifier2> <identifier3>",
72 'from': unicode(self),
73 },
74 'ugc:get_documents_by_identifiers': {
75 'auth': False,
76 'built_in': False,
77 'cb': self.docmd_get_documents_by_identifiers,
78 'args': ["myargs"],
79 'as_user': False,
80 'desc': "get the documents belonging to the provided identifiers",
81 'syntax': "<SESSION_ID> ugc:get_documents_by_identifiers <identifier1> <identifier2> <identifier3>",
82 'from': unicode(self),
83 },
84 'ugc:get_vote': {
85 'auth': False,
86 'built_in': False,
87 'cb': self.docmd_get_vote,
88 'args': ["myargs"],
89 'as_user': False,
90 'desc': "get the vote of the provided package key",
91 'syntax': "<SESSION_ID> ugc:get_vote app-foo/foo",
92 'from': unicode(self),
93 },
94 'ugc:get_downloads': {
95 'auth': False,
96 'built_in': False,
97 'cb': self.docmd_get_downloads,
98 'args': ["myargs"],
99 'as_user': False,
100 'desc': "get the number of downloads of the provided package key",
101 'syntax': "<SESSION_ID> ugc:get_downloads app-foo/foo",
102 'from': unicode(self),
103 },
104 'ugc:get_textdocs': {
105 'auth': False,
106 'built_in': False,
107 'cb': self.docmd_get_textdocs,
108 'args': ["myargs"],
109 'as_user': False,
110 'desc': "get the text documents belonging to the provided package key",
111 'syntax': "<SESSION_ID> ugc:get_textdocs app-foo/foo",
112 'from': unicode(self),
113 },
114 'ugc:get_textdocs_by_identifiers': {
115 'auth': False,
116 'built_in': False,
117 'cb': self.docmd_get_textdocs_by_identifiers,
118 'args': ["myargs"],
119 'as_user': False,
120 'desc': "get the text documents belonging to the provided identifiers",
121 'syntax': "<SESSION_ID> ugc:get_textdocs_by_identifiers <identifier1> <identifier2> <identifier3>",
122 'from': unicode(self),
123 },
124 'ugc:get_alldocs': {
125 'auth': False,
126 'built_in': False,
127 'cb': self.docmd_get_alldocs,
128 'args': ["myargs"],
129 'as_user': False,
130 'desc': "get the all the documents belonging to the provided package key",
131 'syntax': "<SESSION_ID> ugc:get_alldocs app-foo/foo",
132 'from': unicode(self),
133 },
134 'ugc:get_allvotes': {
135 'auth': False,
136 'built_in': False,
137 'cb': self.docmd_get_allvotes,
138 'args': [],
139 'as_user': False,
140 'desc': "get vote information for every available package key",
141 'syntax': "<SESSION_ID> ugc:get_allvotes",
142 'from': unicode(self),
143 },
144 'ugc:get_alldownloads': {
145 'auth': False,
146 'built_in': False,
147 'cb': self.docmd_get_alldownloads,
148 'args': [],
149 'as_user': False,
150 'desc': "get download information for every available package key",
151 'syntax': "<SESSION_ID> ugc:get_alldownloads",
152 'from': unicode(self),
153 },
154 'ugc:do_vote': {
155 'auth': True,
156 'built_in': False,
157 'cb': self.docmd_do_vote,
158 'args': ["authenticator","myargs"],
159 'as_user': False,
160 'desc': "vote the specified application (from 0 to 5)",
161 'syntax': "<SESSION_ID> ugc:do_vote app-foo/foo <0..5>",
162 'from': unicode(self),
163 },
164 'ugc:do_downloads': {
165 'auth': False,
166 'built_in': False,
167 'cb': self.docmd_do_downloads,
168 'args': ["authenticator","myargs"],
169 'as_user': False,
170 'desc': "inform the system of downloaded applications",
171 'syntax': "<SESSION_ID> ugc:do_downloads app-foo/foo1 app-foo/foo2 <...>",
172 'from': unicode(self),
173 },
174 'ugc:add_comment': {
175 'auth': True,
176 'built_in': False,
177 'cb': self.docmd_add_comment,
178 'args': ["authenticator","myargs"],
179 'as_user': False,
180 'desc': "insert a comment related to a package key",
181 'syntax': "<SESSION_ID> ugc:add_comment app-foo/foo <valid xml formatted data>",
182 'from': unicode(self),
183 },
184 'ugc:remove_comment': {
185 'auth': True,
186 'built_in': False,
187 'cb': self.docmd_remove_comment,
188 'args': ["authenticator","myargs"],
189 'as_user': False,
190 'desc': "remove a comment (you need its iddoc and mod/admin privs)",
191 'syntax': "<SESSION_ID> ugc:remove_comment <iddoc>",
192 'from': unicode(self),
193 },
194 'ugc:edit_comment': {
195 'auth': True,
196 'built_in': False,
197 'cb': self.docmd_edit_comment,
198 'args': ["authenticator","myargs"],
199 'as_user': False,
200 'desc': "edit a comment related to a package key (you need its iddoc, mod/admin privs or being the author)",
201 'syntax': "<SESSION_ID> ugc:edit_comment <iddoc> <valid xml formatted data>",
202 'from': unicode(self),
203 },
204 'ugc:register_stream': {
205 'auth': True,
206 'built_in': False,
207 'cb': self.docmd_register_stream,
208 'args': ["authenticator","myargs"],
209 'as_user': False,
210 'desc': "register an uploaded file (through stream cmd) to the relative place (image, file, videos)",
211 'syntax': "<SESSION_ID> ugc:register_stream app-foo/foo <valid xml formatted data>",
212 'from': unicode(self),
213 },
214 'ugc:remove_image': {
215 'auth': True,
216 'built_in': False,
217 'cb': self.docmd_remove_image,
218 'args': ["authenticator","myargs"],
219 'as_user': False,
220 'desc': "remove an image (you need its iddoc and mod/admin privs)",
221 'syntax': "<SESSION_ID> ugc:remove_image <iddoc>",
222 'from': unicode(self),
223 },
224 'ugc:remove_file': {
225 'auth': True,
226 'built_in': False,
227 'cb': self.docmd_remove_file,
228 'args': ["authenticator","myargs"],
229 'as_user': False,
230 'desc': "remove a file (you need its iddoc and mod/admin privs)",
231 'syntax': "<SESSION_ID> ugc:remove_file <iddoc>",
232 'from': unicode(self),
233 },
234 'ugc:remove_youtube_video': {
235 'auth': True,
236 'built_in': False,
237 'cb': self.docmd_remove_youtube_video,
238 'args': ["authenticator","myargs"],
239 'as_user': False,
240 'desc': "remove a youtube video (you need its iddoc and mod/admin privs)",
241 'syntax': "<SESSION_ID> ugc:remove_youtube_video <iddoc>",
242 'from': unicode(self),
243 },
244 'ugc:do_download_stats': {
245 'auth': False,
246 'built_in': False,
247 'cb': self.docmd_do_download_stats,
248 'args': ["authenticator","myargs"],
249 'as_user': False,
250 'desc': "send information regarding downloads and distribution used",
251 'syntax': "<SESSION_ID> ugc:do_download_stats <valid xml formatted data>",
252 'from': unicode(self),
253 },
254 'ugc:report_error': {
255 'auth': False,
256 'built_in': False,
257 'cb': self.docmd_do_report_error,
258 'args': ["authenticator","myargs"],
259 'as_user': False,
260 'desc': "submit an Entropy Error Report",
261 'syntax': "<SESSION_ID> ugc:report_error <valid xml formatted data>",
262 'from': unicode(self),
263 },
264 }
265
267 return Server(self.connection_data, self.store_path, self.store_url)
268
270 session_data = self.HostInterface.sessions.get(authenticator.session)
271 if not session_data:
272 return False
273 elif not session_data.has_key('auth_uid'):
274 return False
275 return session_data['auth_uid']
276
279
281 session_data = self.HostInterface.sessions.get(authenticator.session)
282 if not session_data:
283 return False
284 elif not session_data.has_key('stream_path'):
285 return False
286 elif not session_data['stream_path']:
287 return False
288 mypath = session_data['stream_path']
289 if not (os.path.isfile(mypath) and os.access(mypath,os.R_OK)):
290 return False
291 return mypath
292
294 session_data = self.HostInterface.sessions.get(authenticator.session)
295 if not session_data:
296 return None
297 elif not session_data.has_key('ip_address'):
298 return None
299 return session_data['ip_address']
300
302
303 if len(myargs) < 3:
304 return None,'wrong arguments'
305 pkgkey = myargs[0]
306
307 xml_string = ' '.join(myargs[1:])
308 try:
309 mydict = self.entropyTools.dict_from_xml(xml_string)
310 except Exception, e:
311 return None,"error: %s" % (e,)
312 if not (mydict.has_key('doc_type') \
313 and mydict.has_key('title') \
314 and mydict.has_key('description') \
315 and mydict.has_key('keywords') \
316 and mydict.has_key('file_name') ):
317 return None,'wrong dict arguments, xml must have 5 items with attr value -> doc_type, title, description, keywords, file_name'
318 doc_type = mydict.get('doc_type')
319 title = mydict.get('title')
320 description = mydict.get('description')
321 keywords = mydict.get('keywords')
322 file_name = mydict.get('file_name')
323 real_filename = mydict.get('real_filename')
324
325 try:
326 doc_type = int(doc_type)
327 except (ValueError,):
328 return None,'wrong arguments (doc_type)'
329 if doc_type not in self.SUPPORTED_DOCFILE_TYPES:
330 return None,'unsupported doc type (SUPPORTED_DOCFILE_TYPES)'
331
332 if not title: title = 'No title'
333 if not description: description = 'No description'
334 if not keywords: keywords = ''
335
336 userid = self._get_userid(authenticator)
337 if userid == None:
338 return False,'no session userid available'
339 elif isinstance(userid,bool) and not userid:
340 return False,'no session data available'
341 username = self._get_username(authenticator)
342
343
344 stream_path = self._get_session_file(authenticator)
345 if not stream_path:
346 return False,'no stream path available'
347 orig_stream_path = os.path.dirname(stream_path)
348 new_stream_path = orig_stream_path
349
350 scount = -1
351 while os.path.lexists(new_stream_path):
352 scount += 1
353 b_name = os.path.basename(stream_path)
354 b_name = "%s.%s" % (scount,b_name,)
355 new_stream_path = os.path.join(os.path.dirname(orig_stream_path),b_name)
356 if scount > 1000000:
357 return False,'while loop interrupted while looking for new_stream_path'
358 shutil.move(stream_path,new_stream_path)
359 stream_path = new_stream_path
360
361 ugc = self._load_ugc_interface()
362
363 rslt = None, 'invalid doc type'
364 if doc_type == self.DOC_TYPES['image']:
365 rslt = ugc.insert_image(pkgkey, userid, username, stream_path, file_name, title, description, keywords)
366 elif doc_type == self.DOC_TYPES['generic_file']:
367 rslt = ugc.insert_file(pkgkey, userid, username, stream_path, file_name, title, description, keywords)
368 elif doc_type == self.DOC_TYPES['youtube_video']:
369 rslt = ugc.insert_youtube_video(pkgkey, userid, username, stream_path, real_filename, title, description, keywords)
370 return rslt
371
403
434
475
477
478 if not myargs:
479 return None,'wrong arguments'
480 try:
481 iddoc = int(myargs[0])
482 except (ValueError,):
483 return False,'not a valid iddoc'
484
485 userid = self._get_userid(authenticator)
486 if userid == None:
487 return False,'no session userid available'
488 elif isinstance(userid,bool) and not userid:
489 return False,'no session data available'
490
491 ugc = self._load_ugc_interface()
492 iddoc_userid = ugc.get_iddoc_userid(iddoc)
493 if iddoc_userid == None:
494 return False,'document not available'
495
496
497 if authenticator.is_user() and (userid != iddoc_userid):
498 return False,'permission denied'
499
500 ugc = self._load_ugc_interface()
501 status, iddoc = ugc.delete_image(iddoc)
502 if not status:
503 return False,'document not removed or not available'
504
505 return iddoc,'ok'
506
508
509 if not myargs:
510 return None,'wrong arguments'
511 try:
512 iddoc = int(myargs[0])
513 except (ValueError,):
514 return False,'not a valid iddoc'
515
516 userid = self._get_userid(authenticator)
517 if userid == None:
518 return False,'no session userid available'
519 elif isinstance(userid,bool) and not userid:
520 return False,'no session data available'
521
522 ugc = self._load_ugc_interface()
523 iddoc_userid = ugc.get_iddoc_userid(iddoc)
524 if iddoc_userid == None:
525 return False,'document not available'
526
527
528 if authenticator.is_user() and (userid != iddoc_userid):
529 return False,'permission denied'
530
531 ugc = self._load_ugc_interface()
532 status, iddoc = ugc.delete_file(iddoc)
533 if not status:
534 return False,'document not removed or not available'
535
536 return iddoc,'ok'
537
539
540 if not myargs:
541 return None,'wrong arguments'
542 try:
543 iddoc = int(myargs[0])
544 except (ValueError,):
545 return False,'not a valid iddoc'
546
547 userid = self._get_userid(authenticator)
548 if userid == None:
549 return False,'no session userid available'
550 elif isinstance(userid,bool) and not userid:
551 return False,'no session data available'
552
553 ugc = self._load_ugc_interface()
554 iddoc_userid = ugc.get_iddoc_userid(iddoc)
555 if iddoc_userid == None:
556 return False,'document not available'
557
558
559 if authenticator.is_user() and (userid != iddoc_userid):
560 return False,'permission denied'
561
562 ugc = self._load_ugc_interface()
563 status, iddoc = ugc.remove_youtube_video(iddoc)
564 if not status:
565 return False,'document not removed or not available'
566
567 return iddoc,'ok'
568
570
571 if len(myargs) < 2:
572 return None,'wrong arguments'
573 pkgkey = myargs[0]
574 vote = myargs[1]
575
576 userid = self._get_userid(authenticator)
577 if userid == None:
578 return False,'no session userid available'
579 elif isinstance(userid,bool) and not userid:
580 return userid,'no session data available'
581
582 ugc = self._load_ugc_interface()
583 voted = ugc.do_vote(pkgkey, userid, vote)
584 if not voted:
585 return voted,'already voted'
586 return voted,'ok'
587
589
590 if not myargs:
591 return None,'wrong arguments'
592
593 ip_addr = self._get_session_ip_address(authenticator)
594 ugc = self._load_ugc_interface()
595 done = ugc.do_downloads(myargs, ip_addr = ip_addr)
596 if not done:
597 return done,'download not stored'
598 return done,'ok'
599
601
602 if not myargs:
603 return None,'wrong arguments'
604
605 xml_string = ' '.join(myargs)
606 try:
607 mydict = self.entropyTools.dict_from_xml(xml_string)
608 except Exception, e:
609 return None,"error: %s" % (e,)
610 if not (mydict.has_key('branch') and \
611 mydict.has_key('release_string') and \
612 mydict.has_key('pkgkeys')):
613 return None,'wrong dict arguments, xml must have 3 items with attr value -> branch, release_string, pkgkeys'
614
615 branch = mydict.get('branch')
616 release_string = mydict.get('release_string')
617 hw_hash = mydict.get('hw_hash')
618 pkgkeys = mydict.get('pkgkeys').split()
619 ip_addr = self._get_session_ip_address(authenticator)
620
621 ugc = self._load_ugc_interface()
622 done = ugc.do_download_stats(branch, release_string, hw_hash, pkgkeys,
623 ip_addr)
624 if not done:
625 return done,'stats not stored'
626 return done,'ok'
627
629 ugc = self._load_ugc_interface()
630 metadata = ugc.get_ugc_metadata_doctypes(pkgkey, doctypes)
631 if not metadata:
632 return None
633 return metadata
634
641
648
660
681
683
684 if not myargs:
685 return None,'wrong arguments'
686
687 identifiers = []
688 for myarg in myargs:
689 try:
690 identifiers.append(int(myarg))
691 except ValueError:
692 pass
693
694 if not identifiers:
695 return None,'wrong arguments'
696
697 metadata = self._get_generic_documents_by_identifiers(identifiers)
698 if metadata == None:
699 return None,'no metadata available'
700
701 return metadata,'ok'
702
704 ugc = self._load_ugc_interface()
705 metadata = ugc.get_ugc_allvotes()
706 if not metadata:
707 return None,'no metadata available'
708 return metadata,'ok'
709
711 ugc = self._load_ugc_interface()
712 metadata = ugc.get_ugc_alldownloads()
713 if not metadata:
714 return None,'no metadata available'
715 return metadata,'ok'
716
718
719 if not myargs:
720 return None,'wrong arguments'
721 pkgkey = myargs[0]
722
723 ugc = self._load_ugc_interface()
724 vote = ugc.get_ugc_vote(pkgkey)
725 return vote,'ok'
726
728
729 if not myargs:
730 return None,'wrong arguments'
731 pkgkey = myargs[0]
732
733 ugc = self._load_ugc_interface()
734 downloads = ugc.get_ugc_downloads(pkgkey)
735 return downloads,'ok'
736
737 - def docmd_get_textdocs(self, myargs):
738
739 if not myargs:
740 return None,'wrong arguments'
741 pkgkey = myargs[0]
742
743 metadata = self._get_generic_doctypes(pkgkey, [self.DOC_TYPES['comments'],self.DOC_TYPES['bbcode_doc']])
744 if metadata == None:
745 return None,'no metadata available'
746
747 return metadata,'ok'
748
750
751 if not myargs:
752 return None,'wrong arguments'
753
754 identifiers = []
755 for myarg in myargs:
756 try:
757 identifiers.append(int(myarg))
758 except ValueError:
759 pass
760
761 if not identifiers:
762 return None,'wrong arguments'
763
764 metadata = self._get_generic_doctypes_by_identifiers(identifiers, [self.DOC_TYPES['comments'],self.DOC_TYPES['bbcode_doc']])
765 if metadata == None:
766 return None,'no metadata available'
767
768 return metadata,'ok'
769
771
772 if not myargs:
773 return None,'wrong arguments'
774 pkgkey = myargs[0]
775
776 metadata = self._get_generic_doctypes(pkgkey, [self.DOC_TYPES[x] for x in self.DOC_TYPES])
777 if metadata == None:
778 return None,'no metadata available'
779
780 return metadata,'ok'
781
783
784 if not myargs:
785 return None, 'wrong arguments'
786
787 import zlib
788 comp_xml_string = ' '.join(myargs)
789 try:
790 xml_string = zlib.decompress(comp_xml_string)
791 mydict = self.entropyTools.dict_from_xml_extended(xml_string)
792 except Exception, e:
793 return None, "error: %s" % (e,)
794
795 subject = 'Entropy Error Reporting Handler'
796 destination_email = 'entropy.errors@sabayonlinux.org'
797 sender_email = mydict.get('email', 'www-data@sabayonlinux.org')
798 if not self.entropyTools.is_valid_email(sender_email):
799 sender_email = 'www-data@sabayonlinux.org'
800 keys_to_file = ['errordata', 'processes', 'lspci', 'dmesg', 'locale']
801
802
803 mail_txt = ''
804 for key in sorted(mydict):
805 if key in keys_to_file:
806 continue
807 mail_txt += u'%s: %s\n' % (key, mydict.get(key),)
808
809 from datetime import datetime
810 import time
811 import tempfile
812 date = datetime.fromtimestamp(time.time())
813
814
815 ip_addr = self._get_session_ip_address(authenticator)
816 mail_txt += u'ip_address: %s\n' % (ip_addr,)
817 mail_txt += u'date: %s\n' % (date,)
818
819 files = []
820 rm_paths = []
821 for key in keys_to_file:
822 if key not in mydict:
823 continue
824 fd, path = tempfile.mkstemp(suffix = "__%s.txt" % (key,))
825 try:
826 f_path = open(path, "w")
827 f_path.write(mydict.get(key,''))
828 f_path.flush()
829 f_path.close()
830 except IOError:
831 continue
832 os.close(fd)
833 files.append(path)
834 rm_paths.append(path)
835
836 sender = EmailSender()
837 sender.send_mime_email(sender_email, [destination_email], subject,
838 mail_txt, files)
839 del sender
840
841 for rm_path in rm_paths:
842 os.remove(rm_path)
843
844 return True,'ok'
845