for username, group, filename in extracted_info:
            if filename != '.' and filename != '..':
                parsed_url_set.add(domain_path.url_join(filename))
                users.add(username)
                groups.add(group)

        self.worker_pool.map(self.http_get_and_parse, parsed_url_set)

        if parsed_url_set:
            desc = 'A .listing file was found at: "%s". The contents'\
                   ' of this file disclose filenames.'
            desc = desc % (response.get_url())

            v = Vuln('.listing file found', desc, severity.LOW, response.id,
                     self.get_name())
            v.set_url(response.get_url())

            kb.kb.append(self, 'dot_listing', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())

        real_users = set([u for u in users if not u.isdigit()])
        real_groups = set([g for g in groups if not g.isdigit()])

        if real_users or real_groups:
            desc = 'A .listing file which leaks operating system usernames' \
                   ' and groups was identified at %s. The leaked users are %s,'\
                   ' and the groups are %s. This information can be used' \
                   ' during a bruteforce attack to the Web application,' \
                   ' SSH or FTP services.'
            desc = desc % (response.get_url(),
                           ', '.join(real_users),
                           ', '.join(real_groups))

            v = Vuln('Operating system username and group leak', desc,
                     severity.LOW, response.id, self.get_name())
            v.set_url(response.get_url())

            kb.kb.append(self, 'dot_listing', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())

"""
dot_listing.py

This file is part of w3af, http://example.com .

"""
import re

import w3af.core.controllers.output_manager as om
import w3af.core.data.kb.knowledge_base as kb
import w3af.core.data.constants.severity as severity

from w3af.core.controllers.plugins.crawl_plugin import CrawlPlugin
from w3af.core.controllers.exceptions import BaseFrameworkException
from w3af.core.controllers.core_helpers.fingerprint_404 import is_404
from w3af.core.data.bloomfilter.scalable_bloom import ScalableBloomFilter
from w3af.core.data.kb.vuln import Vuln
from w3af.core.data.request.fuzzable_request import FuzzableRequest

class dot_listing(CrawlPlugin):
    """
    Search for .listing files and extracts new filenames from it.
    :author: Tomas Velazquez ( example@nnthink.com )
    """
    regex_str = '[a-z-]{10}\s*\d+\s*(.*?)\s+(.*?)\s+\d+\s+\w+\s+\d+\s+[0-9:]{4,5}\s+(.*)'
    LISTING_PARSER_RE = re.compile(regex_str)

    def __init__(self):
        CrawlPlugin.__init__(self)

        # Internal variables
        self._analyzed_dirs = ScalableBloomFilter()

    def crawl(self, fuzzable_request):
        """
        For every directory, fetch the .listing file and analyze the response.

        :param fuzzable_request: A fuzzable_request instance that contains
                                    (among other things) the URL to test.
        """
        for domain_path in fuzzable_request.get_url().get_directories():
            if domain_path not in self._analyzed_dirs:
                self._analyzed_dirs.add(domain_path)
                self._check_and_analyze(domain_path)

    def _check_and_analyze(self, domain_path):
        """
        Check if a .listing filename exists in the domain_path.
        :return: None, everything is saved to the self.out_queue.
        """
        # Request the file
        url = domain_path.url_join('.listing')
        try:
            response = self._uri_opener.GET(url, cache=True)
        except BaseFrameworkException, w3:
            msg = 'Failed to GET .listing file: "%s". Exception: "%s".'
            om.out.debug(msg % (url, w3))
            return

        # Check if it's a .listing file
        if is_404(response):
            return

        fr = FuzzableRequest(response.get_url())
        self.output_queue.put(fr)

        parsed_url_set = set()
        users = set()
        groups = set()

        extracted_info = self._extract_info_from_listing(response.get_body())

    def _extract_info_from_listing(self, listing_file_content):
        """
        Extract info from .listing file content, each line looks like:

        -rw-r--r--    1 andresr   w3af         8139 Apr 12 13:23 foo.zip

        We're going to extract "andresr" (user), "w3af" (group) and "foo.zip"
        (file).

        :return: A list with the information extracted from the listing_file_content
        """
        for user, group, filename in self.LISTING_PARSER_RE.findall(listing_file_content):
            yield user, group, filename.strip()

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """
        This plugin searches for the .listing file in all the directories and
        subdirectories that are sent as input and if found it will try to
        discover new URLs from its content. The .listing file holds information
        about the list of files in the current directory. These files are created
        when download files from FTP with command "wget" and argument "-m" or
        "--no-remove-listing". For example, if the input is:
            - http://host.tld/w3af/index.php

        The plugin will perform these requests:
            - http://host.tld/w3af/.listing
            - http://host.tld/.listing

        """


"""
dot_listing.py

This file is part of w3af, http://example.com .

"""
import re

import w3af.core.controllers.output_manager as om
import w3af.core.data.kb.knowledge_base as kb
import w3af.core.data.constants.severity as severity

from w3af.core.controllers.plugins.crawl_plugin import CrawlPlugin
from w3af.core.controllers.exceptions import BaseFrameworkException
from w3af.core.controllers.core_helpers.fingerprint_404 import is_404
from w3af.core.data.bloomfilter.scalable_bloom import ScalableBloomFilter
from w3af.core.data.kb.vuln import Vuln
from w3af.core.data.request.fuzzable_request import FuzzableRequest

class dot_listing(CrawlPlugin):
    """
    Search for .listing files and extracts new filenames from it.
    :author: Tomas Velazquez ( example@nnthink.com )
    """
    regex_str = '[a-z-]{10}\s*\d+\s*(.*?)\s+(.*?)\s+\d+\s+\w+\s+\d+\s+[0-9:]{4,5}\s+(.*)'
    LISTING_PARSER_RE = re.compile(regex_str)

    def __init__(self):
        CrawlPlugin.__init__(self)

        # Internal variables
        self._analyzed_dirs = ScalableBloomFilter()

    def crawl(self, fuzzable_request):
        """
        For every directory, fetch the .listing file and analyze the response.

        :param fuzzable_request: A fuzzable_request instance that contains
                                    (among other things) the URL to test.
        """
        for domain_path in fuzzable_request.get_url().get_directories():
            if domain_path not in self._analyzed_dirs:
                self._analyzed_dirs.add(domain_path)
                self._check_and_analyze(domain_path)

    def _check_and_analyze(self, domain_path):
        """
        Check if a .listing filename exists in the domain_path.
        :return: None, everything is saved to the self.out_queue.
        """
        # Request the file
        url = do

        try:
            response = self._uri_opener.GET(url, cache=True)
        except BaseFrameworkException, w3:
            msg = 'Failed to GET .listing file: "%s". Exception: "%s".'
            om.out.debug(msg % (url, w3))
            return

        # Check if it's a .listing file
        if is_404(response):
            return

        fr = FuzzableRequest(response.get_url())
        self.output_queue.put(fr)

        parsed_url_set = set()
        users = set()
        groups = set()

        extracted_info = self._extract_info_from_listing(response.get_body())
        for username, group, filename in extracted_info:
            if filename != '.' and filename != '..':
                parsed_url_set.add(domain_path.url_join(filename))
                users.add(username)
                groups.add(group)

        self.worker_pool.map(self.http_get_and_parse, parsed_url_set)

        if parsed_url_set:
            desc = 'A .listing file was found at: "%s". The contents'\
                   ' of this file disclose filenames.'
            desc = desc % (response.get_url())

            v = Vuln('.listing file found', desc, severity.LOW, response.id,
                     self.get_name())
            v.set_url(response.get_url())

            kb.kb.append(self, 'dot_listing', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())

        real_users = set([u for u in users if not u.isdigit()])
        real_groups = set([g for g in groups if not g.isdigit()])

        if real_users or real_groups:
            desc = 'A .listing file which leaks operating system usernames' \
                   ' and groups was identified at %s. The leaked users are %s,'\
                   ' and the groups are %s. This information can be used' \
                   ' during a bruteforce attack to the Web application,' \
                   ' SSH or FTP services.'
            desc = desc % (response.get_url(),
                           ', '.join(real_users),
                           ', '.join(real_groups))

            v = Vuln('Operating system username and group leak', desc,
                     severity.LOW, response.id, self.get_name())
            v.set_url(response.get_url())

            kb.kb.append(self, 'dot_listing', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())

    def _extract_info_from_listing(self, listing_file_content):
        """
        Extract info from .listing file content, each line looks like:

        -rw-r--r--    1 andresr   w3af         8139 Apr 12 13:23 foo.zip

        We're going to extract "andresr" (user), "w3af" (group) and "foo.zip"
        (file).

        :return: A list with the information extracted from the listing_file_content
        """
        for user, group, filename in self.LISTING_PARSER_RE.findall(listing_file_content):
            yield user, group, filename.strip()

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """
        This plugin searches for the .listing file in all the directories and
        subdirectories that are sent as input and if found it will try to
        discover new URLs from its content. The .listing file holds information
        about the list of files in the current directory. These files are created
        when download files from FTP with command "wget" and argument "-m" or
        "--no-remove-listing". For example, if the input is:
            - http://host.tld/w3af/index.php

        The plugin will perform these requests:
            - http://host.tld/w3af/.listing
            - http://host.tld/.listing

        """


        for user, group, filename in self.LISTING_PARSER_RE.findall(listing_file_content):
            yield user, group, filename.strip()

"""
dot_listing.py

This file is part of w3af, http://example.com .

"""
import re

import w3af.core.controllers.output_manager as om
import w3af.core.data.kb.knowledge_base as kb
import w3af.core.data.constants.severity as severity

from w3af.core.controllers.plugins.crawl_plugin import CrawlPlugin
from w3af.core.controllers.exceptions import BaseFrameworkException
from w3af.core.controllers.core_helpers.fingerprint_404 import is_404
from w3af.core.data.bloomfilter.scalable_bloom import ScalableBloomFilter
from w3af.core.data.kb.vuln import Vuln
from w3af.core.data.request.fuzzable_request import FuzzableRequest

