# -*- coding: utf-8 -*-
"""These preprocessor functions are used to process data prior to output."""

import string

from cli_helpers import utils
from cli_helpers.compat import (text_type, int_types, float_types,
                                HAS_PYGMENTS, Terminal256Formatter, StringIO)

def truncate_string(data, headers, max_field_width=None, **_):
    """Truncate very long strings. Only needed for tabular
    representation, because trying to tabulate very long data
    is problematic in terms of performance, and does not make any
    sense visually.

    :param iterable data: An :term:`iterable` (e.g. list) of rows.
    :param iterable headers: The column headers.
    :param int max_field_width: Width to truncate field for display
    :return: The processed data and headers.
    :rtype: tuple
    """
    return (([utils.truncate_string(v, max_field_width) for v in row] for row in data),
            [utils.truncate_string(h, max_field_width) for h in headers])

def convert_to_string(data, headers, **_):
    """Convert all *data* and *headers* to strings.

    Binary data that cannot be decoded is converted to a hexadecimal
    representation via :func:`binascii.hexlify`.

    :param iterable data: An :term:`iterable` (e.g. list) of rows.
    :param iterable headers: The column headers.
    :return: The processed data and headers.
    :rtype: tuple

    """
    return (([utils.to_string(v) for v in row] for row in data),
            [utils.to_string(h) for h in headers])

def override_missing_value(data, headers, missing_value='', **_):
    """Override missing values in the *data* with *missing_value*.

    A missing value is any value that is :data:`None`.

    :param iterable data: An :term:`iterable` (e.g. list) of rows.
    :param iterable headers: The column headers.
    :param missing_value: The default value to use for missing data.
    :return: The processed data and headers.
    :rtype: tuple

    """
    return (([missing_value if v is None else v for v in row] for row in data),
            headers)

def override_tab_value(data, headers, new_value='    ', **_):
    """Override tab values in the *data* with *new_value*.

    :param iterable data: An :term:`iterable` (e.g. list) of rows.
    :param iterable headers: The column headers.
    :param new_value: The new value to use for tab.
    :return: The processed data and headers.
    :rtype: tuple

    """
    return (([v.replace('\t', new_value) if isinstance(v, text_type) else v
              for v in row] for row in data),
            headers)

def bytes_to_string(data, headers, **_):
    """Convert all *data* and *headers* bytes to strings.

    Binary data that cannot be decoded is converted to a hexadecimal
    representation via :func:`binascii.hexlify`.

    :param iterable data: An :term:`iterable` (e.g. list) of rows.
    :param iterable headers: The column headers.
    :return: The processed data and headers.
    :rtype: tuple

    """
    return (([utils.bytes_to_string(v) for v in row] for row in data),
            [utils.bytes_to_string(h) for h in headers])

def align_decimals(data, headers, column_types=(), **_):
    """Align numbers in *data* on their decimal points.

    Whitespace padding is added before a number so that all numbers in a
    column are aligned.

    Outputting data before aligning the decimals::

        1
        2.1
        10.59

    Outputting data after aligning the decimals::

         1
         2.1
        10.59

    :param iterable data: An :term:`iterable` (e.g. list) of rows.
    :param iterable headers: The column headers.
    :param iterable column_types: The columns' type objects (e.g. int or float).
    :return: The processed data and headers.
    :rtype: tuple

    """
    pointpos = len(headers) * [0]
    data = list(data)
    for row in data:
        for i, v in enumerate(row):
            if column_types[i] is float and type(v) in float_types:
                v = text_type(v)
                pointpos[i] = max(utils.intlen(v), pointpos[i])

    def results(data):
        for row in data:
            result = []
            for i, v in enumerate(row):
                if column_types[i] is float and type(v) in float_types:
                    v = text_type(v)
                    result.append((pointpos[i] - utils.intlen(v)) * " " + v)
                else:
                    result.append(v)
            yield result

    return results(data), headers

def quote_whitespaces(data, headers, quotestyle="'", **_):
    """Quote leading/trailing whitespace in *data*.

    When outputing data with leading or trailing whitespace, it can be useful
    to put quotation marks around the value so the whitespace is more
    apparent. If one value in a column needs quoted, then all values in that
    column are quoted to keep things consistent.

    .. NOTE::
       :data:`string.whitespace` is used to determine which characters are
       whitespace.

    :param iterable data: An :term:`iterable` (e.g. list) of rows.
    :param iterable headers: The column headers.
    :param str quotestyle: The quotation mark to use (defaults to ``'``).
    :return: The processed data and headers.
    :rtype: tuple

    """
    whitespace = tuple(string.whitespace)
    quote = len(headers) * [False]
    data = list(data)
    for row in data:
        for i, v in enumerate(row):
            v = text_type(v)
            if v.startswith(whitespace) or v.endswith(whitespace):
                quote[i] = True

    def results(data):
        for row in data:
            result = []
            for i, v in enumerate(row):
                quotation = quotestyle if quote[i] else ''
                result.append('{quotestyle}{value}{quotestyle}'.format(
                    quotestyle=quotation, value=v))
            yield result
    return results(data), headers

def style_output(data, headers, style=None,
                 header_token='Token.Output.Header',
                 odd_row_token='Token.Output.OddRow',
                 even_row_token='Token.Output.EvenRow', **_):
    """Style the *data* and *headers* (e.g. bold, italic, and colors)

    .. NOTE::
        This requires the `Pygments <http://pygments.org/>`_ library to
        be installed. You can install it with CLI Helpers as an extra::
            $ pip install cli_helpers[styles]

    Example usage::

        from cli_helpers.tabular_output.preprocessors import style_output
        from pygments.style import Style
        from pygments.token import Token

        class YourStyle(Style):
            default_style = ""
            styles = {
                Token.Output.Header: 'bold #ansired',
                Token.Output.OddRow: 'bg:#eee #111',
                Token.Output.EvenRow: '#0f0'
            }

        headers = ('First Name', 'Last Name')
        data = [['Fred', 'Roberts'], ['George', 'Smith']]

        data, headers = style_output(data, headers, style=YourStyle)

    :param iterable data: An :term:`iterable` (e.g. list) of rows.
    :param iterable headers: The column headers.
    :param str/pygments.style.Style style: A Pygments style. You can `create
        your own styles <http://pygments.org/docs/styles/#creating-own-styles>`_.
    :param str header_token: The token type to be used for the headers.
    :param str odd_row_token: The token type to be used for odd rows.
    :param str even_row_token: The token type to be used for even rows.
    :return: The styled data and headers.
    :rtype: tuple

    """
    if style and HAS_PYGMENTS:
        formatter = Terminal256Formatter(style=style)

        def style_field(token, field):
            """Get the styled text for a *field* using *token* type."""
            s = StringIO()
            formatter.format(((token, field),), s)
            return s.getvalue()

        headers = [style_field(header_token, header) for header in headers]
        data = ([style_field(odd_row_token if i % 2 else even_row_token, f)
                 for f in r] for i, r in enumerate(data, 1))

    return iter(data), headers

def format_numbers(data, headers, column_types=(), integer_format=None,
                   float_format=None, **_):
    """Format numbers according to a format specification.

    This uses Python's format specification to format numbers of the following
    types: :class:`int`, :class:`py2:long` (Python 2), :class:`float`, and
    :class:`~decimal.Decimal`. See the :ref:`python:formatspec` for more
    information about the format strings.

    .. NOTE::
       A column is only formatted if all of its values are the same type
       (except for :data:`None`).

    :param iterable data: An :term:`iterable` (e.g. list) of rows.
    :param iterable headers: The column headers.
    :param iterable column_types: The columns' type objects (e.g. int or float).
    :param str integer_format: The format string to use for integer columns.
    :param str float_format: The format string to use for float columns.
    :return: The processed data and headers.
    :rtype: tuple

    """
    if (integer_format is None and float_format is None) or not column_types:
        return iter(data), headers

    def _format_number(field, column_type):
        if integer_format and column_type is int and type(field) in int_types:
            return format(field, integer_format)
        elif float_format and column_type is float and type(field) in float_types:
            return format(field, float_format)
        return field

    data = ([_format_number(v, column_types[i]) for i, v in enumerate(row)] for row in data)
    return data, headers


# -*- coding: utf-8 -*-
"""Format data into a vertical table layout."""

from __future__ import unicode_literals

from cli_helpers.utils import filter_dict_by_key
from .preprocessors import (convert_to_string, override_missing_value,
                            style_output)

supported_formats = ('vertical', )
preprocessors = (override_missing_value, convert_to_string, style_output)

def _get_separator(num, sep_title, sep_character, sep_length):
    """Get a row separator for row *num*."""
    left_divider_length = right_divider_length = sep_length
    if isinstance(sep_length, tuple):
        left_divider_length, right_divider_length = sep_length
    left_divider = sep_character * left_divider_length
    right_divider = sep_character * right_divider_length
    title = sep_title.format(n=num + 1)

    return "{left_divider}[ {title} ]{right_divider}\n".format(
        left_divider=left_divider, right_divider=right_divider, title=title)

def _format_row(headers, row):
    """Format a row."""
    formatted_row = [' | '.join(field) for field in zip(headers, row)]
    return '\n'.join(formatted_row)

def vertical_table(data, headers, sep_title='{n}. row', sep_character='*',
                   sep_length=27):
    """Format *data* and *headers* as an vertical table.

    The values in *data* and *headers* must be strings.

    :param iterable data: An :term:`iterable` (e.g. list) of rows.
    :param iterable headers: The column headers.
    :param str sep_title: The title given to each row separator. Defaults to
                          ``'{n}. row'``. Any instance of ``'{n}'`` is
                          replaced by the record number.
    :param str sep_character: The character used to separate rows. Defaults to
                              ``'*'``.
    :param int/tuple sep_length: The number of separator characters that should
                                 appear on each side of the *sep_title*. Use
                                 a tuple to specify the left and right values
                                 separately.
    :return: The formatted data.
    :rtype: str

    """
    header_len = max([len(x) for x in headers])
    padded_headers = [x.ljust(header_len) for x in headers]
    formatted_rows = [_format_row(padded_headers, row) for row in data]

    output = []
    for i, result in enumerate(formatted_rows):
        yield _get_separator(i, sep_title, sep_character, sep_length) + result

