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