class dot_listing(CrawlPlugin):
    """
    Search for .listing files and extracts new filenames from it.
    :author: Tomas Velazquez ( example@nnthink.com )
    """
    regex_str = '[a-z-]{10}\s*\d+\s*(.*?)\s+(.*?)\s+\d+\s+\w+\s+\d+\s+[0-9:]{4,5}\s+(.*)'
    LISTING_PARSER_RE = re.compile(regex_str)

    def __init__(self):
        CrawlPlugin.__init__(self)

        # Internal variables
        self._analyzed_dirs = ScalableBloomFilter()

    def crawl(self, fuzzable_request):
        """
        For every directory, fetch the .listing file and analyze the response.

        :param fuzzable_request: A fuzzable_request instance that contains
                                    (among other things) the URL to test.
        """
        for domain_path in fuzzable_request.get_url().get_directories():
            if domain_path not in self._analyzed_dirs:
                self._analyzed_dirs.add(domain_path)
                self._check_and_analyze(domain_path)

    def _check_and_analyze(self, domain_path):
        """
        Check if a .listing filename exists in the domain_path.
        :return: None, everything is saved to the self.out_queue.
        """
        # Request the file
        url = domain_path.url_join('.listing')
        try:
            response = self._uri_opener.GET(url, cache=True)
        except BaseFrameworkException, w3:
            msg = 'Failed to GET .listing file: "%s". Exception: "%s".'
            om.out.debug(msg % (url, w3))
            return

        # Check if it's a .listing file
        if is_404(response):
            return

        fr = FuzzableRequest(response.get_url())
        self.output_queue.put(fr)

        parsed_url_set = set()
        users = set()
        groups = set()

        extracted_info = self._extract_info_from_listing(response.get_body())
        for username, group, filename in extracted_info:
            if filename != '.' and filename != '..':
                parsed_url_set.add(domain_path.url_join(filename))
                users.add(username)
                groups.add(group)

        self.worker_pool.map(self.http_get_and_parse, parsed_url_set)

        if parsed_url_set:
            desc = 'A .listing file was found at: "%s". The contents'\
                   ' of this file disclose filenames.'
            desc = desc % (response.get_url())

            v = Vuln('.listing file found', desc, severity.LOW, response.id,
                     self.get_name())
            v.set_url(response.get_url())

            kb.kb.append(self, 'dot_listing', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())

        real_users = set([u for u in users if not u.isdigit()])
        real_groups = set([g for g in groups if not g.isdigit()])

        if real_users or real_groups:
            desc = 'A .listing file which leaks operating system usernames' \
                   ' and groups was identified at %s. The leaked users are %s,'\
                   ' and the groups are %s. This information can be used' \
                   ' during a bruteforce attack to the Web application,' \
                   ' SSH or FTP services.'
            desc = desc % (response.get_url(),
                           ', '.join(real_users),
                           ', '.join(real_groups))

            v = Vuln('Operating system username and group leak', desc,
                     severity.LOW, response.id, self.get_name())
            v.set_url(response.get_url())

            kb.kb.append(self, 'dot_listing', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())

    def _extract_info_from_listing(self, listing_file_content):
        """
        Extract info from .listing file content, each line looks like:

        -rw-r--r--    1 andresr   w3af         8139 Apr 12 13:23 foo.zip

        We're going to extract "andresr" (user), "w3af" (group) and "foo.zip"
        (file).

        :return: A list with the information extracted from the listing_file_content
        """

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """
        This plugin searches for the .listing file in all the directories and
        subdirectories that are sent as input and if found it will try to
        discover new URLs from its content. The .listing file holds information
        about the list of files in the current directory. These files are created
        when download files from FTP with command "wget" and argument "-m" or
        "--no-remove-listing". For example, if the input is:
            - http://host.tld/w3af/index.php

        The plugin will perform these requests:
            - http://host.tld/w3af/.listing
            - http://host.tld/.listing

        """


if domain_path not in self._analyzed_dirs:
                self._analyzed_dirs.add(domain_path)
                self._check_and_analyze(domain_path)

"""
dot_listing.py

This file is part of w3af, http://example.com .

"""
import re

import w3af.core.controllers.output_manager as om
import w3af.core.data.kb.knowledge_base as kb
import w3af.core.data.constants.severity as severity

from w3af.core.controllers.plugins.crawl_plugin import CrawlPlugin
from w3af.core.controllers.exceptions import BaseFrameworkException
from w3af.core.controllers.core_helpers.fingerprint_404 import is_404
from w3af.core.data.bloomfilter.scalable_bloom import ScalableBloomFilter
from w3af.core.data.kb.vuln import Vuln
from w3af.core.data.request.fuzzable_request import FuzzableRequest

class dot_listing(CrawlPlugin):
    """
    Search for .listing files and extracts new filenames from it.
    :author: Tomas Velazquez ( example@nnthink.com )
    """
    regex_str = '[a-z-]{10}\s*\d+\s*(.*?)\s+(.*?)\s+\d+\s+\w+\s+\d+\s+[0-9:]{4,5}\s+(.*)'
    LISTING_PARSER_RE = re.compile(regex_str)

    def __init__(self):
        CrawlPlugin.__init__(self)

        # Internal variables
        self._analyzed_dirs = ScalableBloomFilter()

    def crawl(self, fuzzable_request):
        """
        For every directory, fetch the .listing file and analyze the response.

        :param fuzzable_request: A fuzzable_request instance that contains
                                    (among other things) the URL to test.
        """
        for domain_path in fuzzable_request.get_url().get_directories():

    def _check_and_analyze(self, domain_path):
        """
        Check if a .listing filename exists in the domain_path.
        :return: None, everything is saved to the self.out_queue.
        """
        # Request the file
        url = domain_path.url_join('.listing')
        try:
            response = self._uri_opener.GET(url, cache=True)
        except BaseFrameworkException, w3:
            msg = 'Failed to GET .listing file: "%s". Exception: "%s".'
            om.out.debug(msg % (url, w3))
            return

        # Check if it's a .listing file
        if is_404(response):
            return

        fr = FuzzableRequest(response.get_url())
        self.output_queue.put(fr)

        parsed_url_set = set()
        users = set()
        groups = set()

        extracted_info = self._extract_info_from_listing(response.get_body())
        for username, group, filename in extracted_info:
            if filename != '.' and filename != '..':
                parsed_url_set.add(domain_path.url_join(filename))
                users.add(username)
                groups.add(group)

        self.worker_pool.map(self.http_get_and_parse, parsed_url_set)

        if parsed_url_set:
            desc = 'A .listing file was found at: "%s". The contents'\
                   ' of this file disclose filenames.'
            desc = desc % (response.get_url())

            v = Vuln('.listing file found', desc, severity.LOW, response.id,
                     self.get_name())
            v.set_url(response.get_url())

            kb.kb.append(self, 'dot_listing', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())

        real_users = set([u for u in users if not u.isdigit()])
        real_groups = set([g for g in groups if not g.isdigit()])

        if real_users or real_groups:
            desc = 'A .listing file which leaks operating system usernames' \
                   ' and groups was identified at %s. The leaked users are %s,'\
                   ' and the groups are %s. This information can be used' \
                   ' during a bruteforce attack to the Web application,' \
                   ' SSH or FTP services.'
            desc = desc % (response.get_url(),
                           ', '.join(real_users),
                           ', '.join(real_groups))

            v = Vuln('Operating system username and group leak', desc,
                     severity.LOW, response.id, self.get_name())
            v.set_url(response.get_url())

            kb.kb.append(self, 'dot_listing', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())

    def _extract_info_from_listing(self, listing_file_content):
        """
        Extract info from .listing file content, each line looks like:

        -rw-r--r--    1 andresr   w3af         8139 Apr 12 13:23 foo.zip

        We're going to extract "andresr" (user), "w3af" (group) and "foo.zip"
        (file).

        :return: A list with the information extracted from the listing_file_content
        """
        for user, group, filename in self.LISTING_PARSER_RE.findall(listing_file_content):
            yield user, group, filename.strip()

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """
        This plugin searches for the .listing file in all the directories and
        subdirectories that are sent as input and if found it will try to
        discover new URLs from its content. The .listing file holds information
        about the list of files in the current directory. These files are created
        when download files from FTP with command "wget" and argument "-m" or
        "--no-remove-listing". For example, if the input is:
            - http://host.tld/w3af/index.php

        The plugin will perform these requests:
            - http://host.tld/w3af/.listing
            - http://host.tld/.listing

        """


for user, group, filename in self.LISTING_PARSER_RE.findall(listing_file_content):
            yield user, group, filename.strip()


try:
            response = self._uri_opener.GET(url, cache=True)
        except BaseFrameworkException, w3:
            msg = 'Failed to GET .listing file: "%s". Exception: "%s".'
            om.out.debug(msg % (url, w3))
            return

"""
dot_listing.py

This file is part of w3af, http://example.com .

"""
import re

import w3af.core.controllers.output_manager as om
import w3af.core.data.kb.knowledge_base as kb
import w3af.core.data.constants.severity as severity

from w3af.core.controllers.plugins.crawl_plugin import CrawlPlugin
from w3af.core.controllers.exceptions import BaseFrameworkException
from w3af.core.controllers.core_helpers.fingerprint_404 import is_404
from w3af.core.data.bloomfilter.scalable_bloom import ScalableBloomFilter
from w3af.core.data.kb.vuln import Vuln
from w3af.core.data.request.fuzzable_request import FuzzableRequest

class dot_listing(CrawlPlugin):
    """
    Search for .listing files and extracts new filenames from it.
    :author: Tomas Velazquez ( example@nnthink.com )
    """
    regex_str = '[a-z-]{10}\s*\d+\s*(.*?)\s+(.*?)\s+\d+\s+\w+\s+\d+\s+[0-9:]{4,5}\s+(.*)'
    LISTING_PARSER_RE = re.compile(regex_str)

    def __init__(self):
        CrawlPlugin.__init__(self)

        # Internal variables
        self._analyzed_dirs = ScalableBloomFilter()

    def crawl(self, fuzzable_request):
        """
        For every directory, fetch the .listing file and analyze the response.

        :param fuzzable_request: A fuzzable_request instance that contains
                                    (among other things) the URL to test.
        """
        for domain_path in fuzzable_request.get_url().get_directories():
            if domain_path not in self._analyzed_dirs:
                self._analyzed_dirs.add(domain_path)
                self._check_and_analyze(domain_path)

    def _check_and_analyze(self, domain_path):
        """
        Check if a .listing filename exists in the domain_path.
        :return: None, everything is saved to the self.out_queue.
        """
        # Request the file
        url = domain_path.url_join('.listing')

        # Check if it's a .listing file
        if is_404(response):
            return

        fr = FuzzableRequest(response.get_url())
        self.output_queue.put(fr)

        parsed_url_set = set()
        users = set()
        groups = set()

        extracted_info = self._extract_info_from_listing(response.get_body())
        for username, group, filename in extracted_info:
            if filename != '.' and filename != '..':
                parsed_url_set.add(domain_path.url_join(filename))
                users.add(username)
                groups.add(group)

        self.worker_pool.map(self.http_get_and_parse, parsed_url_set)

        if parsed_url_set:
            desc = 'A .listing file was found at: "%s". The contents'\
                   ' of this file disclose filenames.'
            desc = desc % (response.get_url())

            v = Vuln('.listing file found', desc, severity.LOW, response.id,
                     self.get_name())
            v.set_url(response.get_url())

            kb.kb.append(self, 'dot_listing', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())

        real_users = set([u for u in users if not u.isdigit()])
        real_groups = set([g for g in groups if not g.isdigit()])

        if real_users or real_groups:
            desc = 'A .listing file which leaks operating system usernames' \
                   ' and groups was identified at %s. The leaked users are %s,'\
                   ' and the groups are %s. This information can be used' \
                   ' during a bruteforce attack to the Web application,' \
                   ' SSH or FTP services.'
            desc = desc % (response.get_url(),
                           ', '.join(real_users),
                           ', '.join(real_groups))

            v = Vuln('Operating system username and group leak', desc,
                     severity.LOW, response.id, self.get_name())
            v.set_url(response.get_url())

            kb.kb.append(self, 'dot_listing', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())

    def _extract_info_from_listing(self, listing_file_content):
        """
        Extract info from .listing file content, each line looks like:

        -rw-r--r--    1 andresr   w3af         8139 Apr 12 13:23 foo.zip

        We're going to extract "andresr" (user), "w3af" (group) and "foo.zip"
        (file).

        :return: A list with the information extracted from the listing_file_content
        """
        for user, group, filename in self.LISTING_PARSER_RE.findall(listing_file_content):
            yield user, group, filename.strip()

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """
        This plugin searches for the .listing file in all the directories and
        subdirectories that are sent as input and if found it will try to
        discover new URLs from its content. The .listing file holds information
        about the list of files in the current directory. These files are created
        when download files from FTP with command "wget" and argument "-m" or
        "--no-remove-listing". For example, if the input is:
            - http://host.tld/w3af/index.php

        The plugin will perform these requests:
            - http://host.tld/w3af/.listing
            - http://host.tld/.listing

        """


"""
dot_listing.py

This file is part of w3af, http://example.com .

"""
import re

import w3af.core.controllers.output_manager as om
import w3af.core.data.kb.knowledge_base as kb
import w3af.core.data.constants.severity as severity

from w3af.core.controllers.plugins.crawl_plugin import CrawlPlugin
from w3af.core.controllers.exceptions import BaseFrameworkException
from w3af.core.controllers.core_helpers.fingerprint_404 import is_404
from w3af.core.data.bloomfilter.scalable_bloom import ScalableBloomFilter
from w3af.core.data.kb.vuln import Vuln
from w3af.core.data.request.fuzzable_request import FuzzableRequest

