~ubuntu-branches/ubuntu/natty/moin/natty-updates

« back to all changes in this revision

Viewing changes to MoinMoin/xmlrpc/__init__.py

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2008-06-22 21:17:13 UTC
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: james.westby@ubuntu.com-20080622211713-inlv5k4eifxckelr
ImportĀ upstreamĀ versionĀ 1.7.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
    when really necessary (like for transferring binary files like
18
18
    attachments maybe).
19
19
 
20
 
    @copyright: 2003-2009 MoinMoin:ThomasWaldmann,
21
 
                2004-2006 MoinMoin:AlexanderSchremmer,
22
 
                2007-2009 MoinMoin:ReimarBauer
 
20
    @copyright: 2003-2008 MoinMoin:ThomasWaldmann,
 
21
                2004-2006 MoinMoin:AlexanderSchremmer
23
22
    @license: GNU GPL, see COPYING for details
24
23
"""
25
24
from MoinMoin.util import pysupport
31
30
from MoinMoin import log
32
31
logging = log.getLogger(__name__)
33
32
 
34
 
from MoinMoin import auth, config, user, wikiutil
 
33
from MoinMoin import config, user, wikiutil
35
34
from MoinMoin.Page import Page
36
35
from MoinMoin.PageEditor import PageEditor
37
36
from MoinMoin.logfile import editlog
38
37
from MoinMoin.action import AttachFile
39
38
from MoinMoin import caching
 
39
from MoinMoin import session
 
40
 
 
41
 
 
42
class XmlRpcAuthTokenIDHandler(session.SessionIDHandler):
 
43
    def __init__(self, token=None):
 
44
        session.SessionIDHandler.__init__(self)
 
45
        self.token = token
 
46
 
 
47
    def get(self, request):
 
48
        return self.token
 
49
 
 
50
    def set(self, request, session_id, expires):
 
51
        self.token = session_id
40
52
 
41
53
 
42
54
logging_tearline = '- XMLRPC %s ' + '-' * 40
43
55
 
44
56
class XmlRpcBase:
45
 
    """
46
 
    XMLRPC base class with common functionality of wiki xmlrpc v1 and v2
47
 
    """
 
57
    """ XMLRPC base class with common functionality of wiki xmlrpc v1 and v2 """
48
58
    def __init__(self, request):
49
59
        """
50
60
        Initialize an XmlRpcBase object.
59
69
    #############################################################################
60
70
 
61
71
    def _instr(self, text):
62
 
        """
63
 
        Convert inbound string.
 
72
        """ Convert inbound string.
64
73
 
65
74
        @param text: the text to convert (encoded str or unicode)
66
75
        @rtype: unicode
69
78
        raise NotImplementedError("please implement _instr in derived class")
70
79
 
71
80
    def _outstr(self, text):
72
 
        """
73
 
        Convert outbound string.
 
81
        """ Convert outbound string.
74
82
 
75
83
        @param text: the text to convert (encoded str or unicode)
76
84
        @rtype: str
79
87
        raise NotImplementedError("please implement _outstr in derived class")
80
88
 
81
89
    def _inlob(self, text):
82
 
        """
83
 
        Convert inbound base64-encoded utf-8 to Large OBject.
 
90
        """ Convert inbound base64-encoded utf-8 to Large OBject.
84
91
 
85
92
        @param text: the text to convert
86
93
        @rtype: unicode
87
94
        @return: text
88
95
        """
89
 
        text = text.data # this is a already base64-decoded 8bit string
 
96
        text = text.data #this is a already base64-decoded 8bit string
90
97
        text = unicode(text, 'utf-8')
91
98
        return text
92
99
 
93
100
    def _outlob(self, text):
94
 
        """
95
 
        Convert outbound Large OBject to base64-encoded utf-8.
 
101
        """ Convert outbound Large OBject to base64-encoded utf-8.
96
102
 
97
103
        @param text: the text, either unicode or utf-8 string
98
104
        @rtype: str
106
112
        return xmlrpclib.Binary(text)
107
113
 
108
114
    def _dump_exc(self):
109
 
        """
110
 
        Convert an exception to a string.
 
115
        """ Convert an exception to a string.
111
116
 
112
117
        @rtype: str
113
118
        @return: traceback as string
121
126
        )
122
127
 
123
128
    def process(self):
124
 
        """
125
 
        xmlrpc v1 and v2 dispatcher