def adapter(data, headers, **kwargs):
    """Wrap vertical table in a function for TabularOutputFormatter."""
    keys = ('sep_title', 'sep_character', 'sep_length')
    return vertical_table(data, headers, **filter_dict_by_key(kwargs, keys))


# -*- coding: utf-8 -*-
"""A delimited data output adapter (e.g. CSV, TSV)."""

from __future__ import unicode_literals
import contextlib

from cli_helpers.compat import csv, StringIO
from cli_helpers.utils import filter_dict_by_key
from .preprocessors import bytes_to_string, override_missing_value

supported_formats = ('csv', 'csv-tab')
preprocessors = (override_missing_value, bytes_to_string)

class linewriter(object):
    def __init__(self):
        self.reset()

    def reset(self):
        self.line = None

    def write(self, d):
        self.line = d

def adapter(data, headers, table_format='csv', **kwargs):
    """Wrap the formatting inside a function for TabularOutputFormatter."""
    keys = ('dialect', 'delimiter', 'doublequote', 'escapechar',
            'quotechar', 'quoting', 'skipinitialspace', 'strict')
    if table_format == 'csv':
        delimiter = ','
    elif table_format == 'csv-tab':
        delimiter = '\t'
    else:
        raise ValueError('Invalid table_format specified.')

    ckwargs = {'delimiter': delimiter, 'lineterminator': ''}
    ckwargs.update(filter_dict_by_key(kwargs, keys))

    l = linewriter()
    writer = csv.writer(l, **ckwargs)
    writer.writerow(headers)
    yield l.line

    for row in data:
        l.reset()
        writer.writerow(row)
        yield l.line


# -*- coding: utf-8 -*-
"""Format adapter for the terminaltables module."""

from __future__ import unicode_literals

import terminaltables
import itertools

from cli_helpers.utils import filter_dict_by_key
from .preprocessors import (convert_to_string, truncate_string, override_missing_value,
                            style_output, HAS_PYGMENTS, Terminal256Formatter, StringIO,
                            override_tab_value)

supported_formats = ('ascii', 'double', 'github')
preprocessors = (
    override_missing_value, convert_to_string, override_tab_value,
    truncate_string, style_output
)

table_format_handler = {
    'ascii': terminaltables.AsciiTable,
    'double': terminaltables.DoubleTable,
    'github': terminaltables.GithubFlavoredMarkdownTable,
}

def style_output_table(format_name=""):
    def style_output(data, headers, style=None,
                     table_separator_token='Token.Output.TableSeparator', **_):
        """Style the *table* (e.g. bold, italic, and colors)

        .. NOTE::
            This requires the `Pygments <http://pygments.org/>`_ library to
            be installed. You can install it with CLI Helpers as an extra::
                $ pip install cli_helpers[styles]

        Example usage::

            from cli_helpers.tabular_output import terminaltables_adapter
            from pygments.style import Style
            from pygments.token import Token

            class YourStyle(Style):
                default_style = ""
                styles = {
                    Token.Output.TableSeparator: '#ansigray'
                }

            headers = ('First Name', 'Last Name')
            data = [['Fred', 'Roberts'], ['George', 'Smith']]
            style_output_table = terminaltables_adapter.style_output_table('psql')
            style_output_table(data, headers, style=CliStyle)

            output = terminaltables_adapter.adapter(data, headers, style=YourStyle)

        :param iterable data: An :term:`iterable` (e.g. list) of rows.
        :param iterable headers: The column headers.
        :param str/pygments.style.Style style: A Pygments style. You can `create
            your own styles <http://pygments.org/docs/styles/#creating-own-styles>`_.
        :param str table_separator_token: The token type to be used for the table separator.
        :return: data and headers.
        :rtype: tuple

        """
        if style and HAS_PYGMENTS and format_name in supported_formats:
            formatter = Terminal256Formatter(style=style)

            def style_field(token, field):
                """Get the styled text for a *field* using *token* type."""
                s = StringIO()
                formatter.format(((token, field),), s)
                return s.getvalue()

            clss = table_format_handler[format_name]
            for char in [char for char in terminaltables.base_table.BaseTable.__dict__ if char.startswith("CHAR_")]:
                setattr(clss, char, style_field(
                    table_separator_token, getattr(clss, char)))

        return iter(data), headers
    return style_output

def adapter(data, headers, table_format=None, **kwargs):
    """Wrap terminaltables inside a function for TabularOutputFormatter."""
    keys = ('title', )

    table = table_format_handler[table_format]

    t = table([headers] + list(data), **filter_dict_by_key(kwargs, keys))

    dimensions = terminaltables.width_and_alignment.max_dimensions(
        t.table_data,
        t.padding_left,
        t.padding_right)[:3]
    for r in t.gen_table(*dimensions):
        yield u''.join(r)


#!/usr/bin/env python
# -*- coding: utf-8 -*-

#
# Copyright 2014, 2015, 2016, 2017 Guenter Bartsch
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

#
# just a collection of random utility subprograms
#

import sys
import os
import subprocess
try:
    import ConfigParser as configparser
except ImportError:
    import configparser
try:
    from imp import reload
except ImportError:
    pass
import shutil
import errno
import curses
import curses.textpad
import traceback
import logging
import code
import signal

from setproctitle import setproctitle
from os.path import expanduser

def load_config(configfn = '.nlprc', defaults={}):

    home_path = expanduser("~")

    config = configparser.ConfigParser(defaults)
    config.read("%s/%s" % (home_path, configfn))

    return config

def _debug(sig, frame):
    """Interrupt running process, and provide a python prompt for
    interactive debugging.

    source: http://stackoverflow.com/questions/132058/showing-the-stack-trace-from-a-running-python-application
    """
    d={'_frame':frame}         # Allow access to frame object.
    d.update(frame.f_globals)  # Unless shadowed by global
    d.update(frame.f_locals)

    i = code.InteractiveConsole(d)
    message  = "Signal received : entering python shell.\nTraceback:\n"
    message += ''.join(traceback.format_stack(frame))
    i.interact(message)

def init_app (proc_title):

    setproctitle (proc_title)

    if sys.version_info < (3, 0):
        reload(sys)
        sys.setdefaultencoding('utf-8')
        sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

    # install signal handler so SIGUSR1 will enter pdb

    signal.signal(signal.SIGUSR1, _debug)  # Register handler

def compress_ws (s):

    vc = True

    res = ''

    for c in s:

        if c == ' ':
            vc = False
        else:
            if vc:
                res = res + c
            else:
                res = res + ' ' + c
            vc = True

    return res

def run_command(command, capture_stderr=True):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT if capture_stderr else subprocess.PIPE)
    return iter(p.stdout.readline, b'')

tex_umlaut_map = { u'ä': '"a', u'ü': '"u', u'ö': '"o', u'Ä':'"A', u'Ü':'"U', u'Ö':'"O', u'ß':'"s' }

def tex_encode (u):

    s = ''

    for c in u:

        if c in tex_umlaut_map:
            s += tex_umlaut_map[c]
        else:
            s += str(c)

    return s

def tex_decode (s):

    u = ''

    pos = 0
    while (pos < len(s)):

        found = False

        for umlaut in tex_umlaut_map:
            v = tex_umlaut_map[umlaut]
            if s[pos:].startswith(v):
                u += umlaut
                pos += len(v)
                found = True
                break

        if not found:
            u += unicode(s[pos])
            pos += 1

    return u

def symlink(targetfn, linkfn):
    try:
        os.symlink(targetfn, linkfn)
    except OSError as e:
        if e.errno == errno.EEXIST:
            logging.debug('symlink %s -> %s already exists' % (targetfn, linkfn))

def mkdirs(path):
    try:
        os.makedirs(path)
    except OSError as exception:
        if exception.errno != errno.EEXIST:
            raise

def copy_file (src, dst):
    logging.debug("copying %s to %s" % (src, dst))
    shutil.copy(src, dst)

def edit_distance (s, t):
    # https://en.wikipedia.org/wiki/Wagner%E2%80%93Fischer_algorithm

    # for all i and j, d[i,j] will hold the Levenshtein distance between
    # the first i words of s and the first j words of t;
    # note that d has (m+1)x(n+1) values

    m = len(s)
    n = len(t)

    d = [[0 for i in range(n+1)] for j in range(m+1)]

    for i in range (m+1):
        d[i][0] = i                        # the distance of any first seq to an empty second seq
    for j in range (n+1):
        d[0][j] = j                         # the distance of any second seq to an empty first seq

    for j in range (1, n+1):
        for i in range (1, m+1):

            if s[i-1] == t[j-1]:
                d[i][j] = d[i-1][j-1]       # no operation required
            else:
                d[i][j] = min ([
                            d[i-1][j] + 1,       # a deletion
                            d[i][j-1] + 1,       # an insertion
                            d[i-1][j-1] + 1      # a substitution
                         ])

    return d[m][n]

def limit_str(s, limit):

    l = len(s)

    if l<=limit:
        return s

    l = limit-3

    return s[:l] + '...'

#
# curses utils
#