class dot_listing(CrawlPlugin):
    """
    Search for .listing files and extracts new filenames from it.
    :author: Tomas Velazquez ( example@nnthink.com )
    """
    regex_str = '[a-z-]{10}\s

*\d+\s*(.*?)\s+(.*?)\s+\d+\s+\w+\s+\d+\s+[0-9:]{4,5}\s+(.*)'
    LISTING_PARSER_RE = re.compile(regex_str)

    def __init__(self):
        CrawlPlugin.__init__(self)

        # Internal variables
        self._analyzed_dirs = ScalableBloomFilter()

    def crawl(self, fuzzable_request):
        """
        For every directory, fetch the .listing file and analyze the response.

        :param fuzzable_request: A fuzzable_request instance that contains
                                    (among other things) the URL to test.
        """
        for domain_path in fuzzable_request.get_url().get_directories():
            if domain_path not in self._analyzed_dirs:
                self._analyzed_dirs.add(domain_path)
                self._check_and_analyze(domain_path)

    def _check_and_analyze(self, domain_path):
        """
        Check if a .listing filename exists in the domain_path.
        :return: None, everything is saved to the self.out_queue.
        """
        # Request the file
        url = domain_path.url_join('.listing')
        try:
            response = self._uri_opener.GET(url, cache=True)
        except BaseFrameworkException, w3:
            msg = 'Failed to GET .listing file: "%s". Exception: "%s".'
            om.out.debug(msg % (url, w3))
            return

        # Check if it's a .listing file
        if is_404(response):
            return

        fr = FuzzableRequest(response.get_url())
        self.output_queue.put(fr)

        parsed_url_set = set()
        users = set()
        groups = set()

        extracted_info = self._extract_info_from_listing(response.get_body())
        for username, group, filename in extracted_info:
            if filename != '.' and filename != '..':
                parsed_url_set.add(domain_path.url_join(filename))
                users.add(username)
                groups.add(group)

        self.worker_pool.map(self.http_get_and_parse, parsed_url_set)

        if parsed_url_set:
            desc = 'A .listing file was found at: "%s". The contents'\
                   ' of this file disclose filenames.'
            desc = desc % (response.get_url())

            v = Vuln('.listing file found', desc, severity.LOW, response.id,
                     self.get_name())
            v.set_url(response.get_url())

            kb.kb.append(self, 'dot_listing', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())

        real_users = set([u for u in users if not u.isdigit()])
        real_groups = set([g for g in groups if not g.isdigit()])

        if real_users or real_groups:
            desc = 'A .listing file which leaks operating system usernames' \
                   ' and groups was identified at %s. The leaked users are %s,'\
                   ' and the groups are %s. This information can be used' \
                   ' during a bruteforce attack to the Web application,' \
                   ' SSH or FTP services.'
            desc = desc % (response.get_url(),
                           ', '.join(real_users),
                           ', '.join(real_groups))

            v = Vuln('Operating system username and group leak', desc,
                     severity.LOW, response.id, self.get_name())
            v.set_url(response.get_url())

            kb.kb.append(self, 'dot_listing', v)
            om.out.vulnerability(v.get_desc(), severity=v.get_severity())

    def _extract_info_from_listing(self, listing_file_content):
        """
        Extract info from .listing file content, each line looks like:

        -rw-r--r--    1 andresr   w3af         8139 Apr 12 13:23 foo.zip

        We're going to extract "andresr" (user), "w3af" (group) and "foo.zip"
        (file).

        :return: A list with the information extracted from the listing_file_content
        """
        for user, group, filename in self.LISTING_PARSER_RE.findall(listing_file_content):
            yield user, group, filename.strip()

    def get_long_desc(self):
        """
        :return: A DETAILED description of the plugin functions and features.
        """
        return """
        This plugin searches for the .listing file in all the directories and
        subdirectories that are sent as input and if found it will try to
        discover new URLs from its content. The .listing file holds information
        about the list of files in the current directory. These files are created
        when download files from FTP with command "wget" and argument "-m" or
        "--no-remove-listing". For example, if the input is:
            - http://host.tld/w3af/index.php

        The plugin will perform these requests:
            - http://host.tld/w3af/.listing
            - http://host.tld/.listing

        """


import bpy
from bpy.props import (BoolProperty, StringProperty, FloatProperty, IntProperty)

from sverchok.utils.sv_obj_helper import SvObjHelper
from sverchok.utils.geom import multiply_vectors
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import dataCorrect, fullList, updateNode

def set_bevel_object(node, cu, obj_index):
    # use bevel object if provided
    bevel_objs = node.inputs['bevel object'].sv_get(default=[])
    if bevel_objs:
        obj_ref = bevel_objs[obj_index] if obj_index < len(bevel_objs) else bevel_objs[-1]
        if obj_ref.type == 'CURVE':
            cu.bevel_object = obj_ref
            cu.use_fill_caps = node.caps
    else:
        cu.bevel_object = None
        cu.use_fill_caps = False

def live_curve(obj_index, node, verts, radii, twist):

    obj, cu = node.get_obj_curve(obj_index)

    obj.show_wire = node.show_wire
    cu.bevel_depth = node.depth
    cu.bevel_resolution = node.resolution
    cu.dimensions = node.curve_dimensions
    if cu.dimensions == '2D':
        cu.fill_mode = 'FRONT'
    else:
        cu.fill_mode = 'FULL'

    set_bevel_object(node, cu, obj_index)

    kind = ["POLY", "NURBS"][bool(node.bspline)]

    if node.selected_mode == 'Multi':
        verts = [verts]
        radii = [radii]
        twist = [twist]

    for idx, (VERTS, RADII, TWIST) in enumerate(zip(verts, radii, twist)):

        full_flat = []
        for v in VERTS:
            full_flat.extend([v[0], v[1], v[2], 1.0])

        polyline = cu.splines.new(kind)
        polyline.points.add(len(VERTS)-1)
        polyline.points.foreach_set('co', full_flat)

        if RADII:
            if len(VERTS) < len(RADII):
                RADII = RADII[:len(VERTS)]
            elif len(VERTS) > len(RADII):
                fullList(RADII, len(VERTS))
            polyline.points.foreach_set('radius', RADII)

        if TWIST:
            if len(VERTS) < len(TWIST):
                TWIST = TWIST[:len(VERTS)]
            elif len(VERTS) > len(TWIST):
                fullList(TWIST, len(VERTS))
            polyline.points.foreach_set('tilt', TWIST)

        if node.close:
            cu.splines[idx].use_cyclic_u = True

        if node.bspline:
            polyline.order_u = len(polyline.points)-1

        polyline.use_smooth = node.use_smooth

    return obj

def make_curve_geometry(obj_index, node, verts, matrix, radii, twist):
    sv_object = live_curve(obj_index, node, verts, radii, twist)
    sv_object.hide_select = False
    node.push_custom_matrix_if_present(sv_object, matrix)
    return sv_object

class SvPolylineViewerNodeV28(bpy.types.Node, SverchCustomTreeNode, SvObjHelper):

    bl_idname = 'SvPolylineViewerNodeV28'
    bl_label = 'Polyline Viewer'
    bl_icon = 'MOD_CURVE'

    mode_options = [(k, k, '', i) for i, k in enumerate(["Multi", "Single"])]
    selected_mode: bpy.props.EnumProperty(
        items=mode_options,
        description="offers joined of unique curves",
        default="Multi", update=updateNode
    )

    dimension_modes = [(k, k, '', i) for i, k in enumerate(["3D", "2D"])]

    curve_dimensions: bpy.props.EnumProperty(
        items=dimension_modes, update=updateNode,
        description="2D or 3D curves", default="3D"
    )

    depth: FloatProperty(min=0.0, default=0.2, update=updateNode)
    resolution: IntProperty(min=0, default=3, update=updateNode)
    bspline: BoolProperty(default=False, update=updateNode)
    close: BoolProperty(default=False, update=updateNode)

    radii: FloatProperty(min=0, default=0.2, update=updateNode)
    twist: FloatProperty(default=0.0, update=updateNode)
    caps: BoolProperty(update=updateNode)

    data_kind: StringProperty(default='CURVE')

    def sv_init(self, context):
        self.sv_init_helper_basedata_name()

        self.inputs.new('VerticesSocket', 'vertices')
        self.inputs.new('MatrixSocket', 'matrix')
        self.inputs.new('StringsSocket', 'radii').prop_name = 'radii'
        self.inputs.new('StringsSocket', 'twist').prop_name = 'twist'
        self.inputs.new('SvObjectSocket', 'bevel object')
        self.outputs.new('SvObjectSocket', "object")

    def draw_buttons(self, context, layout):

        self.draw_live_and_outliner(context, layout)
        self.draw_object_buttons(context, layout)

        layout.row().prop(self, 'curve_dimensions', expand=True)

        col = layout.column()
        if self.curve_dimensions == '3D':
            r1 = col.row(align=True)
            r1.prop(self, 'depth', text='radius')
            r1.prop(self, 'resolution', text='subdiv')
        row = col.row(align=True)
        row.prop(self, 'bspline', text='bspline', toggle=True)
        row.prop(self, 'close', text='close', toggle=True)
        if self.inputs['bevel object'].sv_get(default=[]):
            row.prop(self, 'caps', text='caps', toggle=True)
        row = col.row(align=True)
        row.prop(self, 'show_wire', text='wire', toggle=True)
        row.prop(self, 'use_smooth', text='smooth', toggle=True)
        row.separator()
        row.prop(self, 'selected_mode', expand=True)

    def draw_buttons_ext(self, context, layout):
        self.draw_buttons(context, layout)
        self.draw_ext_object_buttons(context, layout)

    def get_geometry_from_sockets(self, has_matrices):

data = self.inputs[socket_name].sv_get(default=[])
            return dataCorrect(data)

        mverts = get('vertices')
        mradii = self.inputs['radii'].sv_get(deepcopy=True)
        mtwist = self.inputs['twist'].sv_get(deepcopy=True)
        mmtrix = get('matrix')

        # extend all non empty lists to longest of these
        maxlen = max(len(mverts), len(mmtrix))
        if has_matrices:
            fullList(mverts, maxlen)
            fullList(mmtrix, maxlen)

        if mradii:
            fullList(mradii, maxlen)
        if mtwist:
            fullList(mtwist, maxlen)

        return mverts, mradii, mtwist, mmtrix

    def process(self):
        if not self.activate:
            return

        if not self.inputs['vertices'].is_linked:
            return

        has_matrices = self.inputs['matrix'].is_linked
        mverts, mradii, mtwist, mmatrices = self.get_geometry_from_sockets(has_matrices)

        out_objects = []
        for obj_index, Verts in enumerate(mverts):
            if not Verts:
                continue

            matrix = mmatrices[obj_index] if has_matrices else []

            if self.selected_mode == 'Multi':
                curve_args = obj_index, self, Verts, matrix, mradii[obj_index], mtwist[obj_index]
                new_obj = make_curve_geometry(*curve_args)
                out_objects.append(new_obj)
            else:
                if matrix:
                    mverts = [multiply_vectors(*mv) for mv in zip(mmatrices, mverts)]
                new_obj = make_curve_geometry(0, self, mverts, [], mradii, mtwist)
                out_objects.append(new_obj)
                break

        last_index = len(mverts) - 1
        self.remove_non_updated_objects(last_index)
        self.set_corresponding_materials()

        self.outputs['object'].sv_set(out_objects)

def register():
    bpy.utils.register_class(SvPolylineViewerNodeV28)

def unregister():
    bpy.utils.unregister_class(SvPolylineViewerNodeV28)