126
 
        """
127
 
        request = self.request
 
129
        """ xmlrpc v1 and v2 dispatcher """
128
130
        try:
129
131
            if 'xmlrpc' in self.request.cfg.actions_excluded:
130
132
                # we do not handle xmlrpc v1 and v2 differently
131
133
                response = xmlrpclib.Fault(1, "This moin wiki does not allow xmlrpc method calls.")
132
134
            else:
133
 
                # overwrite any user there might be, if you need a valid user for
134
 
                # xmlrpc, you have to use multicall and getAuthToken / applyAuthToken
135
 
                request.user = user.User(request, auth_method='xmlrpc:invalid')
136
 
 
137
 
                data = request.read()
 
135
                data = self.request.read(self.request.content_length)
138
136
 
139
137
                try:
140
138
                    params, method = xmlrpclib.loads(data)
168
166
                # serialize it
169
167
                response = xmlrpclib.dumps(response, methodresponse=1, allow_none=True)
170
168
 
171
 
        request = request.request
172
 
        request.content_type = 'text/xml'
173
 
        request.data = response
174
 
        return request
 
169
        self.request.emit_http_headers([
 
170
            "Content-Type: text/xml; charset=utf-8",
 
171
            "Content-Length: %d" % len(response),
 
172
        ])
 
173
        self.request.write(response)
175
174
 
176
175
    def dispatch(self, method, params):
177
 
        """
178
 
        call dispatcher - for method==xxx it either locates a method called
179
 
        xmlrpc_xxx or loads a plugin from plugin/xmlrpc/xxx.py
 
176
        """ call dispatcher - for method==xxx it either locates a method called
 
177
            xmlrpc_xxx or loads a plugin from plugin/xmlrpc/xxx.py
180
178
        """
181
179
        method = method.replace(".", "_")
182
180
 
212
210
    #############################################################################
213
211
 
214
212
    def xmlrpc_system_multicall(self, call_list):
215
 
        """
216
 
        system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => [[4], ...]
 
213
        """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => [[4], ...]
217
214
 
218
215
        Allows the caller to package multiple XML-RPC calls into a single
219
216
        request.
253
250
    #############################################################################
254
251
 
255
252
    def xmlrpc_getRPCVersionSupported(self):
256
 
        """
257
 
        Returns version of the Wiki xmlrpc API.
 
253
        """ Returns version of the Wiki API.
258
254
 
259
255
        @rtype: int
260
256
        @return: 1 or 2 (wikirpc version)
262
258
        return self.version
263
259
 
264
260
    def xmlrpc_getAllPages(self):
265
 
        """
266
 
        Get all pages readable by current user
 
261
        """ Get all pages readable by current user
267
262
 
268
263
        @rtype: list
269
264
        @return: a list of all pages.
270
265
        """
271
266
 
272
 
        # the official WikiRPC interface is implemented by the extended method as well
 
267
        # the official WikiRPC interface is implemented by the extended method
 
268
        # as well
273
269
        return self.xmlrpc_getAllPagesEx()
274
270
 
275
271
 
276
272
    def xmlrpc_getAllPagesEx(self, opts=None):
277
 
        """
278
 
        Get all pages readable by current user. Not an WikiRPC method.
 
273
        """ Get all pages readable by current user. Not an WikiRPC method.
279
274
 
280
275
        @param opts: dictionary that can contain the following arguments:
281
276
                include_system:: set it to false if you do not want to see system pages
331
326
            return [self._outstr(page) for page in pagelist]
332
327
 
333
328
    def xmlrpc_getRecentChanges(self, date):
334
 
        """
335
 
        Get RecentChanges since date
 
329
        """ Get RecentChanges since date
336
330
 
337
331
        @param date: date since when rc will be listed
338
332
        @rtype: list
384
378
        return return_items
385
379
 
386
380
    def xmlrpc_getPageInfo(self, pagename):
387
 
        """
388
 
        Invoke xmlrpc_getPageInfoVersion with rev=None
389
 
        """
 
381
        """ Invoke xmlrpc_getPageInfoVersion with rev=None """
390
382
        return self.xmlrpc_getPageInfoVersion(pagename, rev=None)
391
383
 
392
384
    def xmlrpc_getPageInfoVersion(self, pagename, rev):
393
 
        """
394
 
        Return page information for specific revision
 