def edit_popup (stdscr, title, s):

    my, mx = stdscr.getmaxyx()

    ww = mx * 9 / 10
    wh = 3

    wox = mx / 2 - ww/2
    woy = my / 2 - wh/2

    win = curses.newwin(wh, ww, woy, wox)
    win.box()
    win.addstr(0, 3, title)

    win.refresh()

    swin = win.derwin (1, ww-4, 1, 2)

    tb = curses.textpad.Textbox(swin, insert_mode=True)

    swin.insstr (0, 0, tex_encode(s))

    swin.refresh()

    s = tex_decode(tb.edit())

    return s.rstrip()

def message_popup (stdscr, title, msg):

    my, mx = stdscr.getmaxyx()

    ww = len(title)

    lines = msg.split('\n')
    for line in lines:
        if len(line)>ww:
            ww = len(line)
    ww += 6
    wh = len(lines) + 2

    wox = mx / 2 - ww/2
    woy = my / 2 - wh/2

    win = curses.newwin(wh, ww, woy, wox)
    win.box()
    win.addstr(0, 3, title.encode('utf8'))

    win.refresh()

    swin = win.derwin (wh-2, ww-4, 1, 1)

    for i, line in enumerate(lines):
        swin.insstr (i, 0, line.encode('utf8'))

    swin.refresh()

    return swin

def render_template(template_file, dst_file, **kwargs):
    """Copy template and substitute template strings

    File `template_file` is copied to `dst_file`. Then, each template variable
    is replaced by a value. Template variables are of the form

        {{val}}

    Example:

    Contents of template_file:

        VAR1={{val1}}
        VAR2={{val2}}
        VAR3={{val3}}

    render_template(template_file, output_file, val1="hello", val2="world")

    Contents of output_file:

        VAR1=hello
        VAR2=world
        VAR3={{val3}}

    :param template_file: Path to the template file.
    :param dst_file: Path to the destination file.
    :param kwargs: Keys correspond to template variables.
    :return:
    """
    with open(template_file) as f:
        template_text = f.read()

    dst_text = template_text

    for key, value in kwargs.iteritems():
        dst_text = dst_text .replace("{{" + key + "}}", value)

    with open(dst_file, "wt") as f:
        f.write(dst_text)


#   Copyright (C) 2006-2015 Ludovic Rousseau (ludovic.rousseau@free.fr)
#   Copyright (C) 2010 Giuseppe Amato (additions to original interface)
#
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.

from __future__ import print_function

import PyKCS11.LowLevel
import os
import sys

# redefine PKCS#11 constants
CK_TRUE = PyKCS11.LowLevel.CK_TRUE
CK_FALSE = PyKCS11.LowLevel.CK_FALSE
CK_UNAVAILABLE_INFORMATION = PyKCS11.LowLevel.CK_UNAVAILABLE_INFORMATION
CK_EFFECTIVELY_INFINITE = PyKCS11.LowLevel.CK_EFFECTIVELY_INFINITE
CK_INVALID_HANDLE = PyKCS11.LowLevel.CK_INVALID_HANDLE

CKM = {}
CKR = {}
CKA = {}
CKO = {}
CKU = {}
CKK = {}
CKC = {}
CKF = {}
CKS = {}
CKG = {}
CKZ = {}

# redefine PKCS#11 constants using well known prefixes
for x in PyKCS11.LowLevel.__dict__.keys():
    if x[:4] == 'CKM_' \
      or x[:4] == 'CKR_' \
      or x[:4] == 'CKA_' \
      or x[:4] == 'CKO_' \
      or x[:4] == 'CKU_' \
      or x[:4] == 'CKK_' \
      or x[:4] == 'CKC_' \
      or x[:4] == 'CKF_' \
      or x[:4] == 'CKS_' \
      or x[:4] == 'CKG_' \
      or x[:4] == 'CKZ_':
        a = "%s=PyKCS11.LowLevel.%s" % (x, x)
        exec(a)
        if x[3:] != "_VENDOR_DEFINED":
            eval(x[:3])[eval(x)] = x  # => CKM[CKM_RSA_PKCS] = 'CKM_RSA_PKCS'
            eval(x[:3])[x] = eval(x)  # => CKM['CKM_RSA_PKCS'] = CKM_RSA_PKCS

# special CKR[] values
CKR[-3] = "Unknown format"
CKR[-2] = "Unkown PKCS#11 type"
CKR[-1] = "Load"

class ckbytelist(PyKCS11.LowLevel.ckbytelist):
    """
    add a __repr__() method to the LowLevel equivalent
    """

    def __init__(self, data=[]):
        # default size of the vector
        size = 0
        super(ckbytelist, self).__init__(size)

        # No value to initialize
        if data is None:
            return

        # b'abc'
        if isinstance(data, bytes):
            self.reserve(len(data))
            for x in data:
                if sys.version_info[0] <= 2:
                    # Python 2
                    v = ord(x)
                else:
                    # Python 3 and more
                    v = x
                self.append(v)

        # "abc"
        elif isinstance(data, str):
            tmp = bytes(data, "utf-8")
            self.reserve(len(tmp))
            for x in tmp:
                self.append(x)

        # [141, 142, 143]
        elif isinstance(data, list) or isinstance(data, ckbytelist):
            self.reserve(len(data))
            for c in range(len(data)):
                self.append(data[c])
        else:
            raise PyKCS11.PyKCS11Error(-3, text=type(data))

    def __repr__(self):
        """
        return the representation of a tuple
        the __str__ method will use it also
        """
        rep = [int(elt) for elt in self]
        return repr(rep)

class CK_OBJECT_HANDLE(PyKCS11.LowLevel.CK_OBJECT_HANDLE):
    """
    add a __repr__() method to the LowLevel equivalent
    """

    def __init__(self, session):
        PyKCS11.LowLevel.CK_OBJECT_HANDLE.__init__(self)
        self.session = session
        pass

    def to_dict(self):
        """
        convert the fields of the object into a dictionnary
        """
        # all the attibutes defined by PKCS#11
        all_attributes = PyKCS11.CKA.keys()

        # only use the integer values and not the strings like 'CKM_RSA_PKCS'
        all_attributes = [attr for attr in all_attributes if
                          isinstance(attr, int)]

        # all the attributes of the object
        attributes = self.session.getAttributeValue(self, all_attributes)

        dico = dict()
        for key, attr in zip(all_attributes, attributes):
            if attr is None:
                continue
            if key == CKA_CLASS:
                dico[PyKCS11.CKA[key]] = PyKCS11.CKO[attr]
            elif key == CKA_CERTIFICATE_TYPE:
                dico[PyKCS11.CKA[key]] = PyKCS11.CKC[attr]
            elif key == CKA_KEY_TYPE:
                dico[PyKCS11.CKA[key]] = PyKCS11.CKK[attr]
            else:
                dico[PyKCS11.CKA[key]] = attr
        return dico

    def __repr__(self):
        """
        text representation of the object
        """
        dico = self.to_dict()
        lines = list()
        for key in sorted(dico.keys()):
            lines.append("%s: %s" % (key, dico[key]))
        return "\n".join(lines)

class CkClass(object):
    """
    Base class for CK_* classes
    """

    # dictionnary of integer_value: text_value for the flags bits
    flags_dict = dict()

    # dictionnary of fields names and types
    # type can be "pair", "flags" or "text"
    fields = dict()

    flags = 0

    def flags2text(self):
        """
        parse the `self.flags` field and create a list of `CKF_*` strings
        corresponding to bits set in flags

        :return: a list of strings
        :rtype: list
        """
        r = []
        for v in self.flags_dict.keys():
            if self.flags & v:
                r.append(self.flags_dict[v])
        return r

    def to_dict(self):
        """
        convert the fields of the object into a dictionnary
        """
        dico = dict()
        for field in self.fields.keys():
            if field == "flags":
                dico[field] = self.flags2text()
            elif field == "state":
                dico[field] = self.state2text()
            else:
                dico[field] = eval("self." + field)
        return dico

    def __str__(self):
        """
        text representation of the object
        """
        dico = self.to_dict()
        lines = list()
        for key in sorted(dico.keys()):
            type = self.fields[key]
            if type == "flags":
                lines.append("%s: %s" % (key, ", ".join(dico[key])))
            elif type == "pair":
                lines.append("%s: " % key + "%d.%d" % dico[key])
            else:
                lines.append("%s: %s" % (key, dico[key]))
        return "\n".join(lines)

class CK_SLOT_INFO(CkClass):
    """
    matches the PKCS#11 CK_SLOT_INFO structure

    :ivar slotDescription: blank padded
    :type slotDescription: string
    :ivar manufacturerID: blank padded
    :type manufacturerID: string
    :ivar flags: See :func:`CkClass.flags2text`
    :type flags: integer
    :ivar hardwareVersion: 2 elements list
    :type hardwareVersion: list
    :ivar firmwareVersion: 2 elements list
    :type firmwareVersion: list
    """

    flags_dict = {
        CKF_TOKEN_PRESENT: "CKF_TOKEN_PRESENT",
        CKF_REMOVABLE_DEVICE: "CKF_REMOVABLE_DEVICE",
        CKF_HW_SLOT: "CKF_HW_SLOT"}

    fields = {"slotDescription": "text",
              "manufacturerID": "text",
              "flags": "flags",
              "hardwareVersion": "text",
              "firmwareVersion": "text"}

class CK_INFO(CkClass):
    """
    matches the PKCS#11 CK_INFO structure

    :ivar cryptokiVersion: Cryptoki interface version
    :type cryptokiVersion: integer
    :ivar manufacturerID: blank padded
    :type manufacturerID: string
    :ivar flags: must be zero
    :type flags: integer
    :ivar libraryDescription: blank padded
    :type libraryDescription: string
    :var libraryVersion: 2 elements list
    :type libraryVersion: list
    """

    fields = {"cryptokiVersion": "pair",
              "manufacturerID": "text",
              "flags": "flags",
              "libraryDescription": "text",
              "libraryVersion": "pair"}

