예제 #1
0
    def readPassword(self):
        """
        Read passwords from a file.

        DO NOT FORGET TO REMOVE READ ACCESS FOR OTHER USERS!!!
        Use chmod 600 password-file.

        All lines below should be valid Python tuples in the form
        (code, family, username, password),
        (family, username, password) or
        (username, password)
        to set a default password for an username. The last matching entry will
        be used, so default usernames should occur above specific usernames.

        The file must be either encoded in ASCII or UTF-8.

        Example:

        (u"my_username", u"my_default_password")
        (u"my_sysop_user", u"my_sysop_password")
        (u"wikipedia", u"my_wikipedia_user", u"my_wikipedia_pass")
        (u"en", u"wikipedia", u"my_en_wikipedia_user", u"my_en_wikipedia_pass")
        """
        # We fix password file permission first,
        # lift upper permission (regular file) from st_mode
        # to compare it with private_files_permission.
        if os.stat(config.password_file).st_mode - stat.S_IFREG \
                != config.private_files_permission:
            os.chmod(config.password_file, config.private_files_permission)

        password_f = codecs.open(config.password_file, encoding='utf-8')
        for line_nr, line in enumerate(password_f):
            if not line.strip():
                continue
            try:
                entry = eval(line)
            except SyntaxError:
                entry = None
            if type(entry) is not tuple:
                warn('Invalid tuple in line {0}'.format(line_nr),
                     _PasswordFileWarning)
                continue
            if not 2 <= len(entry) <= 4:
                warn(
                    'The length of tuple in line {0} should be 2 to 4 ({1} '
                    'given)'.format(line_nr, entry), _PasswordFileWarning)
                continue

            # When the tuple is inverted the default family and code can be
            # easily appended which makes the next condition easier as it does
            # not need to know if it's using the default value or not.
            entry = list(entry[::-1]) + [
                self.site.family.name, self.site.code
            ][len(entry) - 2:]

            if (normalize_username(entry[1]) == self.username
                    and entry[2] == self.site.family.name
                    and entry[3] == self.site.code):
                self.password = entry[0]
        password_f.close()
예제 #2
0
    def readPassword(self):
        """
        Read passwords from a file.

        DO NOT FORGET TO REMOVE READ ACCESS FOR OTHER USERS!!!
        Use chmod 600 password-file.

        All lines below should be valid Python tuples in the form
        (code, family, username, password),
        (family, username, password) or
        (username, password)
        to set a default password for an username. The last matching entry will
        be used, so default usernames should occur above specific usernames.

        The file must be either encoded in ASCII or UTF-8.

        Example:

        (u"my_username", u"my_default_password")
        (u"my_sysop_user", u"my_sysop_password")
        (u"wikipedia", u"my_wikipedia_user", u"my_wikipedia_pass")
        (u"en", u"wikipedia", u"my_en_wikipedia_user", u"my_en_wikipedia_pass")
        """
        # We fix password file permission first,
        # lift upper permission (regular file) from st_mode
        # to compare it with private_files_permission.
        if os.stat(config.password_file).st_mode - stat.S_IFREG \
                != config.private_files_permission:
            os.chmod(config.password_file, config.private_files_permission)

        password_f = codecs.open(config.password_file, encoding='utf-8')
        for line_nr, line in enumerate(password_f):
            if not line.strip():
                continue
            try:
                entry = eval(line)
            except SyntaxError:
                entry = None
            if type(entry) is not tuple:
                warn('Invalid tuple in line {0}'.format(line_nr),
                     _PasswordFileWarning)
                continue
            if not 2 <= len(entry) <= 4:
                warn('The length of tuple in line {0} should be 2 to 4 ({1} '
                     'given)'.format(line_nr, entry), _PasswordFileWarning)
                continue

            # When the tuple is inverted the default family and code can be
            # easily appended which makes the next condition easier as it does
            # not need to know if it's using the default value or not.
            entry = list(entry[::-1]) + [self.site.family.name,
                                         self.site.code][len(entry) - 2:]

            if (normalize_username(entry[1]) == self.username and
                    entry[2] == self.site.family.name and
                    entry[3] == self.site.code):
                self.password = entry[0]
        password_f.close()