385
        """ Return page information for specific revision
395
386
 
396
387
        @param pagename: the name of the page (utf-8)
397
388
        @param rev: revision to get info about (int)
448
439
            }
449
440
 
450
441
    def xmlrpc_getPage(self, pagename):
451
 
        """
452
 
        Invoke xmlrpc_getPageVersion with rev=None
453
 
        """
 
442
        """ Invoke xmlrpc_getPageVersion with rev=None """
454
443
        return self.xmlrpc_getPageVersion(pagename, rev=None)
455
444
 
456
445
    def xmlrpc_getPageVersion(self, pagename, rev):
457
 
        """
458
 
        Get raw text from specific revision of pagename
 
446
        """ Get raw text from specific revision of pagename
459
447
 
460
448
        @param pagename: pagename (utf-8)
461
449
        @param rev: revision number (int)
484
472
            return self._outlob(page.get_raw_body())
485
473
 
486
474
    def xmlrpc_getPageHTML(self, pagename):
487
 
        """
488
 
        Invoke xmlrpc_getPageHTMLVersion with rev=None
489
 
        """
 
475
        """ Invoke xmlrpc_getPageHTMLVersion with rev=None """
490
476
        return self.xmlrpc_getPageHTMLVersion(pagename, rev=None)
491
477
 
492
478
    def xmlrpc_getPageHTMLVersion(self, pagename, rev):
493
 
        """
494
 
        Get HTML of from specific revision of pagename
 
479
        """ Get HTML of from specific revision of pagename
495
480
 
496
481
        @param pagename: the page name (utf-8)
497
482
        @param rev: revision number (int)
555
540
        @param pagename: the page name (unicode or utf-8)
556
541
        @param pagetext: the new page text (content, unicode or utf-8)
557
542
        @rtype: bool
558
 
        @return: True on success
 
543
        @return: true on success
559
544
        """
560
545
 
561
546
        pagename = self._instr(pagename)
562
 
        pagename = wikiutil.normalize_pagename(pagename, self.cfg)
 
547
 
563
548
        if not pagename:
564
549
            return xmlrpclib.Fault("INVALID", "pagename can't be empty")
565
550
 
567
552
        if not self.request.user.may.write(pagename):
568
553
            return xmlrpclib.Fault(1, "You are not allowed to edit this page")
569
554
 
570
 
        page = PageEditor(self.request, pagename, do_editor_backup=0)
 
555
        page = PageEditor(self.request, pagename)
571
556
        try:
572
557
            if self.version == 2:
573
558
                newtext = self._instr(pagetext)
583
568
 
584
569
        return xmlrpclib.Boolean(1)
585
570
 
586
 
    def xmlrpc_renamePage(self, pagename, newpagename):
587
 
        """
588
 
        Renames a page <pagename> to <newpagename>.
589
 
 
590
 
        @param pagename: the page name (unicode or utf-8)
591
 
        @param newpagename: the new pagename (unicode or utf-8)
592
 
        @rtype: bool
593
 
        @return: True on success
594
 
        """
595
 
 
596
 
        pagename = self._instr(pagename)
597
 
        pagename = wikiutil.normalize_pagename(pagename, self.cfg)
598
 
        if not pagename:
599
 
            return xmlrpclib.Fault("INVALID", "pagename can't be empty")
600
 
 
601
 
        # check ACLs
602
 
        if not (self.request.user.may.delete(pagename) and self.request.user.may.write(newpagename)):
603
 
            return xmlrpclib.Fault(1, "You are not allowed to rename this page")
604
 
        editor = PageEditor(self.request, pagename, do_editor_backup=0)
605
 
 
606
 
        try:
607
 
            editor.renamePage(newpagename)
608
 
        except PageEditor.SaveError, error:
609
 
            return xmlrpclib.Fault(1, "Rename failed: %s" % (str(error), ))
610
 
 
611
 
        return xmlrpclib.Boolean(1)
612
 
 
613
571
    def xmlrpc_revertPage(self, pagename, revision):
614
 
        """
615
 
        Revert a page to previous revision
 
572
        """Revert a page to previous revision
616
573
 
617
574
        This is mainly intended to be used by the jabber bot.
618
575
 
619
576
        @param pagename: the page name (unicode or utf-8)
620
577
        @param revision: revision to revert to
621
 
        @rtype: bool
622
 
        @return: True on success
 
578
        @rtype bool
 
579
        @return true on success
623
580
 
