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