예제 #3
0
def Site(code=None, fam=None, user=None, sysop=None, interface=None, url=None):
    """A factory method to obtain a Site object.

    Site objects are cached and reused by this method.

    By default rely on config settings. These defaults may all be overridden
    using the method parameters.

    @param code: language code (override config.mylang)
    @type code: str
    @param fam: family name or object (override config.family)
    @type fam: str or Family
    @param user: bot user name to use on this site (override config.usernames)
    @type user: str
    @param sysop: sysop user to use on this site (override config.sysopnames)
    @type sysop: str
    @param interface: site class or name of class in pywikibot.site
        (override config.site_interface)
    @type interface: subclass of L{pywikibot.site.BaseSite} or string
    @param url: Instead of code and fam, does try to get a Site based on the
        URL. Still requires that the family supporting that URL exists.
    @type url: str
    @rtype: pywikibot.site.APISite
    @raises ValueError: URL and pair of code and family given
    @raises ValueError: Invalid interface name
    @raises SiteDefinitionError: Unknown URL
    """
    _logger = 'wiki'

    if url:
        # Either code and fam or only url
        if code or fam:
            raise ValueError(
                'URL to the wiki OR a pair of code and family name '
                'should be provided')
        code, fam = _code_fam_from_url(url)
    else:
        # Fallback to config defaults
        code = code or config.mylang
        fam = fam or config.family

        if not isinstance(fam, Family):
            fam = Family.load(fam)

    interface = interface or fam.interface(code)

    # config.usernames is initialised with a defaultdict for each family name
    family_name = str(fam)

    code_to_user = config.usernames['*'].copy()
    code_to_user.update(config.usernames[family_name])
    user = user or code_to_user.get(code) or code_to_user.get('*')

    code_to_sysop = config.sysopnames['*'].copy()
    code_to_sysop.update(config.sysopnames[family_name])
    sysop = sysop or code_to_sysop.get(code) or code_to_sysop.get('*')

    if not isinstance(interface, type):
        # If it isn't a class, assume it is a string
        if PY2:  # Must not be unicode in Python 2
            interface = str(interface)
        try:
            tmp = __import__('pywikibot.site', fromlist=[interface])
        except ImportError:
            raise ValueError('Invalid interface name: {0}'.format(interface))
        else:
            interface = getattr(tmp, interface)

    if not issubclass(interface, BaseSite):
        warning('Site called with interface=%s' % interface.__name__)

    user = normalize_username(user)
    key = '%s:%s:%s:%s' % (interface.__name__, fam, code, user)
    if key not in _sites or not isinstance(_sites[key], interface):
        _sites[key] = interface(code=code, fam=fam, user=user, sysop=sysop)
        debug("Instantiated %s object '%s'"
              % (interface.__name__, _sites[key]), _logger)

        if _sites[key].code != code:
            warn('Site %s instantiated using different code "%s"'
                 % (_sites[key], code), UserWarning, 2)

    return _sites[key]
예제 #4
0
def Site(code=None, fam=None, user=None, sysop=None, interface=None, url=None):
    """A factory method to obtain a Site object.

    Site objects are cached and reused by this method.

    By default rely on config settings. These defaults may all be overridden
    using the method parameters.

    @param code: language code (override config.mylang)
    @type code: string
    @param fam: family name or object (override config.family)
    @type fam: string or Family
    @param user: bot user name to use on this site (override config.usernames)
    @type user: unicode
    @param sysop: sysop user to use on this site (override config.sysopnames)
    @type sysop: unicode
    @param interface: site class or name of class in pywikibot.site
        (override config.site_interface)
    @type interface: subclass of L{pywikibot.site.BaseSite} or string
    @param url: Instead of code and fam, does try to get a Site based on the
        URL. Still requires that the family supporting that URL exists.
    @type url: string
    @rtype: pywikibot.site.APISite

    """
    # Either code and fam or only url
    if url and (code or fam):
        raise ValueError('URL to the wiki OR a pair of code and family name '
                         'should be provided')
    _logger = "wiki"

    if url:
        if url not in _url_cache:
            matched_sites = []
            # Iterate through all families and look, which does apply to
            # the given URL
            for fam in config.family_files:
                family = Family.load(fam)
                code = family.from_url(url)
                if code is not None:
                    matched_sites += [(code, family)]

            if matched_sites:
                if len(matched_sites) > 1:
                    warning(
                        'Found multiple matches for URL "{0}": {1} (use first)'
                        .format(url, ', '.join(str(s) for s in matched_sites)))
                _url_cache[url] = matched_sites[0]
            else:
                # TODO: As soon as AutoFamily is ready, try and use an
                #       AutoFamily
                _url_cache[url] = None

        cached = _url_cache[url]
        if cached:
            code = cached[0]
            fam = cached[1]
        else:
            raise SiteDefinitionError("Unknown URL '{0}'.".format(url))
    else:
        # Fallback to config defaults
        code = code or config.mylang
        fam = fam or config.family

        if not isinstance(fam, Family):
            fam = Family.load(fam)

    interface = interface or fam.interface(code)

    # config.usernames is initialised with a defaultdict for each family name
    family_name = str(fam)

    code_to_user = config.usernames['*'].copy()
    code_to_user.update(config.usernames[family_name])
    user = user or code_to_user.get(code) or code_to_user.get('*')

    code_to_sysop = config.sysopnames['*'].copy()
    code_to_sysop.update(config.sysopnames[family_name])
    sysop = sysop or code_to_sysop.get(code) or code_to_sysop.get('*')

    if not isinstance(interface, type):
        # If it isnt a class, assume it is a string
        try:
            tmp = __import__('pywikibot.site', fromlist=[interface])
            interface = getattr(tmp, interface)
        except ImportError:
            raise ValueError('Invalid interface name: {0}'.format(interface))

    if not issubclass(interface, BaseSite):
        warning('Site called with interface=%s' % interface.__name__)

    user = normalize_username(user)
    key = '%s:%s:%s:%s' % (interface.__name__, fam, code, user)
    if key not in _sites or not isinstance(_sites[key], interface):
        _sites[key] = interface(code=code, fam=fam, user=user, sysop=sysop)
        debug(u"Instantiated %s object '%s'"
              % (interface.__name__, _sites[key]), _logger)

        if _sites[key].code != code:
            warn('Site %s instantiated using different code "%s"'
                 % (_sites[key], code), UserWarning, 2)

    return _sites[key]