624
581
        """
625
 
 
626
 
        pagename = self._instr(pagename)
627
 
 
628
582
        if not self.request.user.may.write(pagename):
629
583
            return xmlrpclib.Fault(1, "You are not allowed to edit this page")
630
584
 
631
585
        rev = int(self._instr(revision))
632
 
        editor = PageEditor(self.request, pagename, do_editor_backup=0)
 
586
        editor = PageEditor(self.request, pagename)
633
587
 
634
588
        try:
635
589
            editor.revertPage(rev)
639
593
        return xmlrpclib.Boolean(1)
640
594
 
641
595
    def xmlrpc_searchPages(self, query_string):
642
 
        """
643
 
        Searches pages for query_string.
644
 
        Returns a list of tuples (foundpagename, context)
 
596
        """ Searches pages for query_string.
 
597
            Returns a list of tuples (foundpagename, context)
645
598
        """
646
599
        from MoinMoin import search
647
600
        results = search.searchPages(self.request, query_string)
652
605
                for hit in results.hits]
653
606
 
654
607
    def xmlrpc_searchPagesEx(self, query_string, search_type, length, case, mtime, regexp):
655
 
        """
656
 
        Searches pages for query_string - extended version for compatibility
 
608
        """ Searches pages for query_string - extended version for compatibility
657
609
 
658
610
        This function, in contrary to searchPages(), doesn't return HTML-formatted data.
659
611
 
664
616
        @param mtime: only output pages modified after mtime
665
617
        @param regexp: should the query_string be treates as a regular expression?
666
618
        @return: (page name, context preview, page url)
 
619
 
667
620
        """
668
621
        from MoinMoin import search
669
622
        from MoinMoin.formatter.text_plain import Formatter
682
635
                for hit in results.hits]
683
636
 
684
637
    def xmlrpc_getMoinVersion(self):
685
 
        """
686
 
        Returns a tuple of the MoinMoin version:
687
 
        (project, release, revision)
 
638
        """ Returns a tuple of the MoinMoin version:
 
639
            (project, release, revision)
688
640
        """
689
641
        from MoinMoin import version
690
642
        return (version.project, version.release, version.revision)
693
645
    # user profile data transfer
694
646
 
695
647
    def xmlrpc_getUserProfile(self):
696
 
        """
697
 
        Return the user profile data for the current user.
698
 
        Use this in a single multicall after applyAuthToken.
699
 
        If we have a valid user, returns a dict of items from user profile.
700
 
        Otherwise, return an empty dict.
 
648
        """ Return the user profile data for the current user.
 
649
            Use this in a single multicall after applyAuthToken.
 
650
            If we have a valid user, returns a dict of items from user profile.
 
651
            Otherwise, return an empty dict.
701
652
        """
702
653
        u = self.request.user
703
654
        if not u.valid:
707
658
        return userdata
708
659
 
709
660
    def xmlrpc_getUserLanguageByJID(self, jid):
710
 
        """
711
 
        Returns user's language given his/her Jabber ID
 
661
        """ Returns user's language given his/her Jabber ID
712
662
 
713
663
        It makes no sense to consider this a secret, right? Therefore
714
664
        an authentication token is not required. We return a default
726
676
    # authorization methods
727
677
 
728
678
    def xmlrpc_getAuthToken(self, username, password, *args):
729
 
        """
730
 
        Returns a token which can be used for authentication
731
 
        in other XMLRPC calls. If the token is empty, the username
732
 
        or the password were wrong.
733
 
 
734
 
        Implementation note: token is same as cookie content would be for http session
735
 
        """
736
 
        request = self.request
737
 
        request.session = request.cfg.session_service.get_session(request)
738
 
 
739
 
        u = auth.setup_from_session(request, request.session)
740
 
        u = auth.handle_login(request, u, username=username, password=password)
 
679
        """ Returns a token which can be used for authentication
 
680
            in other XMLRPC calls. If the token is empty, the username
 
681
            or the password were wrong.
 
682
        """
 
683
        id_handler = XmlRpcAuthTokenIDHandler()
 
684
 
 
685
        u = self.request.cfg.session_handler.start(self.request, id_handler)
 
686
        u = self.request.handle_auth(u, username=username,
 
687
                                     password=password, login=True)
 
688
 
 
689
        self.request.cfg.session_handler.after_auth(self.request, id_handler, u)
741
690
 
742
691
        if u and u.valid:
743
 
            request.user = u
744
 
            request.cfg.session_service.finalize(request, request.session)
745
 
            return request.session.sid
 
692
            return id_handler.token
746
693
        else:
747
694
            return ""
748
695
 
749
696
    def xmlrpc_getJabberAuthToken(self, jid, secret):
750
 
        """