self.sv_init_helper_basedata_name()

        self.inputs.new('VerticesSocket', 'vertices')
        self.inputs.new('MatrixSocket', 'matrix')
        self.inputs.new('StringsSocket', 'radii').prop_name = 'radii'
        self.inputs.new('StringsSocket', 'twist').prop_name = 'twist'
        self.inputs.new('SvObjectSocket', 'bevel object')
        self.outputs.new('SvObjectSocket', "object")

import bpy
from bpy.props import (BoolProperty, StringProperty, FloatProperty, IntProperty)

from sverchok.utils.sv_obj_helper import SvObjHelper
from sverchok.utils.geom import multiply_vectors
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import dataCorrect, fullList, updateNode

def set_bevel_object(node, cu, obj_index):
    # use bevel object if provided
    bevel_objs = node.inputs['bevel object'].sv_get(default=[])
    if bevel_objs:
        obj_ref = bevel_objs[obj_index] if obj_index < len(bevel_objs) else bevel_objs[-1]
        if obj_ref.type == 'CURVE':
            cu.bevel_object = obj_ref
            cu.use_fill_caps = node.caps
    else:
        cu.bevel_object = None
        cu.use_fill_caps = False

def live_curve(obj_index, node, verts, radii, twist):

    obj, cu = node.get_obj_curve(obj_index)

    obj.show_wire = node.show_wire
    cu.bevel_depth = node.depth
    cu.bevel_resolution = node.resolution
    cu.dimensions = node.curve_dimensions
    if cu.dimensions == '2D':
        cu.fill_mode = 'FRONT'
    else:
        cu.fill_mode = 'FULL'

    set_bevel_object(node, cu, obj_index)

    kind = ["POLY", "NURBS"][bool(node.bspline)]

    if node.selected_mode == 'Multi':
        verts = [verts]
        radii = [radii]
        twist = [twist]

    for idx, (VERTS, RADII, TWIST) in enumerate(zip(verts, radii, twist)):

        full_flat = []
        for v in VERTS:
            full_flat.extend([v[0], v[1], v[2], 1.0])

        polyline = cu.splines.new(kind)
        polyline.points.add(len(VERTS)-1)
        polyline.points.foreach_set('co', full_flat)

        if RADII:
            if len(VERTS) < len(RADII):
                RADII = RADII[:len(VERTS)]
            elif len(VERTS) > len(RADII):
                fullList(RADII, len(VERTS))
            polyline.points.foreach_set('radius', RADII)

        if TWIST:
            if len(VERTS) < len(TWIST):
                TWIST = TWIST[:len(VERTS)]
            elif len(VERTS) > len(TWIST):
                fullList(TWIST, len(VERTS))
            polyline.points.foreach_set('tilt', TWIST)

        if node.close:
            cu.splines[idx].use_cyclic_u = True

        if node.bspline:
            polyline.order_u = len(polyline.points)-1

        polyline.use_smooth = node.use_smooth

    return obj

def make_curve_geometry(obj_index, node, verts, matrix, radii, twist):
    sv_object = live_curve(obj_index, node, verts, radii, twist)
    sv_object.hide_select = False
    node.push_custom_matrix_if_present(sv_object, matrix)
    return sv_object

class SvPolylineViewerNodeV28(bpy.types.Node, SverchCustomTreeNode, SvObjHelper):

    bl_idname = 'SvPolylineViewerNodeV28'
    bl_label = 'Polyline Viewer'
    bl_icon = 'MOD_CURVE'

    mode_options = [(k, k, '', i) for i, k in enumerate(["Multi", "Single"])]
    selected_mode: bpy.props.EnumProperty(
        items=mode_options,
        description="offers joined of unique curves",
        default="Multi", update=updateNode
    )

    dimension_modes = [(k, k, '', i) for i, k in enumerate(["3D", "2D"])]

    curve_dimensions: bpy.props.EnumProperty(
        items=dimension_modes, update=updateNode,
        description="2D or 3D curves", default="3D"
    )

    depth: FloatProperty(min=0.0, default=0.2, update=updateNode)
    resolution: IntProperty(min=0, default=3, update=updateNode)
    bspline: BoolProperty(default=False, update=updateNode)
    close: BoolProperty(default=False, update=updateNode)

    radii: FloatProperty(min=0, default=0.2, update=updateNode)
    twist: FloatProperty(default=0.0, update=updateNode)
    caps: BoolProperty(update=updateNode)

    data_kind: StringProperty(default='CURVE')

    def sv_init(self, context):

    def draw_buttons(self, context, layout):

        self.draw_live_and_outliner(context, layout)
        self.draw_object_buttons(context, layout)

        layout.row().prop(self, 'curve_dimensions', expand=True)

        col = layout.column()
        if self.curve_dimensions == '3D':
            r1 = col.row(align=True)
            r1.prop(self, 'depth', text='radius')
            r1.prop(self, 'resolution', text='subdiv')
        row = col.row(align=True)
        row.prop(self, 'bspline', text='bspline', toggle=True)
        row.prop(self, 'close', text='close', toggle=True)
        if self.inputs['bevel object'].sv_get(default=[]):
            row.prop(self, 'caps', text='caps', toggle=True)
        row = col.row(align=True)
        row.prop(self, 'show_wire', text='wire', toggle=True)
        row.prop(self, 'use_smooth', text='smooth', toggle=True)
        row.separator()
        row.prop(self, 'selected_mode', expand=True)

    def draw_buttons_ext(self, context, layout):
        self.draw_buttons(context, layout)
        self.draw_ext_object_buttons(context, layout)

    def get_geometry_from_sockets(self, has_matrices):

        def get(socket_name):
            data = self.inputs[socket_name].sv_get(default=[])
            return dataCorrect(data)

        mverts = get('vertices')
        mradii = self.inputs['radii'].sv_get(deepcopy=True)
        mtwist = self.inputs['twist'].sv_get(deepcopy=True)
        mmtrix = get('matrix')

        # extend all non empty lists to longest of these
        maxlen = max(len(mverts), len(mmtrix))
        if has_matrices:
            fullList(mverts, maxlen)
            fullList(mmtrix, maxlen)

        if mradii:
            fullList(mradii, maxlen)
        if mtwist:
            fullList(mtwist, maxlen)

        return mverts, mradii, mtwist, mmtrix

    def process(self):
        if not self.activate:
            return

        if not self.inputs['vertices'].is_linked:
            return

        has_matrices = self.inputs['matrix'].is_linked
        mverts, mradii, mtwist, mmatrices = self.get_geometry_from_sockets(has_matrices)

        out_objects = []
        for obj_index, Verts in enumerate(mverts):
            if not Verts:
                continue

            matrix = mmatrices[obj_index] if has_matrices else []

            if self.selected_mode == 'Multi':
                curve_args = obj_index, self, Verts, matrix, mradii[obj_index], mtwist[obj_index]
                new_obj = make_curve_geometry(*curve_args)
                out_objects.append(new_obj)
            else:
                if matrix:
                    mverts = [multiply_vectors(*mv) for mv in zip(mmatrices, mverts)]
                new_obj = make_curve_geometry(0, self, mverts, [], mradii, mtwist)
                out_objects.append(new_obj)
                break

        last_index = len(mverts) - 1
        self.remove_non_updated_objects(last_index)
        self.set_corresponding_materials()

        self.outputs['object'].sv_set(out_objects)

def register():
    bpy.utils.register_class(SvPolylineViewerNodeV28)

def unregister():
    bpy.utils.unregister_class(SvPolylineViewerNodeV28)


obj, cu = node.get_obj_curve(obj_index)

    obj.show_wire = node.show_wire
    cu.bevel_depth = node.depth
    cu.bevel_resolution = node.resolution

import bpy
from bpy.props import (BoolProperty, StringProperty, FloatProperty, IntProperty)

from sverchok.utils.sv_obj_helper import SvObjHelper
from sverchok.utils.geom import multiply_vectors
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import dataCorrect, fullList, updateNode

def set_bevel_object(node, cu, obj_index):
    # use bevel object if provided
    bevel_objs = node.inputs['bevel object'].sv_get(default=[])
    if bevel_objs:
        obj_ref = bevel_objs[obj_index] if obj_index < len(bevel_objs) else bevel_objs[-1]
        if obj_ref.type == 'CURVE':
            cu.bevel_object = obj_ref
            cu.use_fill_caps = node.caps
    else:
        cu.bevel_object = None
        cu.use_fill_caps = False

def live_curve(obj_index, node, verts, radii, twist):

    cu.dimensions = node.curve_dimensions
    if cu.dimensions == '2D':
        cu.fill_mode = 'FRONT'
    else:
        cu.fill_mode = 'FULL'

    set_bevel_object(node, cu, obj_index)

    kind = ["POLY", "NURBS"][bool(node.bspline)]

    if node.selected_mode == 'Multi':
        verts = [verts]
        radii = [radii]
        twist = [twist]

    for idx, (VERTS, RADII, TWIST) in enumerate(zip(verts, radii, twist)):

        full_flat = []
        for v in VERTS:
            full_flat.extend([v[0], v[1], v[2], 1.0])

        polyline = cu.splines.new(kind)
        polyline.points.add(len(VERTS)-1)
        polyline.points.foreach_set('co', full_flat)

        if RADII:
            if len(VERTS) < len(RADII):
                RADII = RADII[:len(VERTS)]
            elif len(VERTS) > len(RADII):
                fullList(RADII, len(VERTS))
            polyline.points.foreach_set('radius', RADII)

        if TWIST:
            if len(VERTS) < len(TWIST):
                TWIST = TWIST[:len(VERTS)]
            elif len(VERTS) > len(TWIST):
                fullList(TWIST, len(VERTS))
            polyline.points.foreach_set('tilt', TWIST)

        if node.close:
            cu.splines[idx].use_cyclic_u = True

        if node.bspline:
            polyline.order_u = len(polyline.points)-1

        polyline.use_smooth = node.use_smooth

    return obj

def make_curve_geometry(obj_index, node, verts, matrix, radii, twist):
    sv_object = live_curve(obj_index, node, verts, radii, twist)
    sv_object.hide_select = False
    node.push_custom_matrix_if_present(sv_object, matrix)
    return sv_object