class CK_SESSION_INFO(CkClass):
    """
    matches the PKCS#11 CK_SESSION_INFO structure

    :ivar slotID: ID of the slot that interfaces with the token
    :type slotID: integer
    :ivar state: state of the session
    :type state: integer
    :ivar flags: bit flags that define the type of session
    :type flags: integer
    :ivar ulDeviceError: an error code defined by the cryptographic token
    :type ulDeviceError: integer
    """

    flags_dict = {
        CKF_RW_SESSION: "CKF_RW_SESSION",
        CKF_SERIAL_SESSION: "CKF_SERIAL_SESSION",
    }

    def state2text(self):
        """
        parse the `self.state` field and return a `CKS_*` string
        corresponding to the state

        :return: a string
        :rtype: string
        """
        return CKS[self.state]

    fields = {"slotID": "text",
              "state": "text",
              "flags": "flags",
              "ulDeviceError": "text"}

class CK_TOKEN_INFO(CkClass):
    """
    matches the PKCS#11 CK_TOKEN_INFO structure

    :ivar label: blank padded
    :type label: string
    :ivar manufacturerID: blank padded
    :type manufacturerID: string
    :ivar model: string blank padded
    :type model: string
    :ivar serialNumber: string blank padded
    :type serialNumber: string
    :ivar flags:
    :type flags: integer
    :ivar ulMaxSessionCount:
    :type ulMaxSessionCount: integer
    :ivar ulSessionCount:
    :type ulSessionCount: integer
    :ivar ulMaxRwSessionCount:
    :type ulMaxRwSessionCount: integer
    :ivar ulRwSessionCount:
    :type ulRwSessionCount: integer
    :ivar ulMaxPinLen:
    :type ulMaxPinLen: integer
    :ivar ulMinPinLen:
    :type ulMinPinLen: integer
    :ivar ulTotalPublicMemory:
    :type ulTotalPublicMemory: integer
    :ivar ulFreePublicMemory:
    :type ulFreePublicMemory: integer
    :ivar ulTotalPrivateMemory:
    :type ulTotalPrivateMemory: integer
    :ivar ulFreePrivateMemory:
    :type ulFreePrivateMemory: integer
    :ivar hardwareVersion: 2 elements list
    :type hardwareVersion: list
    :ivar firmwareVersion: 2 elements list
    :type firmwareVersion: list
    :ivar utcTime: string
    :type utcTime: string
    """

    flags_dict = {
        CKF_RNG: "CKF_RNG",
        CKF_WRITE_PROTECTED: "CKF_WRITE_PROTECTED",
        CKF_LOGIN_REQUIRED: "CKF_LOGIN_REQUIRED",
        CKF_USER_PIN_INITIALIZED: "CKF_USER_PIN_INITIALIZED",
        CKF_RESTORE_KEY_NOT_NEEDED: "CKF_RESTORE_KEY_NOT_NEEDED",
        CKF_CLOCK_ON_TOKEN: "CKF_CLOCK_ON_TOKEN",
        CKF_PROTECTED_AUTHENTICATION_PATH: "CKF_PROTECTED_AUTHENTICATION_PATH",
        CKF_DUAL_CRYPTO_OPERATIONS: "CKF_DUAL_CRYPTO_OPERATIONS",
        CKF_TOKEN_INITIALIZED: "CKF_TOKEN_INITIALIZED",
        CKF_SECONDARY_AUTHENTICATION: "CKF_SECONDARY_AUTHENTICATION",
        CKF_USER_PIN_COUNT_LOW: "CKF_USER_PIN_COUNT_LOW",
        CKF_USER_PIN_FINAL_TRY: "CKF_USER_PIN_FINAL_TRY",
        CKF_USER_PIN_LOCKED: "CKF_USER_PIN_LOCKED",
        CKF_USER_PIN_TO_BE_CHANGED: "CKF_USER_PIN_TO_BE_CHANGED",
        CKF_SO_PIN_COUNT_LOW: "CKF_SO_PIN_COUNT_LOW",
        CKF_SO_PIN_FINAL_TRY: "CKF_SO_PIN_FINAL_TRY",
        CKF_SO_PIN_LOCKED: "CKF_SO_PIN_LOCKED",
        CKF_SO_PIN_TO_BE_CHANGED: "CKF_SO_PIN_TO_BE_CHANGED",
    }

    fields = {"label": "text",
              "manufacturerID": "text",
              "model": "text",
              "serialNumber": "text",
              "flags": "flags",
              "ulMaxSessionCount": "text",
              "ulSessionCount": "text",
              "ulMaxRwSessionCount": "text",
              "ulRwSessionCount": "text",
              "ulMaxPinLen": "text",
              "ulMinPinLen": "text",
              "ulTotalPublicMemory": "text",
              "ulFreePublicMemory": "text",
              "ulTotalPrivateMemory": "text",
              "ulFreePrivateMemory": "text",
              "hardwareVersion": "pair",
              "firmwareVersion": "pair",
              "utcTime": "text"}

class CK_MECHANISM_INFO(CkClass):
    """
    matches the PKCS#11 CK_MECHANISM_INFO structure

    :ivar ulMinKeySize: minimum size of the key
    :type ulMinKeySize: integer
    :ivar ulMaxKeySize: maximum size of the key
    :type ulMaxKeySize: integer
    :ivar flags: bit flags specifying mechanism capabilities
    :type flags: integer
    """

    flags_dict = {
        CKF_HW: "CKF_HW",
        CKF_ENCRYPT: "CKF_ENCRYPT",
        CKF_DECRYPT: "CKF_DECRYPT",
        CKF_DIGEST: "CKF_DIGEST",
        CKF_SIGN: "CKF_SIGN",
        CKF_SIGN_RECOVER: "CKF_SIGN_RECOVER",
        CKF_VERIFY: "CKF_VERIFY",
        CKF_VERIFY_RECOVER: "CKF_VERIFY_RECOVER",
        CKF_GENERATE: "CKF_GENERATE",
        CKF_GENERATE_KEY_PAIR: "CKF_GENERATE_KEY_PAIR",
        CKF_WRAP: "CKF_WRAP",
        CKF_UNWRAP: "CKF_UNWRAP",
        CKF_DERIVE: "CKF_DERIVE",
        CKF_EXTENSION: "CKF_EXTENSION",
    }

    fields = {"ulMinKeySize": "text",
              "ulMaxKeySize": "text",
              "flags": "flags"}

class PyKCS11Error(Exception):
    """ define the possible PyKCS11 exceptions """

    def __init__(self, value, text=""):
        self.value = value
        self.text = text

    def __str__(self):
        """
        The text representation of a PKCS#11 error is something like:
        "CKR_DEVICE_ERROR (0x00000030)"
        """
        if self.value in CKR:
            if self.value < 0:
                return CKR[self.value] + " (%s)" % self.text
            else:
                return CKR[self.value] + " (0x%08X)" % self.value
        elif self.value & CKR_VENDOR_DEFINED:
            return "Vendor error (0x%08X)" % (self.value & 0xffffffff & ~CKR_VENDOR_DEFINED)
        else:
            return "Unknown error (0x%08X)" % self.value