751
 
        Returns a token which can be used for authentication.
 
697
        """Returns a token which can be used for authentication.
752
698
 
753
699
        This token can be used in other XMLRPC calls. Generation of
754
700
        token depends on user's JID and a secret shared between wiki
756
702
 
757
703
        @param jid: a bare Jabber ID
758
704
        """
759
 
        if self.cfg.secrets['jabberbot'] != secret:
760
 
            logging.warning("getJabberAuthToken: got wrong secret %r" % secret)
 
705
        if self.cfg.secret != secret:
761
706
            return ""
762
707
 
763
 
        request = self.request
764
 
        request.session = request.cfg.session_service.get_session(request)
765
 
        logging.debug("getJabberAuthToken: got session %r" % request.session)
766
 
 
767
 
        u = user.get_by_jabber_id(request, jid) # XXX is someone talking to use from a jid we have stored in
768
 
                                                # XXX some user profile enough to authenticate him as that user?
769
 
        logging.debug("getJabberAuthToken: got user %r" % u)
 
708
        u = self.request.handle_jid_auth(jid)
770
709
 
771
710
        if u and u.valid:
772
 
            u.auth_method = 'moin' # XXX fake 'moin' login so the check for known login methods succeeds
773
 
                                   # XXX if not patched, u.auth_method is 'internal', but that is not accepted either
774
 
                                   # TODO this should be done more cleanly, somehow
775
 
            request.user = u
776
 
            request.cfg.session_service.finalize(request, request.session)
777
 
            logging.debug("getJabberAuthToken: returning sid %r" % request.session.sid)
778
 
            return request.session.sid
 
711
            return self._generate_auth_token(u)
779
712
        else:
780
713
            return ""
781
714
 
782
715
    def xmlrpc_applyAuthToken(self, auth_token):
783
 
        """
784
 
        Applies the auth token and thereby authenticates the user.
785
 
        """
 
716
        """ Applies the auth token and thereby authenticates the user. """
786
717
        if not auth_token:
787
718
            return xmlrpclib.Fault("INVALID", "Empty token.")
788
719
 
789
 
        request = self.request
790
 
        request.session = request.cfg.session_service.get_session(request, auth_token)
791
 
        logging.debug("applyAuthToken: got session %r" % request.session)
792
 
        u = auth.setup_from_session(request, request.session)
793
 
        logging.debug("applyAuthToken: got user %r" % u)
 
720
        id_handler = XmlRpcAuthTokenIDHandler(auth_token)
794
721
 
 
722
        u = self.request.cfg.session_handler.start(self.request, id_handler)
 
723
        u = self.request.handle_auth(u)
 
724
        self.request.cfg.session_handler.after_auth(self.request, id_handler, u)
795
725
        if u and u.valid:
796
726
            self.request.user = u
797
727
            return "SUCCESS"
800
730
 
801
731
 
802
732
    def xmlrpc_deleteAuthToken(self, auth_token):
803
 
        """
804
 
        Delete the given auth token.
805
 
        """
806
 
        if not auth_token:
807
 
            return xmlrpclib.Fault("INVALID", "Empty token.")
808
 
 
809
 
        request = self.request
810
 
        request.session = request.cfg.session_service.get_session(request, auth_token)
811
 
        request.cfg.session_service.destroy_session(request, request.session)
 
733
        """ Delete the given auth token. """
 
734
        id_handler = XmlRpcAuthTokenIDHandler(auth_token)
 
735
 
 
736
        u = self.request.cfg.session_handler.start(self.request, id_handler)
 
737
        u = self.request.handle_auth(u)
 
738
        self.request.cfg.session_handler.after_auth(self.request, id_handler, u)
 
739
 
 
740
        self.request.session.delete()
812
741
 
813
742
        return "SUCCESS"
814
743
 
816
745
    # methods for wiki synchronization
817
746
 
818
747
    def xmlrpc_getDiff(self, pagename, from_rev, to_rev, n_name=None):