예제 #5
0
    def readPassword(self):
        """
        Read passwords from a file.

        DO NOT FORGET TO REMOVE READ ACCESS FOR OTHER USERS!!!
        Use chmod 600 password-file.

        All lines below should be valid Python tuples in the form
        (code, family, username, password),
        (family, username, password) or
        (username, password)
        to set a default password for an username. The last matching entry will
        be used, so default usernames should occur above specific usernames.

        For BotPasswords the password should be given as a BotPassword object.

        The file must be either encoded in ASCII or UTF-8.

        Example::

         ('my_username', 'my_default_password')
         ('my_sysop_user', 'my_sysop_password')
         ('wikipedia', 'my_wikipedia_user', 'my_wikipedia_pass')
         ('en', 'wikipedia', 'my_en_wikipedia_user', 'my_en_wikipedia_pass')
         ('my_username', BotPassword(
          'my_BotPassword_suffix', 'my_BotPassword_password'))
        """
        # Set path to password file relative to the user_config
        # but fall back on absolute path for backwards compatibility
        password_file = os.path.join(config.base_dir, config.password_file)
        if not os.path.isfile(password_file):
            password_file = config.password_file

        # We fix password file permission first.
        file_mode_checker(password_file, mode=config.private_files_permission)

        with codecs.open(password_file, encoding='utf-8') as f:
            lines = f.readlines()
        line_nr = len(lines) + 1
        for line in reversed(lines):
            line_nr -= 1
            if not line.strip() or line.startswith('#'):
                continue
            try:
                entry = eval(line)
            except SyntaxError:
                entry = None
            if type(entry) is not tuple:
                warn('Invalid tuple in line {0}'.format(line_nr),
                     _PasswordFileWarning)
                continue
            if not 2 <= len(entry) <= 4:
                warn(
                    'The length of tuple in line {0} should be 2 to 4 ({1} '
                    'given)'.format(line_nr, entry), _PasswordFileWarning)
                continue

            code, family, username, password = (
                self.site.code, self.site.family.name)[:4 - len(entry)] + entry
            if (normalize_username(username) == self.username
                    and family == self.site.family.name
                    and code == self.site.code):
                if isinstance(password, UnicodeType):
                    self.password = password
                    break
                elif isinstance(password, BotPassword):
                    self.password = password.password
                    self.login_name = password.login_name(self.username)
                    break
                else:
                    warn('Invalid password format', _PasswordFileWarning)