class SvPolylineViewerNodeV28(bpy.types.Node, SverchCustomTreeNode, SvObjHelper):

    bl_idname = 'SvPolylineViewerNodeV28'
    bl_label = 'Polyline Viewer'
    bl_icon = 'MOD_CURVE'

    mode_options = [(k, k, '', i) for i, k in enumerate(["Multi", "Single"])]
    selected_mode: bpy.props.EnumProperty(
        items=mode_options,
        description="offers joined of unique curves",
        default="Multi", update=updateNode
    )

    dimension_modes = [(k, k, '', i) for i, k in enumerate(["3D", "2D"])]

    curve_dimensions: bpy.props.EnumProperty(
        items=dimension_modes, update=updateNode,
        description="2D or 3D curves", default="3D"
    )

    depth: FloatProperty(min=0.0, default=0.2, update=updateNode)
    resolution: IntProperty(min=0, default=3, update=updateNode)
    bspline: BoolProperty(default=False, update=updateNode)
    close: BoolProperty(default=False, update=updateNode)

    radii: FloatProperty(min=0, default=0.2, update=updateNode)
    twist: FloatProperty(default=0.0, update=updateNode)
    caps: BoolProperty(update=updateNode)

    data_kind: StringProperty(default='CURVE')

    def sv_init(self, context):
        self.sv_init_helper_basedata_name()

        self.inputs.new('VerticesSocket', 'vertices')
        self.inputs.new('MatrixSocket', 'matrix')
        self.inputs.new('StringsSocket', 'radii').prop_name = 'radii'
        self.inputs.new('StringsSocket', 'twist').prop_name = 'twist'
        self.inputs.new('SvObjectSocket', 'bevel object')
        self.outputs.new('SvObjectSocket', "object")

    def draw_buttons(self, context, layout):

        self.draw_live_and_outliner(context, layout)
        self.draw_object_buttons(context, layout)

        layout.row().prop(self, 'curve_dimensions', expand=True)

        col = layout.column()
        if self.curve_dimensions == '3D':
            r1 = col.row(align=True)
            r1.prop(self, 'depth', text='radius')
            r1.prop(self, 'resolution', text='subdiv')
        row = col.row(align=True)
        row.prop(self, 'bspline', text='bspline', toggle=True)
        row.prop(self, 'close', text='close', toggle=True)
        if self.inputs['bevel object'].sv_get(default=[]):
            row.prop(self, 'caps', text='caps', toggle=True)
        row = col.row(align=True)
        row.prop(self, 'show_wire', text='wire', toggle=True)
        row.prop(self, 'use_smooth', text='smooth', toggle=True)
        row.separator()
        row.prop(self, 'selected_mode', expand=True)

    def draw_buttons_ext(self, context, layout):
        self.draw_buttons(context, layout)
        self.draw_ext_object_buttons(context, layout)

    def get_geometry_from_sockets(self, has_matrices):

        def get(socket_name):
            data = self.inputs[socket_name].sv_get(default=[])
            return dataCorrect(data)

        mverts = get('vertices')
        mradii = self.inputs['radii'].sv_get(deepcopy=True)
        mtwist = self.inputs['twist'].sv_get(deepcopy=True)
        mmtrix = get('matrix')

        # extend all non empty lists to longest of these
        maxlen = max(len(mverts), len(mmtrix))
        if has_matrices:
            fullList(mverts, maxlen)
            fullList(mmtrix, maxlen)

        if mradii:
            fullList(mradii, maxlen)
        if mtwist:
            fullList(mtwist, maxlen)

        return mverts, mradii, mtwist, mmtrix

    def process(self):
        if not self.activate:
            return

        if not self.inputs['vertices'].is_linked:
            return

        has_matrices = self.inputs['matrix'].is_linked
        mverts, mradii, mtwist, mmatrices = self.get_geometry_from_sockets(has_matrices)

        out_objects = []
        for obj_index, Verts in enumerate(mverts):
            if not Verts:
                continue

            matrix = mmatrices[obj_index] if has_matrices else []

            if self.selected_mode == 'Multi':
                curve_args = obj_index, self, Verts, matrix, mradii[obj_index], mtwist[obj_index]
                new_obj = make_curve_geometry(*curve_args)
                out_objects.append(new_obj)
            else:
                if matrix:
                    mverts = [multiply_vectors(*mv) for mv in zip(mmatrices, mverts)]
                new_obj = make_curve_geometry(0, self, mverts, [], mradii, mtwist)
                out_objects.append(new_obj)
                break

        last_index = len(mverts) - 1
        self.remove_non_updated_objects(last_index)
        self.set_corresponding_materials()

        self.outputs['object'].sv_set(out_objects)

def register():
    bpy.utils.register_class(SvPolylineViewerNodeV28)

def unregister():
    bpy.utils.unregister_class(SvPolylineViewerNodeV28)


cu.dimensions = node.curve_dimensions
    if cu.dimensions == '2D':
        cu.fill_mode = 'FRONT'
    else:
        cu.fill_mode = 'FULL'

    set_bevel_object(node, cu, obj_index)

import bpy
from bpy.props import (BoolProperty, StringProperty, FloatProperty, IntProperty)

from sverchok.utils.sv_obj_helper import SvObjHelper
from sverchok.utils.geom import multiply_vectors
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import dataCorrect, fullList, updateNode

def set_bevel_object(node, cu, obj_index):
    # use bevel object if provided
    bevel_objs = node.inputs['bevel object'].sv_get(default=[])
    if bevel_objs:
        obj_ref = bevel_objs[obj_index] if obj_index < len(bevel_objs) else bevel_objs[-1]
        if obj_ref.type == 'CURVE':
            cu.bevel_object = obj_ref
            cu.use_fill_caps = node.caps
    else:
        cu.bevel_object = None
        cu.use_fill_caps = False

def live_curve(obj_index, node, verts, radii, twist):

    obj, cu = node.get_obj_curve(obj_index)

    obj.show_wire = node.show_wire
    cu.bevel_depth = node.depth
    cu.bevel_resolution = node.resolution

    kind = ["POLY", "NURBS"][bool(node.bspline)]

    if node.selected_mode == 'Multi':
        verts = [verts]
        radii = [radii]
        twist = [twist]

    for idx, (VERTS, RADII, TWIST) in enumerate(zip(verts, radii, twist)):

        full_flat = []
        for v in VERTS:
            full_flat.extend([v[0], v[1], v[2], 1.0])

        polyline = cu.splines.new(kind)
        polyline.points.add(len(VERTS)-1)
        polyline.points.foreach_set('co', full_flat)

        if RADII:
            if len(VERTS) < len(RADII):
                RADII = RADII[:len(VERTS)]
            elif len(VERTS) > len(RADII):
                fullList(RADII, len(VERTS))
            polyline.points.foreach_set('radius', RADII)

        if TWIST:
            if len(VERTS) < len(TWIST):
                TWIST = TWIST[:len(VERTS)]
            elif len(VERTS) > len(TWIST):
                fullList(TWIST, len(VERTS))
            polyline.points.foreach_set('tilt', TWIST)

        if node.close:
            cu.splines[idx].use_cyclic_u = True

        if node.bspline:
            polyline.order_u = len(polyline.points)-1

        polyline.use_smooth = node.use_smooth

    return obj

def make_curve_geometry(obj_index, node, verts, matrix, radii, twist):
    sv_object = live_curve(obj_index, node, verts, radii, twist)
    sv_object.hide_select = False
    node.push_custom_matrix_if_present(sv_object, matrix)
    return sv_object

class SvPolylineViewerNodeV28(bpy.types.Node, SverchCustomTreeNode, SvObjHelper):

    bl_idname = 'SvPolylineViewerNodeV28'
    bl_label = 'Polyline Viewer'
    bl_icon = 'MOD_CURVE'

    mode_options = [(k, k, '', i) for i, k in enumerate(["Multi", "Single"])]
    selected_mode: bpy.props.EnumProperty(
        items=mode_options,
        description="offers joined of unique curves",
        default="Multi", update=updateNode
    )

    dimension_modes = [(k, k, '', i) for i, k in enumerate(["3D", "2D"])]

    curve_dimensions: bpy.props.EnumProperty(
        items=dimension_modes, update=updateNode,
        description="2D or 3D curves", default="3D"
    )

    depth: FloatProperty(min=0.0, default=0.2, update=updateNode)
    resolution: IntProperty(min=0, default=3, update=updateNode)
    bspline: BoolProperty(default=False, update=updateNode)
    close: BoolProperty(default=False, update=updateNode)

    radii: FloatProperty(min=0, default=0.2, update=updateNode)
    twist: FloatProperty(default=0.0, update=updateNode)
    caps: BoolProperty(update=updateNode)

    data_kind: StringProperty(default='CURVE')

    def sv_init(self, context):
        self.sv_init_helper_basedata_name()

        self.inputs.new('VerticesSocket', 'vertices')
        self.inputs.new('MatrixSocket', 'matrix')
        self.inputs.new('StringsSocket', 'radii').prop_name = 'radii'
        self.inputs.new('StringsSocket', 'twist').prop_name = 'twist'
        self.inputs.new('SvObjectSocket', 'bevel object')
        self.outputs.new('SvObjectSocket', "object")

    def draw_buttons(self, context, layout):

        self.draw_live_and_outliner(context, layout)
        self.draw_object_buttons(context, layout)

        layout.row().prop(self, 'curve_dimensions', expand=True)

        col = layout.column()
        if self.curve_dimensions == '3D':
            r1 = col.row(align=True)
            r1.prop(self, 'depth', text='radius')
            r1.prop(self, 'resolution', text='subdiv')
        row = col.row(align=True)
        row.prop(self, 'bspline', text='bspline', toggle=True)
        row.prop(self, 'close', text='close', toggle=True)
        if self.inputs['bevel object'].sv_get(default=[]):
            row.prop(self, 'caps', text='caps', toggle=True)
        row = col.row(align=True)
        row.prop(self, 'show_wire', text='wire', toggle=True)
        row.prop(self, 'use_smooth', text='smooth', toggle=True)
        row.separator()
        row.prop(self, 'selected_mode', expand=True)

    def draw_buttons_ext(self, context, layout):
        self.draw_buttons(context, layout)
        self.draw_ext_object_buttons(context, layout)

    def get_geometry_from_sockets(self, has_matrices):

        def get(socket_name):
            data = self.inputs[socket_name].sv_get(default=[])
            return dataCorrect(data)

        mverts = get('vertices')
        mradii = self.inputs['radii'].sv_get(deepcopy=True)
        mtwist = self.inputs['twist'].sv_get(deepcopy=True)
        mmtrix = get('matrix')

        # extend all non empty lists to longest of these
        maxlen = max(len(mverts), len(mmtrix))
        if has_matrices:
            fullList(mverts, maxlen)
            fullList(mmtrix, maxlen)

        if mradii:
            fullList(mradii, maxlen)
        if mtwist:
            fullList(mtwist, maxlen)

        return mverts, mradii, mtwist, mmtrix

    def process(self):
        if not self.activate:
            return

        if not self.inputs['vertices'].is_linked:
            return

        has_matrices = self.inputs['matrix'].is_linked
        mverts, mradii, mtwist, mmatrices = self.get_geometry_from_sockets(has_matrices)

        out_objects = []
        for obj_index, Verts in enumerate(mverts):
            if not Verts:
                continue

            matrix = mmatrices[obj_index] if has_matrices else []

            if self.selected_mode == 'Multi':
                curve_args = obj_index, self, Verts, matrix, mradii[obj_index], mtwist[obj_index]
                new_obj = make_curve_geometry(*curve_args)
                out_objects.append(new_obj)
            else:
                if matrix:
                    mverts = [multiply_vectors(*mv) for mv in zip(mmatrices, mverts)]
                new_obj = make_curve_geometry(0, self, mverts, [], mradii, mtwist)
                out_objects.append(new_obj)
                break

        last_index = len(mverts) - 1
        self.remove_non_updated_objects(last_index)
        self.set_corresponding_materials()

        self.outputs['object'].sv_set(out_objects)

def register():
    bpy.utils.register_class(SvPolylineViewerNodeV28)

def unregister():
    bpy.utils.unregister_class(SvPolylineViewerNodeV28)


kind = ["POLY", "NURBS"][bool(node.bspline)]

    if node.selected_mode == 'Multi':
        verts = [verts]
        radii = [radii]
        twist = [twist]

    for idx, (VERTS, RADII, TWIST) in enumerate(zip(verts, radii, twist)):

        full_flat = []
        for v in VERTS:
            full_flat.extend([v[0], v[1], v[2], 1.0])

        polyline = cu.splines.new(kind)
        polyline.points.add(len(VERTS)-1)
        polyline.points.foreach_set('co', full_flat)

        if RADII:
            if len(VERTS) < len(RADII):
                RADII = RADII[:len(VERTS)]
            elif len(VERTS) > len(RADII):
                fullList(RADII, len(VERTS))
            polyline.points.foreach_set('radius', RADII)

        if TWIST:
            if len(VERTS) < len(TWIST):
                TWIST = TWIST[:len(VERTS)]
            elif len(VERTS) > len(TWIST):
                fullList(TWIST, len(VERTS))
            polyline.points.foreach_set('tilt', TWIST)

        if node.close:
            cu.splines[idx].use_cyclic_u = True

        if node.bspline:
            polyline.order_u = len(polyline.points)-1

        polyline.use_smooth = node.use_smooth

    return obj

import bpy
from bpy.props import (BoolProperty, StringProperty, FloatProperty, IntProperty)