class PyKCS11Lib(object):
    """ high level PKCS#11 binding """

    def __init__(self):
        self.lib = PyKCS11.LowLevel.CPKCS11Lib()

    def __del__(self):
        if PyKCS11 and PyKCS11.__name__ and \
                PyKCS11.LowLevel and PyKCS11.LowLevel.__name__ and \
                PyKCS11.LowLevel._LowLevel and \
                PyKCS11.LowLevel._LowLevel.__name__:
            self.lib.Unload()

    def load(self, pkcs11dll_filename=None, *init_string):
        """
        load a PKCS#11 library

        :type pkcs11dll_filename: string
        :param pkcs11dll_filename: the library name.
          If this parameter is not set then the environment variable
          `PYKCS11LIB` is used instead
        :returns: a :class:`PyKCS11Lib` object
        :raises: :class:`PyKCS11Error` (-1): when the load fails
        """
        if pkcs11dll_filename is None:
            pkcs11dll_filename = os.getenv("PYKCS11LIB")
            if pkcs11dll_filename is None:
                raise PyKCS11Error(-1, "No PKCS11 library specified (set PYKCS11LIB env variable)")
        rv = self.lib.Load(pkcs11dll_filename)
        if rv == 0:
            raise PyKCS11Error(-1, pkcs11dll_filename)

    def initToken(self, slot, pin, label):
        """
        C_InitToken

        :param slot: slot number returned by :func:`getSlotList`
        :type slot: integer
        :param pin: Security Officer's initial PIN
        :param label: new label of the token
        """
        pin1 = ckbytelist(pin)
        rv = self.lib.C_InitToken(slot, pin1, label)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

    def getInfo(self):
        """
        C_GetInfo

        :return: a :class:`CK_INFO` object
        """
        info = PyKCS11.LowLevel.CK_INFO()
        rv = self.lib.C_GetInfo(info)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

        i = CK_INFO()
        i.cryptokiVersion = (info.cryptokiVersion.major,
                             info.cryptokiVersion.minor)
        i.manufacturerID = info.GetManufacturerID()
        i.flags = info.flags
        i.libraryDescription = info.GetLibraryDescription()
        i.libraryVersion = (info.libraryVersion.major,
                            info.libraryVersion.minor)
        return i

    def getSlotList(self, tokenPresent=False):
        """
        C_GetSlotList

        :param tokenPresent: `False` (default) to list all slots,
          `True` to list only slots with present tokens
        :type tokenPresent: bool
        :return: a list of available slots
        :rtype: list
        """
        slotList = PyKCS11.LowLevel.ckintlist()
        rv = self.lib.C_GetSlotList(CK_TRUE if tokenPresent else CK_FALSE,
                                    slotList)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

        s = []
        for x in range(len(slotList)):
            s.append(slotList[x])
        return s

    def getSlotInfo(self, slot):
        """
        C_GetSlotInfo

        :param slot: slot number returned by :func:`getSlotList`
        :type slot: integer
        :return: a :class:`CK_SLOT_INFO` object
        """
        slotInfo = PyKCS11.LowLevel.CK_SLOT_INFO()
        rv = self.lib.C_GetSlotInfo(slot, slotInfo)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

        s = CK_SLOT_INFO()
        s.slotDescription = slotInfo.GetSlotDescription()
        s.manufacturerID = slotInfo.GetManufacturerID()
        s.flags = slotInfo.flags
        s.hardwareVersion = slotInfo.GetHardwareVersion()
        s.firmwareVersion = slotInfo.GetFirmwareVersion()

        return s

    def getTokenInfo(self, slot):
        """
        C_GetTokenInfo

        :param slot: slot number returned by :func:`getSlotList`
        :type slot: integer
        :return: a :class:`CK_TOKEN_INFO` object
        """
        tokeninfo = PyKCS11.LowLevel.CK_TOKEN_INFO()
        rv = self.lib.C_GetTokenInfo(slot, tokeninfo)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

        t = CK_TOKEN_INFO()
        t.label = tokeninfo.GetLabel()
        t.manufacturerID = tokeninfo.GetManufacturerID()
        t.model = tokeninfo.GetModel()
        t.serialNumber = tokeninfo.GetSerialNumber()
        t.flags = tokeninfo.flags
        t.ulMaxSessionCount = tokeninfo.ulMaxSessionCount
        if t.ulMaxSessionCount == CK_UNAVAILABLE_INFORMATION:
            t.ulMaxSessionCount = -1
        t.ulSessionCount = tokeninfo.ulSessionCount
        if t.ulSessionCount == CK_UNAVAILABLE_INFORMATION:
            t.ulSessionCount = -1
        t.ulMaxRwSessionCount = tokeninfo.ulMaxRwSessionCount
        if t.ulMaxRwSessionCount == CK_UNAVAILABLE_INFORMATION:
            t.ulMaxRwSessionCount = -1
        t.ulRwSessionCount = tokeninfo.ulRwSessionCount
        if t.ulRwSessionCount == CK_UNAVAILABLE_INFORMATION:
            t.ulRwSessionCount = -1
        t.ulMaxPinLen = tokeninfo.ulMaxPinLen
        t.ulMinPinLen = tokeninfo.ulMinPinLen
        t.ulTotalPublicMemory = tokeninfo.ulTotalPublicMemory

        if t.ulTotalPublicMemory == CK_UNAVAILABLE_INFORMATION:
            t.ulTotalPublicMemory = -1
        t.ulFreePublicMemory = tokeninfo.ulFreePublicMemory
        if t.ulFreePublicMemory == CK_UNAVAILABLE_INFORMATION:
            t.ulFreePublicMemory = -1
        t.ulTotalPrivateMemory = tokeninfo.ulTotalPrivateMemory
        if t.ulTotalPrivateMemory == CK_UNAVAILABLE_INFORMATION:
            t.ulTotalPrivateMemory = -1
        t.ulFreePrivateMemory = tokeninfo.ulFreePrivateMemory
        if t.ulFreePrivateMemory == CK_UNAVAILABLE_INFORMATION:
            t.ulFreePrivateMemory = -1
        t.hardwareVersion = (tokeninfo.hardwareVersion.major,
                             tokeninfo.hardwareVersion.minor)
        t.firmwareVersion = (tokeninfo.firmwareVersion.major,
                             tokeninfo.firmwareVersion.minor)
        t.utcTime = tokeninfo.GetUtcTime().replace('\000', ' ')

        return t

    def openSession(self, slot, flags=0):
        """
        C_OpenSession

        :param slot: slot number returned by :func:`getSlotList`
        :type slot: integer
        :param flags: 0 (default), `CKF_RW_SESSION` for RW session
        :type flags: integer
        :return: a :class:`Session` object
        """
        se = PyKCS11.LowLevel.CK_SESSION_HANDLE()
        flags |= CKF_SERIAL_SESSION
        rv = self.lib.C_OpenSession(slot, flags, se)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

        return Session(self, se)

    def closeAllSessions(self, slot):
        """
        C_CloseAllSessions

        :param slot: slot number
        :type slot: integer
        """
        rv = self.lib.C_CloseAllSessions(slot)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

    def getMechanismList(self, slot):
        """
        C_GetMechanismList

        :param slot: slot number returned by :func:`getSlotList`
        :type slot: integer
        :return: the list of available mechanisms for a slot
        :rtype: list
        """
        mechanismList = PyKCS11.LowLevel.ckintlist()
        rv = self.lib.C_GetMechanismList(slot, mechanismList)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

        m = []
        for x in range(len(mechanismList)):
            mechanism = mechanismList[x]
            if mechanism >= CKM_VENDOR_DEFINED:
                k = 'CKR_VENDOR_DEFINED_%X' % (mechanism - CKM_VENDOR_DEFINED)
                CKM[k] = mechanism
                CKM[mechanism] = k
            m.append(CKM[mechanism])
        return m

    def getMechanismInfo(self, slot, type):
        """
        C_GetMechanismInfo

        :param slot: slot number returned by :func:`getSlotList`
        :type slot: integer
        :param type: a `CKM_*` type
        :type type: integer
        :return: information about a mechanism
        :rtype: a :class:`CK_MECHANISM_INFO` object
        """
        info = PyKCS11.LowLevel.CK_MECHANISM_INFO()
        rv = self.lib.C_GetMechanismInfo(slot, CKM[type], info)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

        i = CK_MECHANISM_INFO()
        i.ulMinKeySize = info.ulMinKeySize
        i.ulMaxKeySize = info.ulMaxKeySize
        i.flags = info.flags

        return i

    def waitForSlotEvent(self, flags=0):
        """
        C_WaitForSlotEvent

        :param flags: 0 (default) or `CKF_DONT_BLOCK`
        :type flags: integer
        :return: slot
        :rtype: integer
        """
        tmp = 0
        (rv, slot) = self.lib.C_WaitForSlotEvent(flags, tmp)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

        return slot

class Mechanism(object):
    """Wraps CK_MECHANISM"""

    def __init__(self, mechanism, param=None):
        """
        :param mechanism: the mechanism to be used
        :type mechanism: integer, any `CKM_*` value
        :param param: data to be used as crypto operation parameter
          (i.e. the IV for some algorithms)
        :type param: string or list/tuple of bytes

        :see: :func:`Session.decrypt`, :func:`Session.sign`
        """
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
        self._mech.mechanism = mechanism
        self._param = None
        if param:
            self._param = ckbytelist(param)
            self._mech.pParameter = self._param
            self._mech.ulParameterLen = len(param)

    def to_native(self):
        return self._mech

MechanismSHA1 = Mechanism(CKM_SHA_1, None)
MechanismRSAPKCS1 = Mechanism(CKM_RSA_PKCS, None)
MechanismRSAGENERATEKEYPAIR = Mechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, None)
MechanismECGENERATEKEYPAIR = Mechanism(CKM_EC_KEY_PAIR_GEN, None)
MechanismAESGENERATEKEY = Mechanism(CKM_AES_KEY_GEN, None)

class RSAOAEPMechanism(object):
    """RSA OAEP Wrapping mechanism"""

    def __init__(self, hashAlg, mgf, label=None):
        """
        :param hashAlg: the hash algorithm to use (like `CKM_SHA256`)
        :param mgf: the mask generation function to use (like
          `CKG_MGF1_SHA256`)
        :param label: the (optional) label to use
        """
        self._param = PyKCS11.LowLevel.CK_RSA_PKCS_OAEP_PARAMS()
        self._param.hashAlg = hashAlg
        self._param.mgf = mgf
        self._source = None
        self._param.src = CKZ_DATA_SPECIFIED
        if label:
            self._source = ckbytelist(label)
            self._param.ulSourceDataLen = len(self._source)
        else:
            self._param.ulSourceDataLen = 0
        self._param.pSourceData = self._source
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
        self._mech.mechanism = CKM_RSA_PKCS_OAEP
        self._mech.pParameter = self._param
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_RSA_PKCS_OAEP_PARAMS_LENGTH

    def to_native(self):
        return self._mech

class RSA_PSS_Mechanism(object):
    """RSA PSS Wrapping mechanism"""

    def __init__(self, mecha, hashAlg, mgf, sLen):
        """
        :param mecha: the mechanism to use (like
          `CKM_SHA384_RSA_PKCS_PSS`)
        :param hashAlg: the hash algorithm to use (like `CKM_SHA384`)
        :param mgf: the mask generation function to use (like
          `CKG_MGF1_SHA384`)
        :param sLen: length, in bytes, of the salt value used in the PSS
          encoding (like 0 or the message length)
        """
        self._param = PyKCS11.LowLevel.CK_RSA_PKCS_PSS_PARAMS()
        self._param.hashAlg = hashAlg
        self._param.mgf = mgf
        self._param.sLen = sLen
        self._mech = PyKCS11.LowLevel.CK_MECHANISM()
        self._mech.mechanism = mecha
        self._mech.pParameter = self._param
        self._mech.ulParameterLen = PyKCS11.LowLevel.CK_RSA_PKCS_PSS_PARAMS_LENGTH

    def to_native(self):
        return self._mech