예제 #6
0
def Site(code: Optional[str] = None,
         fam=None,
         user: Optional[str] = None,
         *,
         interface=None,
         url: Optional[str] = None) -> Union[APISite, DataSite, ClosedSite]:
    """A factory method to obtain a Site object.

    Site objects are cached and reused by this method.

    By default rely on config settings. These defaults may all be overridden
    using the method parameters.

    @param code: language code (override config.mylang)
        code may also be a sitename like 'wikipedia:test'
    @param fam: family name or object (override config.family)
    @type fam: str or pywikibot.family.Family
    @param user: bot user name to use on this site (override config.usernames)
    @param interface: site class or name of class in pywikibot.site
        (override config.site_interface)
    @type interface: subclass of L{pywikibot.site.BaseSite} or string
    @param url: Instead of code and fam, does try to get a Site based on the
        URL. Still requires that the family supporting that URL exists.
    @raises ValueError: URL and pair of code and family given
    @raises ValueError: Invalid interface name
    """
    _logger = 'wiki'

    if url:
        # Either code and fam or url with optional fam for AutoFamily name
        if code:
            raise ValueError(
                'URL to the wiki OR a pair of code and family name '
                'should be provided')
        code, fam = _code_fam_from_url(url, fam)
    elif code and ':' in code:
        if fam:
            raise ValueError('sitename OR a pair of code and family name '
                             'should be provided')
        fam, _, code = code.partition(':')
    else:
        # Fallback to config defaults
        code = code or config.mylang
        fam = fam or config.family

    if not isinstance(fam, Family):
        fam = Family.load(fam)

    interface = interface or fam.interface(code)

    # config.usernames is initialised with a defaultdict for each family name
    family_name = str(fam)

    code_to_user = {}
    if '*' in config.usernames:  # T253127: usernames is a defaultdict
        code_to_user = config.usernames['*'].copy()
    code_to_user.update(config.usernames[family_name])
    user = user or code_to_user.get(code) or code_to_user.get('*')

    if not isinstance(interface, type):
        # If it isn't a class, assume it is a string
        try:
            tmp = __import__('pywikibot.site', fromlist=[interface])
        except ImportError:
            raise ValueError('Invalid interface name: {0}'.format(interface))
        else:
            interface = getattr(tmp, interface)

    if not issubclass(interface, BaseSite):
        warning('Site called with interface=%s' % interface.__name__)

    user = normalize_username(user)
    key = '%s:%s:%s:%s' % (interface.__name__, fam, code, user)
    if key not in _sites or not isinstance(_sites[key], interface):
        _sites[key] = interface(code=code, fam=fam, user=user)
        debug(
            "Instantiated %s object '%s'" % (interface.__name__, _sites[key]),
            _logger)

        if _sites[key].code != code:
            warn(
                'Site %s instantiated using different code "%s"' %
                (_sites[key], code), UserWarning, 2)

    return _sites[key]
예제 #7
0
def Site(code=None, fam=None, user=None, sysop=None, interface=None, url=None):
    """A factory method to obtain a Site object.

    Site objects are cached and reused by this method.

    By default rely on config settings. These defaults may all be overridden
    using the method parameters.

    @param code: language code (override config.mylang)
    @type code: string
    @param fam: family name or object (override config.family)
    @type fam: string or Family
    @param user: bot user name to use on this site (override config.usernames)
    @type user: unicode
    @param sysop: sysop user to use on this site (override config.sysopnames)
    @type sysop: unicode
    @param interface: site class or name of class in pywikibot.site
        (override config.site_interface)
    @type interface: subclass of L{pywikibot.site.BaseSite} or string
    @param url: Instead of code and fam, does try to get a Site based on the
        URL. Still requires that the family supporting that URL exists.
    @type url: string
    @rtype: pywikibot.site.APISite

    """
    # Either code and fam or only url
    if url and (code or fam):
        raise ValueError('URL to the wiki OR a pair of code and family name '
                         'should be provided')
    _logger = "wiki"

    if url:
        if url not in _url_cache:
            matched_sites = []
            # Iterate through all families and look, which does apply to
            # the given URL
            for fam in config.family_files:
                family = Family.load(fam)
                code = family.from_url(url)
                if code is not None:
                    matched_sites += [(code, family)]

            if matched_sites:
                if len(matched_sites) > 1:
                    warning(
                        'Found multiple matches for URL "{0}": {1} (use first)'
                        .format(url, ', '.join(str(s) for s in matched_sites)))
                _url_cache[url] = matched_sites[0]
            else:
                # TODO: As soon as AutoFamily is ready, try and use an
                #       AutoFamily
                _url_cache[url] = None

        cached = _url_cache[url]
        if cached:
            code = cached[0]
            fam = cached[1]
        else:
            raise SiteDefinitionError("Unknown URL '{0}'.".format(url))
    else:
        # Fallback to config defaults
        code = code or config.mylang
        fam = fam or config.family

        if not isinstance(fam, Family):
            fam = Family.load(fam)

    interface = interface or fam.interface(code)

    # config.usernames is initialised with a defaultdict for each family name
    family_name = str(fam)

    code_to_user = config.usernames['*'].copy()
    code_to_user.update(config.usernames[family_name])
    user = user or code_to_user.get(code) or code_to_user.get('*')

    code_to_sysop = config.sysopnames['*'].copy()
    code_to_sysop.update(config.sysopnames[family_name])
    sysop = sysop or code_to_sysop.get(code) or code_to_sysop.get('*')

    if not isinstance(interface, type):
        # If it isnt a class, assume it is a string
        try:
            tmp = __import__('pywikibot.site', fromlist=[interface])
            interface = getattr(tmp, interface)
        except ImportError:
            raise ValueError('Invalid interface name: {0}'.format(interface))

    if not issubclass(interface, BaseSite):
        warning('Site called with interface=%s' % interface.__name__)

    user = normalize_username(user)
    key = '%s:%s:%s:%s' % (interface.__name__, fam, code, user)
    if key not in _sites or not isinstance(_sites[key], interface):
        _sites[key] = interface(code=code, fam=fam, user=user, sysop=sysop)
        debug(
            u"Instantiated %s object '%s'" % (interface.__name__, _sites[key]),
            _logger)

        if _sites[key].code != code:
            warn(
                'Site %s instantiated using different code "%s"' %
                (_sites[key], code), UserWarning, 2)

    return _sites[key]