819
 
        """
820
 
        Gets the binary difference between two page revisions.
821
 
 
822
 
        @param pagename: unicode string qualifying the page name
823
 
 
824
 
        @param fromRev: integer specifying the source revision. May be None to
825
 
        refer to a virtual empty revision which leads to a diff
826
 
        containing the whole page.
827
 
 
828
 
        @param toRev: integer specifying the target revision. May be None to
829
 
        refer to the current revision. If the current revision is the same
830
 
        as fromRev, there will be a special error condition "ALREADY_CURRENT"
831
 
 
832
 
        @param n_name: do a tag check verifying that n_name was the normalised
833
 
        name of the last tag
834
 
 
835
 
        If both fromRev and toRev are None, this function acts similar to getPage, i.e. it will diff("",currentRev).
836
 
 
837
 
        @return: Returns a dict:
838
 
        * status (not a field, implicit, returned as Fault if not SUCCESS):
839
 
        * "SUCCESS" - if the diff could be retrieved successfully
840
 
        * "NOT_EXIST" - item does not exist
841
 
        * "FROMREV_INVALID" - the source revision is invalid
842
 
        * "TOREV_INVALID" - the target revision is invalid
843
 
        * "INTERNAL_ERROR" - there was an internal error
844
 
        * "INVALID_TAG" - the last tag does not match the supplied normalised name
845
 
        * "ALREADY_CURRENT" - this not merely an error condition. It rather means that
846
 
         there is no new revision to diff against which is a good thing while
847
 
         synchronisation.
848
 
        * current: the revision number of the current revision (not the one which was diff'ed against)
849
 
        * diff: Binary object that transports a zlib-compressed binary diff (see bdiff.py, taken from Mercurial)
850
 
        * conflict: if there is a conflict on the page currently
 
748
        """ Gets the binary difference between two page revisions.
 
749
 
 
750
            @param pagename: unicode string qualifying the page name
 
751
 
 
752
            @param fromRev: integer specifying the source revision. May be None to
 
753
            refer to a virtual empty revision which leads to a diff
 
754
            containing the whole page.
 
755
 
 
756
            @param toRev: integer specifying the target revision. May be None to
 
757
            refer to the current revision. If the current revision is the same
 
758
            as fromRev, there will be a special error condition "ALREADY_CURRENT"
 
759
 
 
760
            @param n_name: do a tag check verifying that n_name was the normalised
 
761
            name of the last tag
 
762
 
 
763
            If both fromRev and toRev are None, this function acts similar to getPage, i.e. it will diff("",currentRev).
 
764
 
 
765
            @return Returns a dict:
 
766
            * status (not a field, implicit, returned as Fault if not SUCCESS):
 
767
             * "SUCCESS" - if the diff could be retrieved successfully
 
768
             * "NOT_EXIST" - item does not exist
 
769
             * "FROMREV_INVALID" - the source revision is invalid
 
770
             * "TOREV_INVALID" - the target revision is invalid
 
771
             * "INTERNAL_ERROR" - there was an internal error
 
772
             * "INVALID_TAG" - the last tag does not match the supplied normalised name
 
773
             * "ALREADY_CURRENT" - this not merely an error condition. It rather means that
 
774
             there is no new revision to diff against which is a good thing while
 
775
             synchronisation.
 