class DigestSession(object):
    def __init__(self, lib, session, mecha):
        self._lib = lib
        self._session = session
        self._mechanism = mecha.to_native()
        rv = self._lib.C_DigestInit(self._session, self._mechanism)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

    def update(self, data):
        """
        C_DigestUpdate

        :param data: data to add to the digest
        :type data: bytes or string
        """
        data1 = ckbytelist(data)
        rv = self._lib.C_DigestUpdate(self._session, data1)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return self

    def digestKey(self, handle):
        """
        C_DigestKey

        :param handle: key handle
        :type handle: CK_OBJECT_HANDLE
        """
        rv = self._lib.C_DigestKey(self._session, handle)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return self

    def final(self):
        """
        C_DigestFinal

        :return: the digest
        :rtype: ckbytelist
        """
        digest = ckbytelist()
        # Get the size of the digest
        rv = self._lib.C_DigestFinal(self._session, digest)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        # Get the actual digest
        rv = self._lib.C_DigestFinal(self._session, digest)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return digest

class Session(object):
    """ Manage :func:`PyKCS11Lib.openSession` objects """

    def __init__(self, pykcs11, session):
        """
        :param pykcs11: PyKCS11 library object
        :type pykcs11: PyKCS11Lib
        :param session: session handle
        :type session: instance of :class:`CK_SESSION_HANDLE`
        """
        if not isinstance(pykcs11, PyKCS11Lib):
            raise TypeError("pykcs11 must be a PyKCS11Lib")
        if not isinstance(session, LowLevel.CK_SESSION_HANDLE):
            raise TypeError("session must be a CK_SESSION_HANDLE")

        # hold the PyKCS11Lib reference, so that it's not Garbage Collection'd
        self.pykcs11 = pykcs11
        self.session = session

    @property
    def lib(self):
        """
        Get the low level lib of the owning PyKCS11Lib
        """
        return self.pykcs11.lib

    def closeSession(self):
        """
        C_CloseSession
        """
        rv = self.lib.C_CloseSession(self.session)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

    def getSessionInfo(self):
        """
        C_GetSessionInfo

        :return: a :class:`CK_SESSION_INFO` object
        """
        sessioninfo = PyKCS11.LowLevel.CK_SESSION_INFO()
        rv = self.lib.C_GetSessionInfo(self.session, sessioninfo)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

        s = CK_SESSION_INFO()
        s.slotID = sessioninfo.slotID
        s.state = sessioninfo.state
        s.flags = sessioninfo.flags
        s.ulDeviceError = sessioninfo.ulDeviceError
        return s

    def login(self, pin, user_type=CKU_USER):
        """
        C_Login

        :param pin: the user's PIN or None for CKF_PROTECTED_AUTHENTICATION_PATH
        :type pin: string
        :param user_type: the user type. The default value is
          CKU_USER. You may also use CKU_SO
        :type user_type: integer
        """
        pin1 = ckbytelist(pin)
        rv = self.lib.C_Login(self.session, user_type, pin1)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

    def logout(self):
        """
        C_Logout
        """
        rv = self.lib.C_Logout(self.session)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

        del self

    def initPin(self, pin):
        """
        C_InitPIN

        :param pin: new PIN
        """
        new_pin1 = ckbytelist(pin)
        rv = self.lib.C_InitPIN(self.session, new_pin1)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

    def setPin(self, old_pin, new_pin):
        """
        C_SetPIN

        :param old_pin: old PIN
        :param new_pin: new PIN
        """
        old_pin1 = ckbytelist(old_pin)
        new_pin1 = ckbytelist(new_pin)
        rv = self.lib.C_SetPIN(self.session, old_pin1, new_pin1)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

    def createObject(self, template):
        """
        C_CreateObject

        :param template: object template
        """
        attrs = self._template2ckattrlist(template)
        handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
        rv = self.lib.C_CreateObject(self.session, attrs, handle)
        if rv != PyKCS11.CKR_OK:
            raise PyKCS11.PyKCS11Error(rv)
        return handle

    def destroyObject(self, obj):
        """
        C_DestroyObject

        :param obj: object ID
        """
        rv = self.lib.C_DestroyObject(self.session, obj)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

    def digestSession(self, mecha=MechanismSHA1):
        """
        C_DigestInit/C_DigestUpdate/C_DigestKey/C_DigestFinal

        :param mecha: the digesting mechanism to be used
          (use `MechanismSHA1` for `CKM_SHA_1`)
        :type mecha: :class:`Mechanism`
        :return: A :class:`DigestSession` object
        :rtype: DigestSession
        """
        return DigestSession(self.lib, self.session, mecha)

    def digest(self, data, mecha=MechanismSHA1):
        """
        C_DigestInit/C_Digest

        :param data: the data to be digested
        :type data:  (binary) sring or list/tuple of bytes
        :param mecha: the digesting mechanism to be used
          (use `MechanismSHA1` for `CKM_SHA_1`)
        :type mecha: :class:`Mechanism`
        :return: the computed digest
        :rtype: list of bytes

        :note: the returned value is an istance of :class:`ckbytelist`.
          You can easly convert it to a binary string with:
          ``bytes(ckbytelistDigest)``
          or, for Python 2:
          ``''.join(chr(i) for i in ckbytelistDigest)``

        """
        digest = ckbytelist()
        m = mecha.to_native()
        data1 = ckbytelist(data)
        rv = self.lib.C_DigestInit(self.session, m)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        # first call get digest size
        rv = self.lib.C_Digest(self.session, data1, digest)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        # second call get actual digest data
        rv = self.lib.C_Digest(self.session, data1, digest)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return digest

    def sign(self, key, data, mecha=MechanismRSAPKCS1):
        """
        C_SignInit/C_Sign

        :param key: a key handle, obtained calling :func:`findObjects`.
        :type key: integer
        :param data: the data to be signed
        :type data:  (binary) string or list/tuple of bytes
        :param mecha: the signing mechanism to be used
          (use `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
        :type mecha: :class:`Mechanism`
        :return: the computed signature
        :rtype: list of bytes

        :note: the returned value is an instance of :class:`ckbytelist`.
          You can easly convert it to a binary string with:
          ``bytes(ckbytelistSignature)``
          or, for Python 2:
          ``''.join(chr(i) for i in ckbytelistSignature)``

        """
        m = mecha.to_native()
        signature = ckbytelist()
        data1 = ckbytelist(data)
        rv = self.lib.C_SignInit(self.session, m, key)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        # first call get signature size
        rv = self.lib.C_Sign(self.session, data1, signature)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        # second call get actual signature data
        rv = self.lib.C_Sign(self.session, data1, signature)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return signature

    def verify(self, key, data, signature, mecha=MechanismRSAPKCS1):
        """
        C_VerifyInit/C_Verify

        :param key: a key handle, obtained calling :func:`findObjects`.
        :type key: integer
        :param data: the data that was signed
        :type data:  (binary) string or list/tuple of bytes
        :param signature: the signature to be verified
        :type signature:  (binary) string or list/tuple of bytes
        :param mecha: the signing mechanism to be used
          (use `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
        :type mecha: :class:`Mechanism`
        :return: True if signature is valid, False otherwise
        :rtype: bool

        """
        m = mecha.to_native()
        data1 = ckbytelist(data)
        rv = self.lib.C_VerifyInit(self.session, m, key)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        rv = self.lib.C_Verify(self.session, data1, signature)
        if rv == CKR_OK:
            return True
        elif rv == CKR_SIGNATURE_INVALID:
            return False
        else:
            raise PyKCS11Error(rv)

    def encrypt(self, key, data, mecha=MechanismRSAPKCS1):
        """
        C_EncryptInit/C_Encrypt

        :param key: a key handle, obtained calling :func:`findObjects`.
        :type key: integer
        :param data: the data to be encrypted
        :type data:  (binary) string or list/tuple of bytes
        :param mecha: the encryption mechanism to be used
          (use `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
        :type mecha: :class:`Mechanism`
        :return: the encrypted data
        :rtype: list of bytes

        :note: the returned value is an instance of :class:`ckbytelist`.
          You can easly convert it to a binary string with:
          ``bytes(ckbytelistEncrypted)``
          or, for Python 2:
          ``''.join(chr(i) for i in ckbytelistEncrypted)``

        """
        encrypted = ckbytelist()
        m = mecha.to_native()
        data1 = ckbytelist(data)
        rv = self.lib.C_EncryptInit(self.session, m, key)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        # first call get encrypted size
        rv = self.lib.C_Encrypt(self.session, data1, encrypted)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        # second call get actual encrypted data
        rv = self.lib.C_Encrypt(self.session, data1, encrypted)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return encrypted

    def decrypt(self, key, data, mecha=MechanismRSAPKCS1):
        """
        C_DecryptInit/C_Decrypt

        :param key: a key handle, obtained calling :func:`findObjects`.
        :type key: integer
        :param data: the data to be decrypted
        :type data:  (binary) string or list/tuple of bytes
        :param mecha: the decrypt mechanism to be used
        :type mecha: :class:`Mechanism` instance or :class:`MechanismRSAPKCS1`
          for CKM_RSA_PKCS
        :return: the decrypted data
        :rtype: list of bytes

        :note: the returned value is an instance of :class:`ckbytelist`.
          You can easly convert it to a binary string with:
          ``bytes(ckbytelistData)``
          or, for Python 2:
          ``''.join(chr(i) for i in ckbytelistData)``

        """
        m = mecha.to_native()
        decrypted = ckbytelist()
        data1 = ckbytelist(data)
        rv = self.lib.C_DecryptInit(self.session, m, key)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        # first call get decrypted size
        rv = self.lib.C_Decrypt(self.session, data1, decrypted)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        # second call get actual decrypted data
        rv = self.lib.C_Decrypt(self.session, data1, decrypted)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return decrypted

    def wrapKey(self, wrappingKey, key, mecha=MechanismRSAPKCS1):
        """
        C_WrapKey

        :param wrappingKey: a wrapping key handle
        :type wrappingKey: integer
        :param key: a handle of the key to be wrapped
        :type key: integer
        :param mecha: the encrypt mechanism to be used
          (use `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
        :type mecha: :class:`Mechanism`
        :return: the wrapped key bytes
        :rtype: list of bytes

        :note: the returned value is an instance of :class:`ckbytelist`.
          You can easily convert it to a binary string with:
          ``bytes(ckbytelistData)``
          or, for Python 2:
          ``''.join(chr(i) for i in ckbytelistData)``

        """
        wrapped = ckbytelist()
        native = mecha.to_native()
        # first call get wrapped size
        rv = self.lib.C_WrapKey(self.session, native, wrappingKey, key,
                                wrapped)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        # second call get actual wrapped key data
        rv = self.lib.C_WrapKey(self.session, native, wrappingKey, key,
                                wrapped)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return wrapped

    def unwrapKey(self, unwrappingKey, wrappedKey, template,
                  mecha=MechanismRSAPKCS1):
        """
        C_UnwrapKey

        :param unwrappingKey: the unwrapping key handle
        :type unwrappingKey: integer
        :param wrappedKey: the bytes of the wrapped key
        :type wrappedKey:  (binary) string or list/tuple of bytes
        :param template: template for the unwrapped key
        :param mecha: the decrypt mechanism to be used (use
          `MechanismRSAPKCS1` for `CKM_RSA_PKCS`)
        :type mecha: :class:`Mechanism`
        :return: the unwrapped key object
        :rtype: integer

        """
        m = mecha.to_native()
        data1 = ckbytelist(wrappedKey)
        handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
        attrs = self._template2ckattrlist(template)
        rv = self.lib.C_UnwrapKey(self.session, m, unwrappingKey,
                                  data1, attrs, handle)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return handle

    def isNum(self, type):
        """
        is the type a numerical value?

        :param type: PKCS#11 type like `CKA_CERTIFICATE_TYPE`
        :rtype: bool
        """
        if type in (CKA_CERTIFICATE_TYPE,
                    CKA_CLASS,
                    CKA_KEY_GEN_MECHANISM,
                    CKA_KEY_TYPE,
                    CKA_MODULUS_BITS,
                    CKA_VALUE_BITS,
                    CKA_VALUE_LEN):
            return True
        return False

    def isString(self, type):
        """
        is the type a string value?

        :param type: PKCS#11 type like `CKA_LABEL`
        :rtype: bool
        """
        if type in (CKA_LABEL,
                    CKA_APPLICATION):
            return True
        return False

    def isBool(self, type):
        """
        is the type a boolean value?

        :param type: PKCS#11 type like `CKA_ALWAYS_SENSITIVE`
        :rtype: bool
        """
        if type in (CKA_ALWAYS_SENSITIVE,
                    CKA_DECRYPT,
                    CKA_DERIVE,
                    CKA_ENCRYPT,
                    CKA_EXTRACTABLE,
                    CKA_HAS_RESET,
                    CKA_LOCAL,
                    CKA_MODIFIABLE,
                    CKA_NEVER_EXTRACTABLE,
                    CKA_PRIVATE,
                    CKA_RESET_ON_INIT,
                    CKA_SECONDARY_AUTH,
                    CKA_SENSITIVE,
                    CKA_SIGN,
                    CKA_SIGN_RECOVER,
                    CKA_TOKEN,
                    CKA_TRUSTED,
                    CKA_UNWRAP,
                    CKA_VERIFY,
                    CKA_VERIFY_RECOVER,
                    CKA_WRAP,
                    CKA_WRAP_WITH_TRUSTED):
            return True
        return False

    def isBin(self, type):
        """
        is the type a byte array value?

        :param type: PKCS#11 type like `CKA_MODULUS`
        :rtype: bool
        """
        return (not self.isBool(type)) \
            and (not self.isString(type)) \
            and (not self.isNum(type))

    def isAttributeList(self, type):
        """
        is the type a attribute list value?

        :param type: PKCS#11 type like `CKA_WRAP_TEMPLATE`
        :rtype: bool
        """
        if type in (CKA_WRAP_TEMPLATE,
                    CKA_UNWRAP_TEMPLATE):
            return True
        return False

    def _template2ckattrlist(self, template):
        t = PyKCS11.LowLevel.ckattrlist(len(template))
        for x in range(len(template)):
            attr = template[x]
            if self.isNum(attr[0]):
                t[x].SetNum(attr[0], int(attr[1]))
            elif self.isString(attr[0]):
                t[x].SetString(attr[0], str(attr[1]))
            elif self.isBool(attr[0]):
                t[x].SetBool(attr[0], attr[1] == CK_TRUE)
            elif self.isAttributeList(attr[0]):
                t[x].SetList(attr[0],
                        self._template2ckattrlist(attr[1]))
            elif self.isBin(attr[0]):
                attrBin = attr[1]
                attrStr = attr[1]
                if isinstance(attr[1], int):
                    attrStr = str(attr[1])
                if isinstance(attr[1], bytes):
                    attrBin = ckbytelist(attrStr)
                t[x].SetBin(attr[0], attrBin)
            else:
                raise PyKCS11Error(-2)
        return t

    def generateKey(self, template, mecha=MechanismAESGENERATEKEY):
        """
        generate a secret key

        :param template: template for the secret key
        :param mecha: mechanism to use
        :return: handle of the generated key
        :rtype: PyKCS11.LowLevel.CK_OBJECT_HANDLE
        """
        t = self._template2ckattrlist(template)
        ck_handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
        m = mecha.to_native()
        rv = self.lib.C_GenerateKey(self.session, m, t, ck_handle)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return ck_handle

    def generateKeyPair(self, templatePub, templatePriv,
                        mecha=MechanismRSAGENERATEKEYPAIR):
        """
        generate a key pair

        :param templatePub: template for the public key
        :param templatePriv:  template for the private key
        :param mecha: mechanism to use
        :return: a tuple of handles (pub, priv)
        :rtype: tuple
        """
        tPub = self._template2ckattrlist(templatePub)
        tPriv = self._template2ckattrlist(templatePriv)
        ck_pub_handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
        ck_prv_handle = PyKCS11.LowLevel.CK_OBJECT_HANDLE()
        m = mecha.to_native()
        rv = self.lib.C_GenerateKeyPair(self.session, m, tPub, tPriv,
                                        ck_pub_handle, ck_prv_handle)

        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return ck_pub_handle, ck_prv_handle

    def findObjects(self, template=()):
        """
        find the objects matching the template pattern

        :param template: list of attributes tuples (attribute,value).
          The default value is () and all the objects are returned
        :type template: list
        :return: a list of object ids
        :rtype: list
        """
        t = self._template2ckattrlist(template)

        # we search for 10 objects by default. speed/memory tradeoff
        result = PyKCS11.LowLevel.ckobjlist(10)

        rv = self.lib.C_FindObjectsInit(self.session, t)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

        res = []
        while True:
            rv = self.lib.C_FindObjects(self.session, result)
            if rv != CKR_OK:
                raise PyKCS11Error(rv)
            for x in result:
                # make a copy of the handle: the original value get
                # corrupted (!!)
                a = CK_OBJECT_HANDLE(self)
                a.assign(x.value())
                res.append(a)
            if len(result) == 0:
                break

        rv = self.lib.C_FindObjectsFinal(self.session)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return res

    def getAttributeValue(self, obj_id, attr, allAsBinary=False):
        """
        C_GetAttributeValue

        :param obj_id: object ID returned by :func:`findObjects`
        :type obj_id: integer
        :param attr: list of attributes
        :type attr: list
        :param allAsBinary: return all values as binary data; default is False.
        :type allAsBinary: Boolean
        :return: a list of values corresponding to the list of attributes
        :rtype: list

        :see: :func:`getAttributeValue_fragmented`

        :note: if allAsBinary is True the function do not convert results to
          Python types (i.e.: CKA_TOKEN to Bool, CKA_CLASS to int, ...).

          Binary data is returned as :class:`ckbytelist` type, usable
          as a list containing only bytes.
          You can easly convert it to a binary string with:
          ``bytes(ckbytelistVariable)``
          or, for Python 2:
          ``''.join(chr(i) for i in ckbytelistVariable)``

        """
        valTemplate = PyKCS11.LowLevel.ckattrlist(len(attr))
        for x in range(len(attr)):
            valTemplate[x].SetType(attr[x])
        # first call to get the attribute size and reserve the memory
        rv = self.lib.C_GetAttributeValue(self.session, obj_id, valTemplate)
        if rv in (CKR_ATTRIBUTE_TYPE_INVALID, CKR_ATTRIBUTE_SENSITIVE,
                  CKR_ARGUMENTS_BAD):
            return self.getAttributeValue_fragmented(obj_id, attr, allAsBinary)

        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        # second call to get the attribute value
        rv = self.lib.C_GetAttributeValue(self.session, obj_id, valTemplate)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

        res = []
        for x in range(len(attr)):
            if allAsBinary:
                res.append(valTemplate[x].GetBin())
            elif valTemplate[x].IsNum():
                res.append(valTemplate[x].GetNum())
            elif valTemplate[x].IsBool():
                res.append(valTemplate[x].GetBool())
            elif valTemplate[x].IsString():
                res.append(valTemplate[x].GetString())
            elif valTemplate[x].IsBin():
                res.append(valTemplate[x].GetBin())
            else:
                raise PyKCS11Error(-2)

        return res

    def getAttributeValue_fragmented(self, obj_id, attr, allAsBinary=False):
        """
        Same as :func:`getAttributeValue` except that when some attribute
        is sensitive or unknown an empty value (None) is returned.

        Note: this is achived by getting attributes one by one.

        :see: :func:`getAttributeValue`
        """
        # some attributes does not exists or is sensitive
        # but we don't know which ones. So try one by one
        valTemplate = PyKCS11.LowLevel.ckattrlist(1)
        res = []
        for x in range(len(attr)):
            valTemplate[0].Reset()
            valTemplate[0].SetType(attr[x])
            # first call to get the attribute size and reserve the memory
            rv = self.lib.C_GetAttributeValue(self.session, obj_id,
                                              valTemplate)
            if rv in (CKR_ATTRIBUTE_TYPE_INVALID,
                      CKR_ATTRIBUTE_SENSITIVE, CKR_ARGUMENTS_BAD):
                # append an empty value
                res.append(None)
                continue

            if rv != CKR_OK:
                raise PyKCS11Error(rv)
            # second call to get the attribute value
            rv = self.lib.C_GetAttributeValue(self.session, obj_id,
                                              valTemplate)
            if rv != CKR_OK:
                raise PyKCS11Error(rv)

            if allAsBinary:
                res.append(valTemplate[0].GetBin())
            elif valTemplate[0].IsNum():
                res.append(valTemplate[0].GetNum())
            elif valTemplate[0].IsBool():
                res.append(valTemplate[0].GetBool())
            elif valTemplate[0].IsString():
                res.append(valTemplate[0].GetString())
            elif valTemplate[0].IsBin():
                res.append(valTemplate[0].GetBin())
            elif valTemplate[0].IsAttributeList():
                res.append(valTemplate[0].GetBin())
            else:
                raise PyKCS11Error(-2)

        return res

    def seedRandom(self, seed):
        """
        C_SeedRandom

        :param seed: seed material
        :type seed: iterable
        """
        low_seed = ckbytelist(seed)
        rv = self.lib.C_SeedRandom(self.session, low_seed)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)

    def generateRandom(self, size=16):
        """
        C_GenerateRandom

        :param size: number of random bytes to get
        :type size: integer

        :note: the returned value is an instance of :class:`ckbytelist`.
          You can easly convert it to a binary string with:
          ``bytes(random)``
          or, for Python 2:
          ``''.join(chr(i) for i in random)``
        """
        low_rand = ckbytelist([0] * size)
        rv = self.lib.C_GenerateRandom(self.session, low_rand)
        if rv != CKR_OK:
            raise PyKCS11Error(rv)
        return low_rand