예제 #8
0
def Site(code: Optional[str] = None,
         fam: Union[str, 'Family', None] = None,
         user: Optional[str] = None,
         *,
         interface: Union[str, 'BaseSite', None] = None,
         url: Optional[str] = None) -> BaseSite:
    """A factory method to obtain a Site object.

    Site objects are cached and reused by this method.

    By default rely on config settings. These defaults may all be overridden
    using the method parameters.

    Creating the default site using config.mylang and config.family::

        site = pywikibot.Site()

    Override default site code::

        site = pywikibot.Site('fr')

    Override default family::

        site = pywikibot.Site(family='wikisource')

    Setting a specific site::

        site = pywikibot.Site('fr', 'wikisource')

    which is equal to::

        site = pywikibot.Site('wikisource:fr')

    :Note: An already created site is cached an a new variable points to
        the same object if interface, family, code and user are equal:

    >>> import pywikibot
    >>> site_1 = pywikibot.Site('wikisource:fr')
    >>> site_2 = pywikibot.Site('fr', 'wikisource')
    >>> site_1 is site_2
    True
    >>> site_1
    APISite("fr", "wikisource")

    ``APISite`` is the default interface. Refer :py:obj:`pywikibot.site` for
    other interface types.

    **Never create a site object via interface class directly.**
    Always use this factory method.

    :param code: language code (override config.mylang)
        code may also be a sitename like 'wikipedia:test'
    :param fam: family name or object (override config.family)
    :param user: bot user name to use on this site (override config.usernames)
    :param interface: site class or name of class in :py:obj:`pywikibot.site`
        (override config.site_interface)
    :param url: Instead of code and fam, does try to get a Site based on the
        URL. Still requires that the family supporting that URL exists.
    :raises ValueError: URL and pair of code and family given
    :raises ValueError: Invalid interface name
    :raises ValueError: Missing Site code
    :raises ValueError: Missing Site family
    """
    _logger = 'wiki'

    if url:
        # Either code and fam or url with optional fam for AutoFamily name
        if code:
            raise ValueError(
                'URL to the wiki OR a pair of code and family name '
                'should be provided')
        code, fam = _code_fam_from_url(url, fam)
    elif code and ':' in code:
        if fam:
            raise ValueError('sitename OR a pair of code and family name '
                             'should be provided')
        fam, _, code = code.partition(':')
    else:
        # Fallback to config defaults
        code = code or _config.mylang
        fam = fam or _config.family

    if not (code and fam):
        raise ValueError(
            'Missing Site {}'.format('code' if not code else 'family'))

    if not isinstance(fam, Family):
        fam = Family.load(fam)

    interface = interface or fam.interface(code)

    # config.usernames is initialised with a defaultdict for each family name
    family_name = str(fam)

    code_to_user = {}
    if '*' in _config.usernames:  # T253127: usernames is a defaultdict
        code_to_user = _config.usernames['*'].copy()
    code_to_user.update(_config.usernames[family_name])
    user = user or code_to_user.get(code) or code_to_user.get('*')

    if not isinstance(interface, type):
        # If it isn't a class, assume it is a string
        try:
            tmp = __import__('pywikibot.site', fromlist=[interface])
        except ImportError:
            raise ValueError('Invalid interface name: {}'.format(interface))
        else:
            interface = getattr(tmp, interface)

    if not issubclass(interface, BaseSite):
        warning('Site called with interface={}'.format(interface.__name__))

    user = normalize_username(user)
    key = '{}:{}:{}:{}'.format(interface.__name__, fam, code, user)
    if key not in _sites or not isinstance(_sites[key], interface):
        _sites[key] = interface(code=code, fam=fam, user=user)
        debug(
            "Instantiated {} object '{}'".format(interface.__name__,
                                                 _sites[key]), _logger)

        if _sites[key].code != code:
            warn(
                'Site {} instantiated using different code "{}"'.format(
                    _sites[key], code), UserWarning, 2)

    return _sites[key]