from sverchok.utils.sv_obj_helper import SvObjHelper
from sverchok.utils.geom import multiply_vectors
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import dataCorrect, fullList, updateNode

def set_bevel_object(node, cu, obj_index):
    # use bevel object if provided
    bevel_objs = node.inputs['bevel object'].sv_get(default=[])
    if bevel_objs:
        obj_ref = bevel_objs[obj_index] if obj_index < len(bevel_objs) else bevel_objs[-1]
        if obj_ref.type == 'CURVE':
            cu.bevel_object = obj_ref
            cu.use_fill_caps = node.caps
    else:
        cu.bevel_object = None
        cu.use_fill_caps = False

def live_curve(obj_index, node, verts, radii, twist):

    obj, cu = node.get_obj_curve(obj_index)

    obj.show_wire = node.show_wire
    cu.bevel_depth = node.depth
    cu.bevel_resolution = node.resolution
    cu.dimensions = node.curve_dimensions
    if cu.dimensions == '2D':
        cu.fill_mode = 'FRONT'
    else:
        cu.fill_mode = 'FULL'

    set_bevel_object(node, cu, obj_index)

def make_curve_geometry(obj_index, node, verts, matrix, radii, twist):
    sv_object = live_curve(obj_index, node, verts, radii, twist)
    sv_object.hide_select = False
    node.push_custom_matrix_if_present(sv_object, matrix)
    return sv_object

class SvPolylineViewerNodeV28(bpy.types.Node, SverchCustomTreeNode, SvObjHelper):

    bl_idname = 'SvPolylineViewerNodeV28'
    bl_label = 'Polyline Viewer'
    bl_icon = 'MOD_CURVE'

    mode_options = [(k, k, '', i) for i, k in enumerate(["Multi", "Single"])]
    selected_mode: bpy.props.EnumProperty(
        items=mode_options,
        description="offers joined of unique curves",
        default="Multi", update=updateNode
    )

    dimension_modes = [(k, k, '', i) for i, k in enumerate(["3D", "2D"])]

    curve_dimensions: bpy.props.EnumProperty(
        items=dimension_modes, update=updateNode,
        description="2D or 3D curves", default="3D"
    )

    depth: FloatProperty(min=0.0, default=0.2, update=updateNode)
    resolution: IntProperty(min=0, default=3, update=updateNode)
    bspline: BoolProperty(default=False, update=updateNode)
    close: BoolProperty(default=False, update=updateNode)

    radii: FloatProperty(min=0, default=0.2, update=updateNode)
    twist: FloatProperty(default=0.0, update=updateNode)
    caps: BoolProperty(update=updateNode)

    data_kind: StringProperty(default='CURVE')

    def sv_init(self, context):
        self.sv_init_helper_basedata_name()

        self.inputs.new('VerticesSocket', 'vertices')
        self.inputs.new('MatrixSocket', 'matrix')
        self.inputs.new('StringsSocket', 'radii').prop_name = 'radii'
        self.inputs.new('StringsSocket', 'twist').prop_name = 'twist'
        self.inputs.new('SvObjectSocket', 'bevel object')
        self.outputs.new('SvObjectSocket', "object")

    def draw_buttons(self, context, layout):

        self.draw_live_and_outliner(context, layout)
        self.draw_object_buttons(context, layout)

        layout.row().prop(self, 'curve_dimensions', expand=True)

        col = layout.column()
        if self.curve_dimensions == '3D':
            r1 = col.row(align=True)
            r1.prop(self, 'depth', text='radius')
            r1.prop(self, 'resolution', text='subdiv')
        row = col.row(align=True)
        row.prop(self, 'bspline', text='bspline', toggle=True)
        row.prop(self, 'close', text='close', toggle=True)
        if self.inputs['bevel object'].sv_get(default=[]):
            row.prop(self, 'caps', text='caps', toggle=True)
        row = col.row(align=True)
        row.prop(self, 'show_wire', text='wire', toggle=True)
        row.prop(self, 'use_smooth', text='smooth', toggle=True)
        row.separator()
        row.prop(self, 'selected_mode', expand=True)

    def draw_buttons_ext(self, context, layout):
        self.draw_buttons(context, layout)
        self.draw_ext_object_buttons(context, layout)

    def get_geometry_from_sockets(self, has_matrices):

        def get(socket_name):
            data = self.inputs[socket_name].sv_get(default=[])
            return dataCorrect(data)

        mverts = get('vertices')
        mradii = self.inputs['radii'].sv_get(deepcopy=True)
        mtwist = self.inputs['twist'].sv_get(deepcopy=True)
        mmtrix = get('matrix')

        # extend all non empty lists to longest of these
        maxlen = max(len(mverts), len(mmtrix))
        if has_matrices:
            fullList(mverts, maxlen)
            fullList(mmtrix, maxlen)

        if mradii:
            fullList(mradii, maxlen)
        if mtwist:
            fullList(mtwist, maxlen)

        return mverts, mradii, mtwist, mmtrix

    def process(self):
        if not self.activate:
            return

        if not self.inputs['vertices'].is_linked:
            return

        has_matrices = self.inputs['matrix'].is_linked
        mverts, mradii, mtwist, mmatrices = self.get_geometry_from_sockets(has_matrices)

        out_objects = []
        for obj_index, Verts in enumerate(mverts):
            if not Verts:
                continue

            matrix = mmatrices[obj_index] if has_matrices else []

            if self.selected_mode == 'Multi':
                curve_args = obj_index, self, Verts, matrix, mradii[obj_index], mtwist[obj_index]
                new_obj = make_curve_geometry(*curve_args)
                out_objects.append(new_obj)
            else:
                if matrix:
                    mverts = [multiply_vectors(*mv) for mv in zip(mmatrices, mverts)]
                new_obj = make_curve_geometry(0, self, mverts, [], mradii, mtwist)
                out_objects.append(new_obj)
                break

        last_index = len(mverts) - 1
        self.remove_non_updated_objects(last_index)
        self.set_corresponding_materials()

        self.outputs['object'].sv_set(out_objects)

def register():
    bpy.utils.register_class(SvPolylineViewerNodeV28)

def unregister():
    bpy.utils.unregister_class(SvPolylineViewerNodeV28)


import bpy
from bpy.props import (BoolProperty, StringProperty, FloatProperty, IntProperty)

from sverchok.utils.sv_obj_helper import SvObjHelper
from sverchok.utils.geom import multiply_vectors
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import dataCorrect, fullList, updateNode

def set_bevel_object(node, cu, obj_index):
    # use bevel object if provided
    bevel_objs = node.inputs['bevel object'].sv_get(default=[])
    if bevel_objs:
        obj_ref = bevel_objs[obj_index] if obj_index < len(bevel_objs) else bevel_objs[-1]
        if obj_ref.type == 'CURVE':
            cu.bevel_object = obj_ref
            cu.use_fill_caps = node.caps
    else:
        cu.bevel_object = None
        cu.use_fill_caps = False

def live_curve(obj_index, node, verts, radii, twist):

    obj, cu = node.get_obj_curve(obj_index)

    obj.show_wire = node.show_wire
    cu.bevel_depth = node.depth
    cu.bevel_resolution = node.resolution
    cu.dimensions = node.curve_dimensions
    if cu.dimensions == '2D':
        cu.fill_mode = 'FRONT'
    else:
        cu.fill_mode = 'FULL'

    set_bevel_object(node, cu, obj_index)

    kind = ["POLY", "NURBS"][bool(node.bspline)]

    if node.selected_mode == 'Multi':
        verts = [verts]
        radii = [radii]
        twist = [twist]

    for idx, (VERTS, RADII, TWIST) in enumerate(zip(verts, radii, twist)):

        full_flat = []
        for v in VERTS:
            full_flat.extend([v[0], v[1], v[2], 1.0])

        polyline = cu.splines.new(kind)
        polyline.points.add(len(VERTS)-1)
        polyline.points.foreach_set('co', full_flat)

        if RADII:
            if len(VERTS) < len(RADII):
                RADII = RADII[:len(VERTS)]
            elif len(VERTS) > len(RADII):
                fullList(RADII, len(VERTS))
            polyline.points.foreach_set('radius', RADII)

        if TWIST:
            if len(VERTS) < len(TWIST):
                TWIST = TWIST[:len(VERTS)]
            elif len(VERTS) > len(TWIST):
                fullList(TWIST, len(VERTS))
            polyline.points.foreach_set('tilt', TWIST)

        if node.close:
            cu.splines[idx].use_cyclic_u = True

        if node.bspline:
            polyline.order_u = len(polyline.points)-1

        polyline.use_smooth = node.use_smooth

    return obj

def make_curve_geometry(obj_index, node, verts, matrix, radii, twist):
    sv_object = live_curve(obj_index, node, verts, radii, twist)
    sv_object.hide_select = False
    node.push_custom_matrix_if_present(sv_object, matrix)
    return sv_object