if __name__ == "__main__":
    # sample test/debug code
    p = PyKCS11Lib()
    p.load()

    print("getInfo")
    print(p.getInfo())

    print()
    print("getSlotList")
    s = p.getSlotList()
    print("slots:", s)
    slot = s[0]
    print("using slot:", slot)

    print()
    print("getSlotInfo")
    print(p.getSlotInfo(slot))

    print()
    print("getTokenInfo")
    print(p.getTokenInfo(slot))

    print()
    print("openSession")
    se = p.openSession(slot)

    print()
    print("sessionInfo")
    print(se.getSessionInfo())

    print()
    print("seedRandom")
    try:
        se.seedRandom([1, 2, 3, 4])
    except PyKCS11Error as e:
        print(e)
    print("generateRandom")
    print(se.generateRandom())

    print()
    print("login")
    se.login(pin="0000")

    print()
    print("sessionInfo")
    print(se.getSessionInfo())

    print()
    print("findObjects")
    objs = se.findObjects([(CKA_CLASS, CKO_CERTIFICATE)])
    print("Nb objetcs:", len(objs))
    print(objs)

    print()
    print("getAttributeValue")
    for o in objs:
        attr = se.getAttributeValue(o, [CKA_LABEL, CKA_CLASS])
        print(attr)

    print()
    print("logout")
    se.logout()

    print()
    print("closeSession")
    se.closeSession()