예제 #9
0
    def __init__(self, code: str, fam=None, user=None) -> None:
        """
        Initializer.

        @param code: the site's language code
        @type code: str
        @param fam: wiki family name (optional)
        @type fam: str or pywikibot.family.Family
        @param user: bot user name (optional)
        @type user: str
        """
        if code.lower() != code:
            # Note the Site function in __init__ also emits a UserWarning
            # for this condition, showing the callers file and line no.
            pywikibot.log('BaseSite: code "{}" converted to lowercase'
                          .format(code))
            code = code.lower()
        if not all(x in pywikibot.family.CODE_CHARACTERS for x in code):
            pywikibot.log('BaseSite: code "{}" contains invalid characters'
                          .format(code))
        self.__code = code
        if isinstance(fam, str) or fam is None:
            self.__family = pywikibot.family.Family.load(fam)
        else:
            self.__family = fam

        self.obsolete = False
        # if we got an outdated language code, use the new one instead.
        if self.__code in self.__family.obsolete:
            if self.__family.obsolete[self.__code] is not None:
                self.__code = self.__family.obsolete[self.__code]
                # Note the Site function in __init__ emits a UserWarning
                # for this condition, showing the callers file and line no.
                pywikibot.log('Site {} instantiated using aliases code of {}'
                              .format(self, code))
            else:
                # no such language anymore
                self.obsolete = True
                pywikibot.log('Site %s instantiated and marked "obsolete" '
                              'to prevent access' % self)
        elif self.__code not in self.languages():
            if self.__family.name in self.__family.langs and \
               len(self.__family.langs) == 1:
                self.__code = self.__family.name
                if self.__family == pywikibot.config.family \
                        and code == pywikibot.config.mylang:
                    pywikibot.config.mylang = self.__code
                    warn('Global configuration variable "mylang" changed to '
                         '"%s" while instantiating site %s'
                         % (self.__code, self), UserWarning)
            else:
                raise UnknownSite("Language '%s' does not exist in family %s"
                                  % (self.__code, self.__family.name))

        self._username = normalize_username(user)

        self.use_hard_category_redirects = (
            self.code in self.family.use_hard_category_redirects)

        # following are for use with lock_page and unlock_page methods
        self._pagemutex = threading.Condition()
        self._locked_pages = set()