class SvPolylineViewerNodeV28(bpy.types.Node, SverchCustomTreeNode, SvObjHelper):

    bl_idname = 'SvPolylineViewerNodeV28'
    bl_label = 'Polyline Viewer'
    bl_icon = 'MOD_CURVE'

    mode_options = [(k, k, '', i) for i, k in enumerate(["Multi", "Single"])]
    selected_mode: bpy.props.EnumProperty(
        items=mode_options,
        description="offers joined of unique curves",
        default="Multi", update=updateNode
    )

    dimension_modes = [(k, k, '', i) for i, k in enumerate(["3D", "2D"])]

    curve_dimensions: bpy.props.EnumProperty(
        items=dimension_modes, update=updateNode,
        description="2D or 3D curves", default="3D"
    )

    depth: FloatProperty(min=0.0, default=0.2, update=updateNode)
    resolution: IntProperty(min=0, default=3, update=updateNode)
    bspline: BoolProperty(default=False, update=updateNode)
    close: BoolProperty(default=False, update=updateNode)

    radii: FloatProperty(min=0, default=0.2, update=updateNode)
    twist: FloatProperty(default=0.0, update=updateNode)
    caps: BoolProperty(update=updateNode)

    data_kind: StringProperty(default='CURVE')

    def sv_init(self, context):
        self.sv_init_helper_basedata_name()

        self.inputs.new('VerticesSocket', 'vertices')
        self.inputs.new('MatrixSocket', 'matrix')
        self.inputs.new('StringsSocket', 'radii').prop_name = 'radii'
        self.inputs.new('StringsSocket', 'twist').prop_name = 'twist'
        self.inputs.new('SvObjectSocket', 'bevel object')
        self.outputs.new('SvObjectSocket', "ob


if cu.dimensions == '2D':
        cu.fill_mode = 'FRONT'
    else:
        cu.fill_mode = 'FULL'

import bpy
from bpy.props import (BoolProperty, StringProperty, FloatProperty, IntProperty)

from sverchok.utils.sv_obj_helper import SvObjHelper
from sverchok.utils.geom import multiply_vectors
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import dataCorrect, fullList, updateNode

def set_bevel_object(node, cu, obj_index):
    # use bevel object if provided
    bevel_objs = node.inputs['bevel object'].sv_get(default=[])
    if bevel_objs:
        obj_ref = bevel_objs[obj_index] if obj_index < len(bevel_objs) else bevel_objs[-1]
        if obj_ref.type == 'CURVE':
            cu.bevel_object = obj_ref
            cu.use_fill_caps = node.caps
    else:
        cu.bevel_object = None
        cu.use_fill_caps = False

def live_curve(obj_index, node, verts, radii, twist):

    obj, cu = node.get_obj_curve(obj_index)

    obj.show_wire = node.show_wire
    cu.bevel_depth = node.depth
    cu.bevel_resolution = node.resolution
    cu.dimensions = node.curve_dimensions

    set_bevel_object(node, cu, obj_index)

    kind = ["POLY", "NURBS"][bool(node.bspline)]

    if node.selected_mode == 'Multi':
        verts = [verts]
        radii = [radii]
        twist = [twist]

    for idx, (VERTS, RADII, TWIST) in enumerate(zip(verts, radii, twist)):

        full_flat = []
        for v in VERTS:
            full_flat.extend([v[0], v[1], v[2], 1.0])

        polyline = cu.splines.new(kind)
        polyline.points.add(len(VERTS)-1)
        polyline.points.foreach_set('co', full_flat)

        if RADII:
            if len(VERTS) < len(RADII):
                RADII = RADII[:len(VERTS)]
            elif len(VERTS) > len(RADII):
                fullList(RADII, len(VERTS))
            polyline.points.foreach_set('radius', RADII)

        if TWIST:
            if len(VERTS) < len(TWIST):
                TWIST = TWIST[:len(VERTS)]
            elif len(VERTS) > len(TWIST):
                fullList(TWIST, len(VERTS))
            polyline.points.foreach_set('tilt', TWIST)

        if node.close:
            cu.splines[idx].use_cyclic_u = True

        if node.bspline:
            polyline.order_u = len(polyline.points)-1

        polyline.use_smooth = node.use_smooth

    return obj

def make_curve_geometry(obj_index, node, verts, matrix, radii, twist):
    sv_object = live_curve(obj_index, node, verts, radii, twist)
    sv_object.hide_select = False
    node.push_custom_matrix_if_present(sv_object, matrix)
    return sv_object

class SvPolylineViewerNodeV28(bpy.types.Node, SverchCustomTreeNode, SvObjHelper):

    bl_idname = 'SvPolylineViewerNodeV28'
    bl_label = 'Polyline Viewer'
    bl_icon = 'MOD_CURVE'

    mode_options = [(k, k, '', i) for i, k in enumerate(["Multi", "Single"])]
    selected_mode: bpy.props.EnumProperty(
        items=mode_options,
        description="offers joined of unique curves",
        default="Multi", update=updateNode
    )

    dimension_modes = [(k, k, '', i) for i, k in enumerate(["3D", "2D"])]

    curve_dimensions: bpy.props.EnumProperty(
        items=dimension_modes, update=updateNode,
        description="2D or 3D curves", default="3D"
    )

    depth: FloatProperty(min=0.0, default=0.2, update=updateNode)
    resolution: IntProperty(min=0, default=3, update=updateNode)
    bspline: BoolProperty(default=False, update=updateNode)
    close: BoolProperty(default=False, update=updateNode)

    radii: FloatProperty(min=0, default=0.2, update=updateNode)
    twist: FloatProperty(default=0.0, update=updateNode)
    caps: BoolProperty(update=updateNode)

    data_kind: StringProperty(default='CURVE')

    def sv_init(self, context):
        self.sv_init_helper_basedata_name()

        self.inputs.new('VerticesSocket', 'vertices')
        self.inputs.new('MatrixSocket', 'matrix')
        self.inputs.new('StringsSocket', 'radii').prop_name = 'radii'
        self.inputs.new('StringsSocket', 'twist').prop_name = 'twist'
        self.inputs.new('SvObjectSocket', 'bevel object')
        self.outputs.new('SvObjectSocket', "object")

    def draw_buttons(self, context, layout):

        self.draw_live_and_outliner(context, layout)
        self.draw_object_buttons(context, layout)

        layout.row().prop(self, 'curve_dimensions', expand=True)

        col = layout.column()
        if self.curve_dimensions == '3D':
            r1 = col.row(align=True)
            r1.prop(self, 'depth', text='radius')
            r1.prop(self, 'resolution', text='subdiv')
        row = col.row(align=True)
        row.prop(self, 'bspline', text='bspline', toggle=True)
        row.prop(self, 'close', text='close', toggle=True)
        if self.inputs['bevel object'].sv_get(default=[]):
            row.prop(self, 'caps', text='caps', toggle=True)
        row = col.row(align=True)
        row.prop(self, 'show_wire', text='wire', toggle=True)
        row.prop(self, 'use_smooth', text='smooth', toggle=True)
        row.separator()
        row.prop(self, 'selected_mode', expand=True)

    def draw_buttons_ext(self, context, layout):
        self.draw_buttons(context, layout)
        self.draw_ext_object_buttons(context, layout)

    def get_geometry_from_sockets(self, has_matrices):

        def get(socket_name):
            data = self.inputs[socket_name].sv_get(default=[])
            return dataCorrect(data)

        mverts = get('vertices')
        mradii = self.inputs['radii'].sv_get(deepcopy=True)
        mtwist = self.inputs['twist'].sv_get(deepcopy=True)
        mmtrix = get('matrix')

        # extend all non empty lists to longest of these
        maxlen = max(len(mverts), len(mmtrix))
        if has_matrices:
            fullList(mverts, maxlen)
            fullList(mmtrix, maxlen)

        if mradii:
            fullList(mradii, maxlen)
        if mtwist:
            fullList(mtwist, maxlen)

        return mverts, mradii, mtwist, mmtrix

    def process(self):
        if not self.activate:
            return

        if not self.inputs['vertices'].is_linked:
            return

        has_matrices = self.inputs['matrix'].is_linked
        mverts, mradii, mtwist, mmatrices = self.get_geometry_from_sockets(has_matrices)

        out_objects = []
        for obj_index, Verts in enumerate(mverts):
            if not Verts:
                continue

            matrix = mmatrices[obj_index] if has_matrices else []

            if self.selected_mode == 'Multi':
                curve_args = obj_index, self, Verts, matrix, mradii[obj_index], mtwist[obj_index]
                new_obj = make_curve_geometry(*curve_args)
                out_objects.append(new_obj)
            else:
                if matrix:
                    mverts = [multiply_vectors(*mv) for mv in zip(mmatrices, mverts)]
                new_obj = make_curve_geometry(0, self, mverts, [], mradii, mtwist)
                out_objects.append(new_obj)
                break

        last_index = len(mverts) - 1
        self.remove_non_updated_objects(last_index)
        self.set_corresponding_materials()

        self.outputs['object'].sv_set(out_objects)

def register():
    bpy.utils.register_class(SvPolylineViewerNodeV28)

def unregister():
    bpy.utils.unregister_class(SvPolylineViewerNodeV28)


for v in VERTS:
            full_flat.extend([v[0], v[1], v[2], 1.0])

import bpy
from bpy.props import (BoolProperty, StringProperty, FloatProperty, IntProperty)

from sverchok.utils.sv_obj_helper import SvObjHelper
from sverchok.utils.geom import multiply_vectors
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import dataCorrect, fullList, updateNode

def set_bevel_object(node, cu, obj_index):
    # use bevel object if provided
    bevel_objs = node.inputs['bevel object'].sv_get(default=[])
    if bevel_objs:
        obj_ref = bevel_objs[obj_index] if obj_index < len(bevel_objs) else bevel_objs[-1]
        if obj_ref.type == 'CURVE':
            cu.bevel_object = obj_ref
            cu.use_fill_caps = node.caps
    else:
        cu.bevel_object = None
        cu.use_fill_caps = False

def live_curve(obj_index, node, verts, radii, twist):

    obj, cu = node.get_obj_curve(obj_index)

    obj.show_wire = node.show_wire
    cu.bevel_depth = node.depth
    cu.bevel_resolution = node.resolution
    cu.dimensions = node.curve_dimensions
    if cu.dimensions == '2D':
        cu.fill_mode = 'FRONT'
    else:
        cu.fill_mode = 'FULL'

    set_bevel_object(node, cu, obj_index)

    kind = ["POLY", "NURBS"][bool(node.bspline)]

    if node.selected_mode == 'Multi':
        verts = [verts]
        radii = [radii]
        twist = [twist]

    for idx, (VERTS, RADII, TWIST) in enumerate(zip(verts, radii, twist)):

        full_flat = []

        polyline = cu.splines.new(kind)
        polyline.points.add(len(VERTS)-1)
        polyline.points.foreach_set('co', full_flat)

        if RADII:
            if len(VERTS) < len(RADII):
                RADII = RADII[:len(VERTS)]
            elif len(VERTS) > len(RADII):
                fullList(RADII, len(VERTS))
            polyline.points.foreach_set('radius', RADII)

        if TWIST:
            if len(VERTS) < len(TWIST):
                TWIST = TWIST[:len(VERTS)]
            elif len(VERTS) > len(TWIST):
                fullList(TWIST, len(VERTS))
            polyline.points.foreach_set('tilt', TWIST)

        if node.close:
            cu.splines[idx].use_cyclic_u = True

        if node.bspline:
            polyline.order_u = len(polyline.points)-1

        polyline.use_smooth = node.use_smooth

    return obj

def make_curve_geometry(obj_index, node, verts, matrix, radii, twist):
    sv_object = live_curve(obj_index, node, verts, radii, twist)
    sv_object.hide_select = False
    node.push_custom_matrix_if_present(sv_object, matrix)
    return sv_object

class SvPolylineViewerNodeV28(bpy.types.Node, SverchCustomTreeNode, SvObjHelper):

    bl_idname = 'SvPolylineViewerNodeV28'
    bl_label = 'Polyline Viewer'
    bl_icon = 'MOD_CURVE'

    mode_options = [(k, k, '', i) for i, k in enumerate(["Multi", "Single"])]
    selected_mode: bpy.props.EnumProperty(
        items=mode_options,
        description="offers joined of unique curves",
        default="Multi", update=updateNode
    )

    dimension_modes = [(k, k, '', i) for i, k in enumerate(["3D", "2D"])]

    curve_dimensions: bpy.props.EnumProperty(
        items=dimension_modes, update=updateNode,
        description="2D or 3D curves", default="3D"
    )

    depth: FloatProperty(min=0.0, default=0.2, update=updateNode)
    resolution: IntProperty(min=0, default=3, update=updateNode)
    bspline: BoolProperty(default=False, update=updateNode)
    close: BoolProperty(default=False, update=updateNode)

    radii: FloatProperty(min=0, default=0.2, update=updateNode)
    twist: FloatProperty(default=0.0, update=updateNode)
    caps: BoolProperty(update=updateNode)

    data_kind: StringProperty(default='CURVE')

    def sv_init(self, context):
        self.sv_init_helper_basedata_name()

        self.inputs.new('VerticesSocket', 'vertices')
        self.inputs.new('MatrixSocket', 'matrix')
        self.inputs.new('StringsSocket', 'radii').prop_name = 'radii'
        self.inputs.new('StringsSocket', 'twist').prop_name = 'twist'
        self.inputs.new('SvObjectSocket', 'bevel object')
        self.outputs.new('SvObjectSocket', "object")

    def draw_buttons(self, context, layout):

        self.draw_live_and_outliner(context, layout)
        self.draw_object_buttons(context, layout)

        layout.row().prop(self, 'curve_dimensions', expand=True)

        col = layout.column()
        if self.curve_dimensions == '3D':
            r1 = col.row(align=True)
            r1.prop(self, 'depth', text='radius')
            r1.prop(self, 'resolution', text='subdiv')
        row = col.row(align=True)
        row.prop(self, 'bspline', text='bspline', toggle=True)
        row.prop(self, 'close', text='close', toggle=True)
        if self.inputs['bevel object'].sv_get(default=[]):
            row.prop(self, 'caps', text='caps', toggle=True)
        row = col.row(align=True)
        row.prop(self, 'show_wire', text='wire', toggle=True)
        row.prop(self, 'use_smooth', text='smooth', toggle=True)
        row.separator()
        row.prop(self, 'selected_mode', expand=True)

    def draw_buttons_ext(self, context, layout):
        self.draw_buttons(context, layout)
        self.draw_ext_object_buttons(context, layout)

    def get_geometry_from_sockets(self, has_matrices):

        def get(socket_name):
            data = self.inputs[socket_name].sv_get(default=[])
            return dataCorrect(data)

        mverts = get('vertices')
        mradii = self.inputs['radii'].sv_get(deepcopy=True)
        mtwist = self.inputs['twist'].sv_get(deepcopy=True)
        mmtrix = get('matrix')

        # extend all non empty lists to longest of these
        maxlen = max(len(mverts), len(mmtrix))
        if has_matrices:
            fullList(mverts, maxlen)
            fullList(mmtrix, maxlen)

        if mradii:
            fullList(mradii, maxlen)
        if mtwist:
            fullList(mtwist, maxlen)

        return mverts, mradii, mtwist, mmtrix

    def process(self):
        if not self.activate:
            return

        if not self.inputs['vertices'].is_linked:
            return

        has_matrices = self.inputs['matrix'].is_linked
        mverts, mradii, mtwist, mmatrices = self.get_geometry_from_sockets(has_matrices)

        out_objects = []
        for obj_index, Verts in enumerate(mverts):
            if not Verts:
                continue

            matrix = mmatrices[obj_index] if has_matrices else []

            if self.selected_mode == 'Multi':
                curve_args = obj_index, self, Verts, matrix, mradii[obj_index], mtwist[obj_index]
                new_obj = make_curve_geometry(*curve_args)
                out_objects.append(new_obj)
            else:
                if matrix:
                    mverts = [multiply_vectors(*mv) for mv in zip(mmatrices, mverts)]
                new_obj = make_curve_geometry(0, self, mverts, [], mradii, mtwist)
                out_objects.append(new_obj)
                break

        last_index = len(mverts) - 1
        self.remove_non_updated_objects(last_index)
        self.set_corresponding_materials()

        self.outputs['object'].sv_set(out_objects)

def register():
    bpy.utils.register_class(SvPolylineViewerNodeV28)

def unregister():
    bpy.utils.unregister_class(SvPolylineViewerNodeV28)


import bpy
from bpy.props import (BoolProperty, StringProperty, FloatProperty, IntProperty)

from sverchok.utils.sv_obj_helper import SvObjHelper
from sverchok.utils.geom import multiply_vectors
from sverchok.node_tre

e import SverchCustomTreeNode
from sverchok.data_structure import dataCorrect, fullList, updateNode

def set_bevel_object(node, cu, obj_index):
    # use bevel object if provided
    bevel_objs = node.inputs['bevel object'].sv_get(default=[])
    if bevel_objs:
        obj_ref = bevel_objs[obj_index] if obj_index < len(bevel_objs) else bevel_objs[-1]
        if obj_ref.type == 'CURVE':
            cu.bevel_object = obj_ref
            cu.use_fill_caps = node.caps
    else:
        cu.bevel_object = None
        cu.use_fill_caps = False

def live_curve(obj_index, node, verts, radii, twist):

    obj, cu = node.get_obj_curve(obj_index)

    obj.show_wire = node.show_wire
    cu.bevel_depth = node.depth
    cu.bevel_resolution = node.resolution
    cu.dimensions = node.curve_dimensions
    if cu.dimensions == '2D':
        cu.fill_mode = 'FRONT'
    else:
        cu.fill_mode = 'FULL'

    set_bevel_object(node, cu, obj_index)

    kind = ["POLY", "NURBS"][bool(node.bspline)]

    if node.selected_mode == 'Multi':
        verts = [verts]
        radii = [radii]
        twist = [twist]

    for idx, (VERTS, RADII, TWIST) in enumerate(zip(verts, radii, twist)):

        full_flat = []
        for v in VERTS:
            full_flat.extend([v[0], v[1], v[2], 1.0])

        polyline = cu.splines.new(kind)
        polyline.points.add(len(VERTS)-1)
        polyline.points.foreach_set('co', full_flat)

        if RADII:
            if len(VERTS) < len(RADII):
                RADII = RADII[:len(VERTS)]
            elif len(VERTS) > len(RADII):
                fullList(RADII, len(VERTS))
            polyline.points.foreach_set('radius', RADII)

        if TWIST:
            if len(VERTS) < len(TWIST):
                TWIST = TWIST[:len(VERTS)]
            elif len(VERTS) > len(TWIST):
                fullList(TWIST, len(VERTS))
            polyline.points.foreach_set('tilt', TWIST)

        if node.close:
            cu.splines[idx].use_cyclic_u = True

        if node.bspline:
            polyline.order_u = len(polyline.points)-1

        polyline.use_smooth = node.use_smooth

    return obj

def make_curve_geometry(obj_index, node, verts, matrix, radii, twist):
    sv_object = live_curve(obj_index, node, verts, radii, twist)
    sv_object.hide_select = False
    node.push_custom_matrix_if_present(sv_object, matrix)
    return sv_object

class SvPolylineViewerNodeV28(bpy.types.Node, SverchCustomTreeNode, SvObjHelper):

    bl_idname = 'SvPolylineViewerNodeV28'
    bl_label = 'Polyline Viewer'
    bl_icon = 'MOD_CURVE'

    mode_options = [(k, k, '', i) for i, k in enumerate(["Multi", "Single"])]
    selected_mode: bpy.props.EnumProperty(
        items=mode_options,
        description="offers joined of unique curves",
        default="Multi", update=updateNode
    )

    dimension_modes = [(k, k, '', i) for i, k in enumerate(["3D", "2D"])]

    curve_dimensions: bpy.props.EnumProperty(
        items=dimension_modes, update=updateNode,
        description="2D or 3D curves", default="3D"
    )

    depth: FloatProperty(min=0.0, default=0.2, update=updateNode)
    resolution: IntProperty(min=0, default=3, update=updateNode)
    bspline: BoolProperty(default=False, update=updateNode)
    close: BoolProperty(default=False, update=updateNode)

    radii: FloatProperty(min=0, default=0.2, update=updateNode)
    twist: FloatProperty(default=0.0, update=updateNode)
    caps: BoolProperty(update=updateNode)

    data_kind: StringProperty(default='CURVE')

    def sv_init(self, context):
        self.sv_init_helper_basedata_name()

        self.inputs.new('VerticesSocket', 'vertices')
        self.inputs.new('MatrixSocket', 'matrix')
        self.inputs.new('StringsSocket', 'radii').prop_name = 'radii'
        self.inputs.new('StringsSocket', 'twist').prop_name = 'twist'
        self.inputs.new('SvObjectSocket', 'bevel object')
        self.outputs.new('SvObjectSocket', "object")

    def draw_buttons(self, context, layout):

        self.draw_live_and_outliner(context, layout)
        self.draw_object_buttons(context, layout)

        layout.row().prop(self, 'curve_dimensions', expand=True)

        col = layout.column()
        if self.curve_dimensions == '3D':
            r1 = col.row(align=True)
            r1.prop(self, 'depth', text='radius')
            r1.prop(self, 'resolution', text='subdiv')
        row = col.row(align=True)
        row.prop(self, 'bspline', text='bspline', toggle=True)
        row.prop(self, 'close', text='close', toggle=True)
        if self.inputs['bevel object'].sv_get(default=[]):
            row.prop(self, 'caps', text='caps', toggle=True)
        row = col.row(align=True)
        row.prop(self, 'show_wire', text='wire', toggle=True)
        row.prop(self, 'use_smooth', text='smooth', toggle=True)
        row.separator()
        row.prop(self, 'selected_mode', expand=True)

    def draw_buttons_ext(self, context, layout):
        self.draw_buttons(context, layout)
        self.draw_ext_object_buttons(context, layout)

    def get_geometry_from_sockets(self, has_matrices):

        def get(socket_name):
            data = self.inputs[socket_name].sv_get(default=[])
            return dataCorrect(data)

        mverts = get('vertices')
        mradii = self.inputs['radii'].sv_get(deepcopy=True)
        mtwist = self.inputs['twist'].sv_get(deepcopy=True)
        mmtrix = get('matrix')

        # extend all non empty lists to longest of these
        maxlen = max(len(mverts), len(mmtrix))
        if has_matrices:
            fullList(mverts, maxlen)
            fullList(mmtrix, maxlen)

        if mradii:
            fullList(mradii, maxlen)
        if mtwist:
            fullList(mtwist, maxlen)

        return mverts, mradii, mtwist, mmtrix

    def process(self):
        if not self.activate:
            return

        if not self.inputs['vertices'].is_linked:
            return

        has_matrices = self.inputs['matrix'].is_linked
        mverts, mradii, mtwist, mmatrices = self.get_geometry_from_sockets(has_matrices)

        out_objects = []
        for obj_index, Verts in enumerate(mverts):
            if not Verts:
                continue

            matrix = mmatrices[obj_index] if has_matrices else []

            if self.selected_mode == 'Multi':
                curve_args = obj_index, self, Verts, matrix, mradii[obj_index], mtwist[obj_index]
                new_obj = make_curve_geometry(*curve_args)
                out_objects.append(new_obj)
            else:
                if matrix:
                    mverts = [multiply_vectors(*mv) for mv in zip(mmatrices, mverts)]
                new_obj = make_curve_geometry(0, self, mverts, [], mradii, mtwist)
                out_objects.append(new_obj)
                break

        last_index = len(mverts) - 1
        self.remove_non_updated_objects(last_index)
        self.set_corresponding_materials()

        self.outputs['object'].sv_set(out_objects)

def register():
    bpy.utils.register_class(SvPolylineViewerNodeV28)

def unregister():
    bpy.utils.unregister_class(SvPolylineViewerNodeV28)


import json

from alipay.aop.api.constant.ParamConstants import *

class ZhimaCreditWatchlistBriefGetModel(object):

    def __init__(self):
        self._cert_no = None
        self._cert_type = None
        self._name = None
        self._product_code = None
        self._transaction_id = None

    @property
    def cert_no(self):
        return self._cert_no

    @cert_no.setter
    def cert_no(self, value):
        self._cert_no = value
    @property
    def cert_type(self):
        return self._cert_type

    @cert_type.setter
    def cert_type(self, value):
        self._cert_type = value
    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value
    @property

return self._product_code

    @product_code.setter
    def product_code(self, value):
        self._product_code = value
    @property
    def transaction_id(self):
        return self._transaction_id

    @transaction_id.setter
    def transaction_id(self, value):
        self._transaction_id = value

    def to_alipay_dict(self):
        params = dict()
        if self.cert_no:
            if hasattr(self.cert_no, 'to_alipay_dict'):
                params['cert_no'] = self.cert_no.to_alipay_dict()
            else:
                params['cert_no'] = self.cert_no
        if self.cert_type:
            if hasattr(self.cert_type, 'to_alipay_dict'):
                params['cert_type'] = self.cert_type.to_alipay_dict()
            else:
                params['cert_type'] = self.cert_type
        if self.name:
            if hasattr(self.name, 'to_alipay_dict'):
                params['name'] = self.name.to_alipay_dict()
            else:
                params['name'] = self.name
        if self.product_code:
            if hasattr(self.product_code, 'to_alipay_dict'):
                params['product_code'] = self.product_code.to_alipay_dict()
            else:
                params['product_code'] = self.product_code
        if self.transaction_id:
            if hasattr(self.transaction_id, 'to_alipay_dict'):
                params['transaction_id'] = self.transaction_id.to_alipay_dict()
            else:
                params['transaction_id'] = self.transaction_id
        return params

    @staticmethod
    def from_alipay_dict(d):
        if not d:
            return None
        o = ZhimaCreditWatchlistBriefGetModel()
        if 'cert_no' in d:
            o.cert_no = d['cert_no']
        if 'cert_type' in d:
            o.cert_type = d['cert_type']
        if 'name' in d:
            o.name = d['name']
        if 'product_code' in d:
            o.product_code = d['product_code']
        if 'transaction_id' in d:
            o.transaction_id = d['transaction_id']
        return o