"""

 Copyright (C) 2016 Marco Agner

 This file is part of Flask-QRcode.

 Flask-QRcode is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 Flask-QRcode is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with Flask-QRcode.  If not, see <http://www.gnu.org/licenses/>.

"""
# coding=utf-8
import os
import base64
from io import BytesIO

try:
    from urllib import urlopen  # py2
except:
    from urllib.request import urlopen  # py3

from PIL import Image
import qrcode as qrc
from flask import Blueprint

class QRcode(object):
    """QR Code image class"""

    color = [
        "red",
        "maroon",
        "olive",
        "yellow",
        "lime",
        "green",
        "aqua",
        "teal",
        "blue",
        "navy",
        "fuchsia",
        "purple",
        "white",
        "silver",
        "gray",
        "black",
    ]

    correction_levels = {
        "L": qrc.constants.ERROR_CORRECT_L,
        "M": qrc.constants.ERROR_CORRECT_M,
        "Q": qrc.constants.ERROR_CORRECT_Q,
        "H": qrc.constants.ERROR_CORRECT_H,
    }

    def __init__(self, app=None, config_jinja=True, **kwargs):
        self.app = app
        self._config_jinja = config_jinja

        if app:
            self.init_app(app)

    def __call__(self, *args, **kwargs):
        if self.app:
            return self.qrcode(static_dir=self.app.static_folder, *args, **kwargs)
        return self.qrcode(*args, **kwargs)

    def init_app(self, app):
        self.app = app
        self.register_blueprint(app)

        if not hasattr(app, "extensions"):
            app.extensions = {}
        app.extensions["qrcode"] = self

        if self._config_jinja:
            self._qrcode = self.__call__
            app.add_template_filter(self._qrcode, "qrcode")
            app.add_template_global(self._qrcode, "qrcode")

    def register_blueprint(self, app):
        module = Blueprint("qrcode", __name__, template_folder="templates")
        app.register_blueprint(module)
        return module

    @classmethod
    def qrcode(
        cls,
        data,
        mode="base64",
        version=None,
        error_correction="L",
        box_size=10,
        border=0,
        fit=True,
        fill_color="black",
        back_color="white",
        **kwargs
    ):
        """Makes qr image using qrcode as qrc. See documentation
        for qrcode (https://pypi.python.org/pypi/qrcode) package for more info.

        :param data: String data.
        :param mode: Output mode, [base64|raw].
        :param version: The size of the QR Code (1-40).
        :param error_correction: The error correction used for the QR Code.
        :param box_size: The number of pixels for each "box" of the QR code.
        :param border: The number of box for border.
        :param fit: If `True`, find the best fit for the data.
        :param fill_color: Frontend color.
        :param back_color: Background color.

        :param icon_img: Small icon image name or url.
        :param factor: Resize for icon image (default: 4, one-fourth of QRCode)
        :param icon_box: Icon image position [left, top] (default: image center)
        """
        qr = qrc.QRCode(
            version=version,
            error_correction=cls.correction_levels[error_correction],
            box_size=box_size,
            border=border,
        )
        qr.add_data(data)
        qr.make(fit=fit)

        fcolor = (
            fill_color
            if fill_color.lower() in cls.color or fill_color.startswith("#")
            else "#" + fill_color
        )
        bcolor = (
            back_color
            if back_color.lower() in cls.color or back_color.startswith("#")
            else "#" + back_color
        )

        # creates qrcode base64
        out = BytesIO()
        qr_img = qr.make_image(back_color=bcolor, fill_color=fcolor)
        qr_img = qr_img.convert("RGBA")
        qr_img = cls._insert_img(qr_img, **kwargs)
        qr_img.save(out, "PNG")
        out.seek(0)

        if mode == "base64":
            return u"data:image/png;base64," + base64.b64encode(out.getvalue()).decode(
                "ascii"
            )
        elif mode == "raw":
            return out

    @staticmethod
    def _insert_img(qr_img, icon_img=None, factor=4, icon_box=None, static_dir=None):
        """Inserts a small icon to QR Code image"""
        img_w, img_h = qr_img.size
        size_w = int(img_w) / int(factor)
        size_h = int(img_h) / int(factor)

        try:
            # load icon from current dir
            icon_fp = os.path.join(icon_img)
            if static_dir:
                # load icon from app's static dir
                icon_fp = os.path.join(static_dir, icon_img)
            if icon_img.split("://")[0] in ["http", "https", "ftp"]:
                icon_fp = BytesIO(urlopen(icon_img).read())  # download icon
            icon = Image.open(icon_fp)
        except:
            return qr_img

        icon_w, icon_h = icon.size
        icon_w = size_w if icon_w > size_w else icon_w
        icon_h = size_h if icon_h > size_h else icon_h
        icon = icon.resize((int(icon_w), int(icon_h)), Image.ANTIALIAS)
        icon = icon.convert("RGBA")

        left = int((img_w - icon_w) / 2)
        top = int((img_h - icon_h) / 2)
        icon_box = (int(icon_box[0]), int(icon_box[1])) if icon_box else (left, top)
        qr_img.paste(im=icon, box=icon_box, mask=icon)
        return qr_img


import logging

import click

from scout.commands.utils import builds_option

from scout.export.panel import (export_gene_panels, export_panels)

LOG = logging.getLogger(__name__)

@click.command('panel', short_help='Export gene panels')
@click.argument('panel',
                nargs=-1,
                metavar='<panel_name>'
)
@builds_option
@click.option('--version',
    type=float,
    help="Specify panel version, only works if one panel"
)
@click.option('--bed',
    help="Export genes in bed like format",
    is_flag=True,
)
@click.pass_context
def panel(context, panel, build, bed, version):
    """Export gene panels to .bed like format.

        Specify any number of panels on the command line
    """
    LOG.info("Running scout export panel")
    adapter = context.obj['adapter']
    # Save all chromosomes found in the collection if panels
    chromosomes_found = set()

    if not panel:
        LOG.warning("Please provide at least one gene panel")
        context.abort()

    LOG.info("Exporting panels: {}".format(', '.join(panel)))
    if bed:
        if version:
            version = [version]
        lines = export_panels(
            adapter=adapter,
            panels=panel,
            versions=version,
            build=build,
        )
    else:
        lines = export_gene_panels(
            adapter=adapter,
            panels=panel,
            version=version,
            )
    for line in lines:
        click.echo(line)