예제 #10
0
    def readPassword(self):
        """
        Read passwords from a file.

        DO NOT FORGET TO REMOVE READ ACCESS FOR OTHER USERS!!!
        Use chmod 600 password-file.

        All lines below should be valid Python tuples in the form
        (code, family, username, password),
        (family, username, password) or
        (username, password)
        to set a default password for an username. The last matching entry will
        be used, so default usernames should occur above specific usernames.

        For BotPasswords the password should be given as a BotPassword object.

        The file must be either encoded in ASCII or UTF-8.

        Example::

         ('my_username', 'my_default_password')
         ('my_sysop_user', 'my_sysop_password')
         ('wikipedia', 'my_wikipedia_user', 'my_wikipedia_pass')
         ('en', 'wikipedia', 'my_en_wikipedia_user', 'my_en_wikipedia_pass')
         ('my_username', BotPassword(
          'my_BotPassword_suffix', 'my_BotPassword_password'))
        """
        # Set path to password file relative to the user_config
        # but fall back on absolute path for backwards compatibility
        password_file = os.path.join(config.base_dir, config.password_file)
        if not os.path.isfile(password_file):
            password_file = config.password_file

        # We fix password file permission first.
        file_mode_checker(password_file, mode=config.private_files_permission)

        with codecs.open(password_file, encoding='utf-8') as f:
            lines = f.readlines()
        line_nr = len(lines) + 1
        for line in reversed(lines):
            line_nr -= 1
            if not line.strip() or line.startswith('#'):
                continue
            try:
                entry = eval(line)
            except SyntaxError:
                entry = None
            if type(entry) is not tuple:
                warn('Invalid tuple in line {0}'.format(line_nr),
                     _PasswordFileWarning)
                continue
            if not 2 <= len(entry) <= 4:
                warn('The length of tuple in line {0} should be 2 to 4 ({1} '
                     'given)'.format(line_nr, entry), _PasswordFileWarning)
                continue

            code, family, username, password = (
                self.site.code, self.site.family.name)[:4 - len(entry)] + entry
            if (normalize_username(username) == self.username
                    and family == self.site.family.name
                    and code == self.site.code):
                if isinstance(password, UnicodeType):
                    self.password = password
                    break
                elif isinstance(password, BotPassword):
                    self.password = password.password
                    self.login_name = password.login_name(self.username)
                    break
                else:
                    warn('Invalid password format', _PasswordFileWarning)
    def readPassword(self):
        """
        Read passwords from a file.

        DO NOT FORGET TO REMOVE READ ACCESS FOR OTHER USERS!!!
        Use chmod 600 password-file.

        All lines below should be valid Python tuples in the form
        (code, family, username, password),
        (family, username, password) or
        (username, password)
        to set a default password for an username. The last matching entry will
        be used, so default usernames should occur above specific usernames.

        For BotPasswords the password should be given as a BotPassword object.

        The file must be either encoded in ASCII or UTF-8.

        Example:

        (u"my_username", u"my_default_password")
        (u"my_sysop_user", u"my_sysop_password")
        (u"wikipedia", u"my_wikipedia_user", u"my_wikipedia_pass")
        (u"en", u"wikipedia", u"my_en_wikipedia_user", u"my_en_wikipedia_pass")
        (u"my_username", BotPassword(u"my_BotPassword_suffix", u"my_BotPassword_password"))
        """
        # Set path to password file relative to the user_config
        # but fall back on absolute path for backwards compatibility
        password_file = os.path.join(config.base_dir, config.password_file)
        if not os.path.isfile(password_file):
            password_file = config.password_file

        # We fix password file permission first.
        file_mode_checker(password_file, mode=config.private_files_permission)

        password_f = codecs.open(password_file, encoding='utf-8')
        for line_nr, line in enumerate(password_f):
            if not line.strip():
                continue
            try:
                entry = eval(line)
            except SyntaxError:
                entry = None
            if type(entry) is not tuple:
                warn('Invalid tuple in line {0}'.format(line_nr),
                     _PasswordFileWarning)
                continue
            if not 2 <= len(entry) <= 4:
                warn(
                    'The length of tuple in line {0} should be 2 to 4 ({1} '
                    'given)'.format(line_nr, entry), _PasswordFileWarning)
                continue

            # When the tuple is inverted the default family and code can be
            # easily appended which makes the next condition easier as it does
            # not need to know if it's using the default value or not.
            entry = list(entry[::-1]) + [
                self.site.family.name, self.site.code
            ][len(entry) - 2:]

            if (normalize_username(entry[1]) == self.username
                    and entry[2] == self.site.family.name
                    and entry[3] == self.site.code):
                if isinstance(entry[0], basestring):
                    self.password = entry[0]
                elif isinstance(entry[0], BotPassword):
                    self.password = entry[0].password
                    self.login_name = entry[0].login_name(self.username)
                else:
                    warn('Invalid password format', _PasswordFileWarning)
        password_f.close()