776
            * current: the revision number of the current revision (not the one which was diff'ed against)
 
777
            * diff: Binary object that transports a zlib-compressed binary diff (see bdiff.py, taken from Mercurial)
 
778
            * conflict: if there is a conflict on the page currently
 
779
 
851
780
        """
852
781
        from MoinMoin.util.bdiff import textdiff, compress
853
782
        from MoinMoin.wikisync import TagStore
912
841
        return {"conflict": conflict, "diff": diffblob, "diffversion": 1, "current": currentpage.get_real_rev()}
913
842
 
914
843
    def xmlrpc_interwikiName(self):
915
 
        """
916
 
        Returns the interwiki name and the IWID of the current wiki.
917
 
        """
 
844
        """ Returns the interwiki name and the IWID of the current wiki. """
918
845
        name = self.request.cfg.interwikiname
919
846
        iwid = self.request.cfg.iwid
920
847
        if name is None:
923
850
            return [self._outstr(name), iwid]
924
851
 
925
852
    def xmlrpc_mergeDiff(self, pagename, diff, local_rev, delta_remote_rev, last_remote_rev, interwiki_name, normalised_name):
926
 
        """
927
 
        Merges a diff sent by the remote machine and returns the number of the new revision.
928
 
        Additionally, this method tags the new revision.
929
 
 
930
 
        @param pagename: The pagename that is currently dealt with.
931
 
        @param diff: The diff that can be applied to the version specified by delta_remote_rev.
932
 
            If it is None, the page is deleted.
933
 
        @param local_rev: The revno of the page on the other wiki system, used for the tag.
934
 
        @param delta_remote_rev: The revno that the diff is taken against.
935
 
        @param last_remote_rev: The last revno of the page `pagename` that is known by the other wiki site.
936
 
        @param interwiki_name: Used to build the interwiki tag.
937
 
        @param normalised_name: The normalised pagename that is common to both wikis.
938
 
 
939
 
        @return: Returns the current revision number after the merge was done. Or one of the following errors:
940
 
            * "SUCCESS" - the page could be merged and tagged successfully.
941
 
            * "NOT_EXIST" - item does not exist and there was not any content supplied.
942
 
            * "LASTREV_INVALID" - the page was changed and the revision got invalid
943
 
            * "INTERNAL_ERROR" - there was an internal error
944
 
            * "NOT_ALLOWED" - you are not allowed to do the merge operation on the page
 
853
        """ Merges a diff sent by the remote machine and returns the number of the new revision.
 
854
            Additionally, this method tags the new revision.
 
855
 
 
856
            @param pagename: The pagename that is currently dealt with.
 
857
            @param diff: The diff that can be applied to the version specified by delta_remote_rev.
 
858
                If it is None, the page is deleted.
 
859
            @param local_rev: The revno of the page on the other wiki system, used for the tag.
 
860
            @param delta_remote_rev: The revno that the diff is taken against.
 
861
            @param last_remote_rev: The last revno of the page `pagename` that is known by the other wiki site.
 
862
            @param interwiki_name: Used to build the interwiki tag.
 
863
            @param normalised_name: The normalised pagename that is common to both wikis.
 
864
 
 
865
            @return Returns the current revision number after the merge was done. Or one of the following errors:
 
866
                * "SUCCESS" - the page could be merged and tagged successfully.
 
867
                * "NOT_EXIST" - item does not exist and there was not any content supplied.
 
868
                * "LASTREV_INVALID" - the page was changed and the revision got invalid
 
869
                * "INTERNAL_ERROR" - there was an internal error
 
870
                * "NOT_ALLOWED" - you are not allowed to do the merge operation on the page
945
871
        """
946
872
        from MoinMoin.util.bdiff import decompress, patch
947
873
        from MoinMoin.wikisync import TagStore, BOTH
1008
934
    # If the first beta or more stable release of 1.6 will have new item semantics,
1009
935
    # we will remove the functions before it is released.
1010
936
    def xmlrpc_listAttachments(self, pagename):
1011
 
        """
1012
 
        Get list of attachment names for page <pagename>.
 
937
        """ Get all attachments associated with pagename
1013
938
        Deprecated.
1014
939
 
1015
940
        @param pagename: pagename (utf-8)
1025
950
        return result
1026
951
 
1027
952
    def xmlrpc_getAttachment(self, pagename, attachname):
1028
 
        """
1029
 
        Get contents of attachment <attachname> of page <pagename>
 
953
        """ Get attachname associated with pagename
1030
954
 
1031
955
        @param pagename: pagename (utf-8)
1032
956
        @param attachname: attachment name (utf-8)
1033
 
        @rtype: base64
1034
 
        @return: base64 data
 
957
        @rtype base64
 
958
        @return base64 data
1035
959
        """
1036
960
        pagename = self._instr(pagename)
1037
961
        # User may read page?
1038
962
        if not self.request.user.may.read(pagename):
1039
963
            return self.notAllowedFault()
1040
964
 
1041
 
        attachname = wikiutil.taintfilename(self._instr(attachname))
1042
 
        filename = AttachFile.getFilename(self.request, pagename, attachname)
 
965
        filename = wikiutil.taintfilename(self._instr(attachname))
 
966
        filename = AttachFile.getFilename(self.request, pagename, filename)
1043
967
        if not os.path.isfile(filename):
1044
968
            return self.noSuchPageFault()
1045
969
        return self._outlob(open(filename, 'rb').read())
1046
970
 
1047
971
    def xmlrpc_putAttachment(self, pagename, attachname, data):
1048
 
        """
1049
 
        Store <data> as content of attachment <attachname> of page <pagename>.
 
972
        """ Set attachname associated with pagename to data
1050
973
 
1051
974
        @param pagename: pagename (utf-8)
1052
975
        @param attachname: attachment name (utf-8)
1053
976
        @param data: file data (base64)
1054
 
        @rtype: bool
1055
 
        @return: True if attachment was successfully stored
 
977
        @rtype boolean
 
978
        @return True if attachment was set
1056
979
        """
1057
980
        pagename = self._instr(pagename)
1058
981
        # User may read page?
1063
986
        if not self.request.user.may.write(pagename):
1064
987
            return xmlrpclib.Fault(1, "You are not allowed to edit this page")
1065
988
 
1066
 
        attachname = wikiutil.taintfilename(self._instr(attachname))
 
989
        attachname = wikiutil.taintfilename(attachname)
1067
990
        filename = AttachFile.getFilename(self.request, pagename, attachname)
1068
991
        if os.path.exists(filename) and not os.path.isfile(filename):
1069
992
            return self.noSuchPageFault()
1070
993
        open(filename, 'wb+').write(data.data)
1071
 
        AttachFile._addLogEntry(self.request, 'ATTNEW', pagename, attachname)
1072
 
        return xmlrpclib.Boolean(1)
1073
 
 
1074
 
    def xmlrpc_deleteAttachment(self, pagename, attachname):
1075
 
        """
1076
 
        Deletes attachment <attachname> of page <pagename>.
1077
 
 
1078
 
        @param pagename: pagename (utf-8)
1079
 
        @param attachname: attachment name (utf-8)
1080
 
        @rtype: bool
1081
 
        @return: True on success
1082
 
        """
1083
 
        pagename = self._instr(pagename)
1084
 
 
1085
 
        if not self.request.user.may.delete(pagename):
1086
 
            return xmlrpclib.Fault(1, 'You are not allowed to delete attachments on this page.')
1087
 
 
1088
 
        attachname = wikiutil.taintfilename(self._instr(attachname))
1089
 
        filename = AttachFile.getFilename(self.request, pagename, attachname)
1090
 
        AttachFile.remove_attachment(self.request, pagename, attachname)
 
994
        AttachFile._addLogEntry(self.request, 'ATTNEW', pagename, filename)
1091
995
        return xmlrpclib.Boolean(1)
1092
996
 
1093
997
    # XXX END WARNING XXX
1094
998
 
 
999
 
1095
1000
    def xmlrpc_getBotTranslations(self):
1096
 
        """
1097
 
        Return translations to be used by notification bot
 
1001
        """ Return translations to be used by notification bot
1098
1002
 
1099
1003
        @return: a dict (indexed by language) of dicts of translated strings (indexed by original ones)
1100
1004
        """
1109
1013
        self.version = 1
1110
1014
 
1111
1015
    def _instr(self, text):
1112
 
        """
1113
 
        Convert string we get from xmlrpc into internal representation
 
1016
        """ Convert string we get from xmlrpc into internal representation
1114
1017
 
1115
1018
        @param text: quoted text (str or unicode object)
1116
1019
        @rtype: unicode
1119
1022
        return wikiutil.url_unquote(text) # config.charset must be utf-8
1120
1023
 
1121
1024
    def _outstr(self, text):
1122
 
        """
1123
 
        Convert string from internal representation to xmlrpc
 
1025
        """ Convert string from internal representation to xmlrpc
1124
1026
 
1125
1027
        @param text: unicode or string in config.charset
1126
1028
        @rtype: str
1136
1038
        self.version = 2
1137
1039
 
1138
1040
    def _instr(self, text):
1139
 
        """
1140
 
        Convert string we get from xmlrpc into internal representation
 
1041
        """ Convert string we get from xmlrpc into internal representation
1141
1042
 
1142
1043
        @param text: unicode or utf-8 string
1143
1044
        @rtype: unicode
1148
1049
        return text
1149
1050
 
1150
1051
    def _outstr(self, text):
1151
 
        """
1152
 
        Convert string from internal representation to xmlrpc
 
1052
        """ Convert string from internal representation to xmlrpc
1153
1053
 
1154
1054
        @param text: unicode or string in config.charset
1155
1055
        @rtype: str
1163
1063
 
1164
1064
 
1165
1065
def xmlrpc(request):
1166
 
    return XmlRpc1(request).process()
 
1066
    XmlRpc1(request).process()
 
1067
 
1167
1068
 
1168
1069
def xmlrpc2(request):
1169
 
    return XmlRpc2(request).process()
 
1070
    XmlRpc2(request).process()
1170
1071