예제 #12
0
def Site(code=None, fam=None, user=None, sysop=None, interface=None, url=None):
    """A factory method to obtain a Site object.

    Site objects are cached and reused by this method.

    By default rely on config settings. These defaults may all be overridden
    using the method parameters.

    @param code: language code (override config.mylang)
    @type code: str
    @param fam: family name or object (override config.family)
    @type fam: str or Family
    @param user: bot user name to use on this site (override config.usernames)
    @type user: str
    @param sysop: sysop user to use on this site (override config.sysopnames)
    @type sysop: str
    @param interface: site class or name of class in pywikibot.site
        (override config.site_interface)
    @type interface: subclass of L{pywikibot.site.BaseSite} or string
    @param url: Instead of code and fam, does try to get a Site based on the
        URL. Still requires that the family supporting that URL exists.
    @type url: str
    @rtype: pywikibot.site.APISite
    @raises ValueError: URL and pair of code and family given
    @raises ValueError: Invalid interface name
    @raises SiteDefinitionError: Unknown URL
    """
    _logger = 'wiki'

    if url:
        # Either code and fam or only url
        if code or fam:
            raise ValueError(
                'URL to the wiki OR a pair of code and family name '
                'should be provided')
        code, fam = _code_fam_from_url(url)
    else:
        # Fallback to config defaults
        code = code or config.mylang
        fam = fam or config.family

        if not isinstance(fam, Family):
            fam = Family.load(fam)

    interface = interface or fam.interface(code)

    # config.usernames is initialised with a defaultdict for each family name
    family_name = str(fam)

    code_to_user = config.usernames['*'].copy()
    code_to_user.update(config.usernames[family_name])
    user = user or code_to_user.get(code) or code_to_user.get('*')

    code_to_sysop = config.sysopnames['*'].copy()
    code_to_sysop.update(config.sysopnames[family_name])
    sysop = sysop or code_to_sysop.get(code) or code_to_sysop.get('*')

    if not isinstance(interface, type):
        # If it isn't a class, assume it is a string
        if PY2:  # Must not be unicode in Python 2
            interface = str(interface)
        try:
            tmp = __import__('pywikibot.site', fromlist=[interface])
        except ImportError:
            raise ValueError('Invalid interface name: {0}'.format(interface))
        else:
            interface = getattr(tmp, interface)

    if not issubclass(interface, BaseSite):
        warning('Site called with interface=%s' % interface.__name__)

    user = normalize_username(user)
    key = '%s:%s:%s:%s' % (interface.__name__, fam, code, user)
    if key not in _sites or not isinstance(_sites[key], interface):
        _sites[key] = interface(code=code, fam=fam, user=user, sysop=sysop)
        debug("Instantiated %s object '%s'"
              % (interface.__name__, _sites[key]), _logger)

        if _sites[key].code != code:
            warn('Site %s instantiated using different code "%s"'
                 % (_sites[key], code), UserWarning, 2)

    return _sites[key]
예제 #13
0
    def readPassword(self):
        """
        Read passwords from a file.

        DO NOT FORGET TO REMOVE READ ACCESS FOR OTHER USERS!!!
        Use chmod 600 password-file.

        All lines below should be valid Python tuples in the form
        (code, family, username, password),
        (family, username, password) or
        (username, password)
        to set a default password for an username. The last matching entry will
        be used, so default usernames should occur above specific usernames.

        For BotPasswords the password should be given as a BotPassword object.

        The file must be either encoded in ASCII or UTF-8.

        Example:

        (u"my_username", u"my_default_password")
        (u"my_sysop_user", u"my_sysop_password")
        (u"wikipedia", u"my_wikipedia_user", u"my_wikipedia_pass")
        (u"en", u"wikipedia", u"my_en_wikipedia_user", u"my_en_wikipedia_pass")
        (u"my_username", BotPassword(u"my_BotPassword_suffix", u"my_BotPassword_password"))
        """
        # Set path to password file relative to the user_config
        # but fall back on absolute path for backwards compatibility
        password_file = os.path.join(config.base_dir, config.password_file)
        if not os.path.isfile(password_file):
            password_file = config.password_file

        # We fix password file permission first.
        file_mode_checker(password_file, mode=config.private_files_permission)

        password_f = codecs.open(password_file, encoding='utf-8')
        for line_nr, line in enumerate(password_f):
            if not line.strip():
                continue
            try:
                entry = eval(line)
            except SyntaxError:
                entry = None
            if type(entry) is not tuple:
                warn('Invalid tuple in line {0}'.format(line_nr),
                     _PasswordFileWarning)
                continue
            if not 2 <= len(entry) <= 4:
                warn('The length of tuple in line {0} should be 2 to 4 ({1} '
                     'given)'.format(line_nr, entry), _PasswordFileWarning)
                continue

            # When the tuple is inverted the default family and code can be
            # easily appended which makes the next condition easier as it does
            # not need to know if it's using the default value or not.
            entry = list(entry[::-1]) + [self.site.family.name,
                                         self.site.code][len(entry) - 2:]

            if (normalize_username(entry[1]) == self.username and
                    entry[2] == self.site.family.name and
                    entry[3] == self.site.code):
                if isinstance(entry[0], basestring):
                    self.password = entry[0]
                elif isinstance(entry[0], BotPassword):
                    self.password = entry[0].password
                    self.login_name = entry[0].login_name(self.username)
                else:
                    warn('Invalid password format', _PasswordFileWarning)
        password_f.close()