/*
 * dvb_ca.c: generic DVB functions for EN50221 CAM interfaces
 *
 * Copyright (C) 2004 Andrew de Quincey
 *
 * Parts of this file were based on sources as follows:
 *
 * Copyright (C) 2003 Ralph Metzler <rjkm@metzlerbros.de>
 *
 * based on code:
 *
 * Copyright (C) 1999-2002 Ralph  Metzler
 *                       & Marcus Metzler for convergence integrated media GmbH
 *
 * This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
 */

#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/kthread.h>

#include "dvb_ca_en50221.h"
#include "dvb_ringbuffer.h"

static int dvb_ca_en50221_debug;

module_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644);
MODULE_PARM_DESC(cam_debug, "enable verbose debug messages");

#define dprintk if (dvb_ca_en50221_debug) printk

#define INIT_TIMEOUT_SECS 10

#define HOST_LINK_BUF_SIZE 0x200

#define RX_BUFFER_SIZE 65535

#define MAX_RX_PACKETS_PER_ITERATION 10

#define CTRLIF_DATA      0
#define CTRLIF_COMMAND   1
#define CTRLIF_STATUS    1
#define CTRLIF_SIZE_LOW  2
#define CTRLIF_SIZE_HIGH 3

#define CMDREG_HC        1	/* Host control */
#define CMDREG_SW        2	/* Size write */
#define CMDREG_SR        4	/* Size read */
#define CMDREG_RS        8	/* Reset interface */
#define CMDREG_FRIE   0x40	/* Enable FR interrupt */
#define CMDREG_DAIE   0x80	/* Enable DA interrupt */
#define IRQEN (CMDREG_DAIE)

#define STATUSREG_RE     1	/* read error */
#define STATUSREG_WE     2	/* write error */
#define STATUSREG_FR  0x40	/* module free */
#define STATUSREG_DA  0x80	/* data available */
#define STATUSREG_TXERR (STATUSREG_RE|STATUSREG_WE)	/* general transfer error */

#define DVB_CA_SLOTSTATE_NONE           0
#define DVB_CA_SLOTSTATE_UNINITIALISED  1
#define DVB_CA_SLOTSTATE_RUNNING        2
#define DVB_CA_SLOTSTATE_INVALID        3
#define DVB_CA_SLOTSTATE_WAITREADY      4
#define DVB_CA_SLOTSTATE_VALIDATE       5
#define DVB_CA_SLOTSTATE_WAITFR         6
#define DVB_CA_SLOTSTATE_LINKINIT       7

/* Information on a CA slot */
struct dvb_ca_slot {

	/* current state of the CAM */
	int slot_state;

	/* mutex used for serializing access to one CI slot */
	struct mutex slot_lock;

	/* Number of CAMCHANGES that have occurred since last processing */
	atomic_t camchange_count;

	/* Type of last CAMCHANGE */
	int camchange_type;

	/* base address of CAM config */
	u32 config_base;

	/* value to write into Config Control register */
	u8 config_option;

	/* if 1, the CAM supports DA IRQs */
	u8 da_irq_supported:1;

	/* size of the buffer to use when talking to the CAM */
	int link_buf_size;

	/* buffer for incoming packets */
	struct dvb_ringbuffer rx_buffer;

	/* timer used during various states of the slot */
	unsigned long timeout;
};

/* Private CA-interface information */
struct dvb_ca_private {

	/* pointer back to the public data structure */
	struct dvb_ca_en50221 *pub;

	/* the DVB device */
	struct dvb_device *dvbdev;

	/* Flags describing the interface (DVB_CA_FLAG_*) */
	u32 flags;

	/* number of slots supported by this CA interface */
	unsigned int slot_count;

	/* information on each slot */
	struct dvb_ca_slot *slot_info;

	/* wait queues for read() and write() operations */
	wait_queue_head_t wait_queue;

	/* PID of the monitoring thread */
	struct task_struct *thread;

	/* Flag indicating if the CA device is open */
	unsigned int open:1;

	/* Flag indicating the thread should wake up now */
	unsigned int wakeup:1;

	/* Delay the main thread should use */
	unsigned long delay;

	/* Slot to start looking for data to read from in the next user-space read operation */
	int next_read_slot;

	/* mutex serializing ioctls */
	struct mutex ioctl_mutex;
};

static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);
static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount);

/**
 * Safely find needle in haystack.
 *
 * @haystack: Buffer to look in.
 * @hlen: Number of bytes in haystack.
 * @needle: Buffer to find.
 * @nlen: Number of bytes in needle.
 * @return Pointer into haystack needle was found at, or NULL if not found.
 */
static char *findstr(char * haystack, int hlen, char * needle, int nlen)
{
	int i;

	if (hlen < nlen)
		return NULL;

	for (i = 0; i <= hlen - nlen; i++) {
		if (!strncmp(haystack + i, needle, nlen))
			return haystack + i;
	}

	return NULL;
}

/* ******************************************************************************** */
/* EN50221 physical interface functions */

/**
 * dvb_ca_en50221_check_camstatus - Check CAM status.
 */
static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private *ca, int slot)
{
	int slot_status;
	int cam_present_now;
	int cam_changed;

	/* IRQ mode */
	if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) {
		return (atomic_read(&ca->slot_info[slot].camchange_count) != 0);
	}

	/* poll mode */
	slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open);

	cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1 : 0;
	cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1 : 0;
	if (!cam_changed) {
		int cam_present_old = (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE);
		cam_changed = (cam_present_now != cam_present_old);
	}

	if (cam_changed) {
		if (!cam_present_now) {
			ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
		} else {
			ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED;
		}
		atomic_set(&ca->slot_info[slot].camchange_count, 1);
	} else {
		if ((ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) &&
		    (slot_status & DVB_CA_EN50221_POLL_CAM_READY)) {
			// move to validate state if reset is completed
			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
		}
	}

	return cam_changed;
}

/**
 * dvb_ca_en50221_wait_if_status - Wait for flags to become set on the STATUS
 *	 register on a CAM interface, checking for errors and timeout.
 *
 * @ca: CA instance.
 * @slot: Slot on interface.
 * @waitfor: Flags to wait for.
 * @timeout_ms: Timeout in milliseconds.
 *
 * @return 0 on success, nonzero on error.
 */
static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private *ca, int slot,
					 u8 waitfor, int timeout_hz)
{
	unsigned long timeout;
	unsigned long start;

	dprintk("%s\n", __func__);

	/* loop until timeout elapsed */
	start = jiffies;
	timeout = jiffies + timeout_hz;
	while (1) {
		/* read the status and check for error */
		int res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
		if (res < 0)
			return -EIO;

		/* if we got the flags, it was successful! */
		if (res & waitfor) {
			dprintk("%s succeeded timeout:%lu\n", __func__, jiffies - start);
			return 0;
		}

		/* check for timeout */
		if (time_after(jiffies, timeout)) {
			break;
		}

		/* wait for a bit */
		msleep(1);
	}

	dprintk("%s failed timeout:%lu\n", __func__, jiffies - start);

	/* if we get here, we've timed out */
	return -ETIMEDOUT;
}

/**
 * dvb_ca_en50221_link_init - Initialise the link layer connection to a CAM.
 *
 * @ca: CA instance.
 * @slot: Slot id.
 *
 * @return 0 on success, nonzero on failure.
 */
static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot)
{
	int ret;
	int buf_size;
	u8 buf[2];

	dprintk("%s\n", __func__);

	/* we'll be determining these during this function */
	ca->slot_info[slot].da_irq_supported = 0;

	/* set the host link buffer size temporarily. it will be overwritten with the
	 * real negotiated size later. */
	ca->slot_info[slot].link_buf_size = 2;

	/* read the buffer size from the CAM */
	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0)
		return ret;
	if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ / 10)) != 0)
		return ret;
	if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2)
		return -EIO;
	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0)
		return ret;

	/* store it, and choose the minimum of our buffer and the CAM's buffer size */
	buf_size = (buf[0] << 8) | buf[1];
	if (buf_size > HOST_LINK_BUF_SIZE)
		buf_size = HOST_LINK_BUF_SIZE;
	ca->slot_info[slot].link_buf_size = buf_size;
	buf[0] = buf_size >> 8;
	buf[1] = buf_size & 0xff;
	dprintk("Chosen link buffer size of %i\n", buf_size);

	/* write the buffer size to the CAM */
	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0)
		return ret;
	if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ / 10)) != 0)
		return ret;
	if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2)
		return -EIO;
	if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0)
		return ret;

	/* success */
	return 0;
}

/**
 * dvb_ca_en50221_read_tuple - Read a tuple from attribute memory.
 *
 * @ca: CA instance.
 * @slot: Slot id.
 * @address: Address to read from. Updated.
 * @tupleType: Tuple id byte. Updated.
 * @tupleLength: Tuple length. Updated.
 * @tuple: Dest buffer for tuple (must be 256 bytes). Updated.
 *
 * @return 0 on success, nonzero on error.
 */
static int dvb_ca_en50221_read_tuple(struct dvb_ca_private *ca, int slot,
				     int *address, int *tupleType, int *tupleLength, u8 * tuple)
{
	int i;
	int _tupleType;
	int _tupleLength;
	int _address = *address;

	/* grab the next tuple length and type */
	if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0)
		return _tupleType;
	if (_tupleType == 0xff) {
		dprintk("END OF CHAIN TUPLE type:0x%x\n", _tupleType);
		*address += 2;
		*tupleType = _tupleType;
		*tupleLength = 0;
		return 0;
	}
	if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address + 2)) < 0)
		return _tupleLength;
	_address += 4;

	dprintk("TUPLE type:0x%x length:%i\n", _tupleType, _tupleLength);

	/* read in the whole tuple */
	for (i = 0; i < _tupleLength; i++) {
		tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot, _address + (i * 2));
		dprintk("  0x%02x: 0x%02x %c\n",
			i, tuple[i] & 0xff,
			((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.');
	}
	_address += (_tupleLength * 2);

	// success
	*tupleType = _tupleType;
	*tupleLength = _tupleLength;
	*address = _address;
	return 0;
}

/**
 * dvb_ca_en50221_parse_attributes - Parse attribute memory of a CAM module,
 *	extracting Config register, and checking it is a DVB CAM module.
 *
 * @ca: CA instance.
 * @slot: Slot id.
 *
 * @return 0 on success, <0 on failure.
 */
static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot)
{
	int address = 0;
	int tupleLength;
	int tupleType;
	u8 tuple[257];
	char *dvb_str;
	int rasz;
	int status;
	int got_cftableentry = 0;
	int end_chain = 0;
	int i;
	u16 manfid = 0;
	u16 devid = 0;

	// CISTPL_DEVICE_0A
	if ((status =
	     dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
		return status;
	if (tupleType != 0x1D)
		return -EINVAL;

	// CISTPL_DEVICE_0C
	if ((status =
	     dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
		return status;
	if (tupleType != 0x1C)
		return -EINVAL;

	// CISTPL_VERS_1
	if ((status =
	     dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0)
		return status;
	if (tupleType != 0x15)
		return -EINVAL;

	// CISTPL_MANFID
	if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
						&tupleLength, tuple)) < 0)
		return status;
	if (tupleType != 0x20)
		return -EINVAL;
	if (tupleLength != 4)
		return -EINVAL;
	manfid = (tuple[1] << 8) | tuple[0];
	devid = (tuple[3] << 8) | tuple[2];

	// CISTPL_CONFIG
	if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
						&tupleLength, tuple)) < 0)
		return status;
	if (tupleType != 0x1A)
		return -EINVAL;
	if (tupleLength < 3)
		return -EINVAL;

	/* extract the configbase */
	rasz = tuple[0] & 3;
	if (tupleLength < (3 + rasz + 14))
		return -EINVAL;
	ca->slot_info[slot].config_base = 0;
	for (i = 0; i < rasz + 1; i++) {
		ca->slot_info[slot].config_base |= (tuple[2 + i] << (8 * i));
	}

	/* check it contains the correct DVB string */
	dvb_str = findstr((char *)tuple, tupleLength, "DVB_CI_V", 8);
	if (dvb_str == NULL)
		return -EINVAL;
	if (tupleLength < ((dvb_str - (char *) tuple) + 12))
		return -EINVAL;

	/* is it a version we support? */
	if (strncmp(dvb_str + 8, "1.00", 4)) {
		printk("dvb_ca adapter %d: Unsupported DVB CAM module version %c%c%c%c\n",
		       ca->dvbdev->adapter->num, dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]);
		return -EINVAL;
	}

	/* process the CFTABLE_ENTRY tuples, and any after those */
	while ((!end_chain) && (address < 0x1000)) {
		if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType,
							&tupleLength, tuple)) < 0)
			return status;
		switch (tupleType) {
		case 0x1B:	// CISTPL_CFTABLE_ENTRY
			if (tupleLength < (2 + 11 + 17))
				break;

			/* if we've already parsed one, just use it */
			if (got_cftableentry)
				break;

			/* get the config option */
			ca->slot_info[slot].config_option = tuple[0] & 0x3f;

			/* OK, check it contains the correct strings */
			if ((findstr((char *)tuple, tupleLength, "DVB_HOST", 8) == NULL) ||
			    (findstr((char *)tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL))
				break;

			got_cftableentry = 1;
			break;

		case 0x14:	// CISTPL_NO_LINK
			break;

		case 0xFF:	// CISTPL_END
			end_chain = 1;
			break;

		default:	/* Unknown tuple type - just skip this tuple and move to the next one */
			dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n", tupleType,
				tupleLength);
			break;
		}
	}

	if ((address > 0x1000) || (!got_cftableentry))
		return -EINVAL;

	dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n",
		manfid, devid, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option);

	// success!
	return 0;
}

/**
 * dvb_ca_en50221_set_configoption - Set CAM's configoption correctly.
 *
 * @ca: CA instance.
 * @slot: Slot containing the CAM.
 */
static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot)
{
	int configoption;

	dprintk("%s\n", __func__);

	/* set the config option */
	ca->pub->write_attribute_mem(ca->pub, slot,
				     ca->slot_info[slot].config_base,
				     ca->slot_info[slot].config_option);

	/* check it */
	configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base);
	dprintk("Set configoption 0x%x, read configoption 0x%x\n",
		ca->slot_info[slot].config_option, configoption & 0x3f);

	/* fine! */
	return 0;

}

/**
 * dvb_ca_en50221_read_data - This function talks to an EN50221 CAM control
 *	interface. It reads a buffer of data from the CAM. The data can either
 *	be stored in a supplied buffer, or automatically be added to the slot's
 *	rx_buffer.
 *
 * @ca: CA instance.
 * @slot: Slot to read from.
 * @ebuf: If non-NULL, the data will be written to this buffer. If NULL,
 * the data will be added into the buffering system as a normal fragment.
 * @ecount: Size of ebuf. Ignored if ebuf is NULL.
 *
 * @return Number of bytes read, or < 0 on error
 */
static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, u8 * ebuf, int ecount)
{
	int bytes_read;
	int status;
	u8 buf[HOST_LINK_BUF_SIZE];
	int i;

	dprintk("%s\n", __func__);

	/* check if we have space for a link buf in the rx_buffer */
	if (ebuf == NULL) {
		int buf_free;

		if (ca->slot_info[slot].rx_buffer.data == NULL) {
			status = -EIO;
			goto exit;
		}
		buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer);

		if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
			status = -EAGAIN;
			goto exit;
		}
	}

	/* check if there is data available */
	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
		goto exit;
	if (!(status & STATUSREG_DA)) {
		/* no data */
		status = 0;
		goto exit;
	}

	/* read the amount of data */
	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0)
		goto exit;
	bytes_read = status << 8;
	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0)
		goto exit;
	bytes_read |= status;

	/* check it will fit */
	if (ebuf == NULL) {
		if (bytes_read > ca->slot_info[slot].link_buf_size) {
			printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n",
			       ca->dvbdev->adapter->num, bytes_read, ca->slot_info[slot].link_buf_size);
			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
			status = -EIO;
			goto exit;
		}
		if (bytes_read < 2) {
			printk("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n",
			       ca->dvbdev->adapter->num);
			ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
			status = -EIO;
			goto exit;
		}
	} else {
		if (bytes_read > ecount) {
			printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n",
			       ca->dvbdev->adapter->num);
			status = -EIO;
			goto exit;
		}
	}

	/* fill the buffer */
	for (i = 0; i < bytes_read; i++) {
		/* read byte and check */
		if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0)
			goto exit;

		/* OK, store it in the buffer */
		buf[i] = status;
	}

	/* check for read error (RE should now be 0) */
	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
		goto exit;
	if (status & STATUSREG_RE) {
		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
		status = -EIO;
		goto exit;
	}

	/* OK, add it to the receive buffer, or copy into external buffer if supplied */
	if (ebuf == NULL) {
		if (ca->slot_info[slot].rx_buffer.data == NULL) {
			status = -EIO;
			goto exit;
		}
		dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read);
	} else {
		memcpy(ebuf, buf, bytes_read);
	}

	dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot,
		buf[0], (buf[1] & 0x80) == 0, bytes_read);

	/* wake up readers when a last_fragment is received */
	if ((buf[1] & 0x80) == 0x00) {
		wake_up_interruptible(&ca->wait_queue);
	}
	status = bytes_read;

exit:
	return status;
}

/**
 * dvb_ca_en50221_write_data - This function talks to an EN50221 CAM control
 *				interface. It writes a buffer of data to a CAM.
 *
 * @ca: CA instance.
 * @slot: Slot to write to.
 * @ebuf: The data in this buffer is treated as a complete link-level packet to
 * be written.
 * @count: Size of ebuf.
 *
 * @return Number of bytes written, or < 0 on error.
 */
static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * buf, int bytes_write)
{
	int status;
	int i;

	dprintk("%s\n", __func__);

	/* sanity check */
	if (bytes_write > ca->slot_info[slot].link_buf_size)
		return -EINVAL;

	/* it is possible we are dealing with a single buffer implementation,
	   thus if there is data available for read or if there is even a read
	   already in progress, we do nothing but awake the kernel thread to
	   process the data if necessary. */
	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
		goto exitnowrite;
	if (status & (STATUSREG_DA | STATUSREG_RE)) {
		if (status & STATUSREG_DA)
			dvb_ca_en50221_thread_wakeup(ca);

		status = -EAGAIN;
		goto exitnowrite;
	}

	/* OK, set HC bit */
	if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND,
						 IRQEN | CMDREG_HC)) != 0)
		goto exit;

	/* check if interface is still free */
	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
		goto exit;
	if (!(status & STATUSREG_FR)) {
		/* it wasn't free => try again later */
		status = -EAGAIN;
		goto exit;
	}

	/*
	 * It may need some time for the CAM to settle down, or there might
	 * be a race condition between the CAM, writing HC and our last
	 * check for DA. This happens, if the CAM asserts DA, just after
	 * checking DA before we are setting HC. In this case it might be
	 * a bug in the CAM to keep the FR bit, the lower layer/HW
	 * communication requires a longer timeout or the CAM needs more
	 * time internally. But this happens in reality!
	 * We need to read the status from the HW again and do the same
	 * we did for the previous check for DA
	 */
	status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
	if (status < 0)
		goto exit;

	if (status & (STATUSREG_DA | STATUSREG_RE)) {
		if (status & STATUSREG_DA)
			dvb_ca_en50221_thread_wakeup(ca);

		status = -EAGAIN;
		goto exit;
	}

	/* send the amount of data */
	if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) != 0)
		goto exit;
	if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW,
						 bytes_write & 0xff)) != 0)
		goto exit;

	/* send the buffer */
	for (i = 0; i < bytes_write; i++) {
		if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA, buf[i])) != 0)
			goto exit;
	}

	/* check for write error (WE should now be 0) */
	if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
		goto exit;
	if (status & STATUSREG_WE) {
		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
		status = -EIO;
		goto exit;
	}
	status = bytes_write;

	dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot,
		buf[0], (buf[1] & 0x80) == 0, bytes_write);

exit:
	ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);

exitnowrite:
	return status;
}
EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);

/* ******************************************************************************** */
/* EN50221 higher level functions */

/**
 * dvb_ca_en50221_camready_irq - A CAM has been removed => shut it down.
 *
 * @ca: CA instance.
 * @slot: Slot to shut down.
 */
static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot)
{
	dprintk("%s\n", __func__);

	ca->pub->slot_shutdown(ca->pub, slot);
	ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;

	/* need to wake up all processes to check if they're now
	   trying to write to a defunct CAM */
	wake_up_interruptible(&ca->wait_queue);

	dprintk("Slot %i shutdown\n", slot);

	/* success */
	return 0;
}
EXPORT_SYMBOL(dvb_ca_en50221_camready_irq);

/**
 * dvb_ca_en50221_camready_irq - A CAMCHANGE IRQ has occurred.
 *
 * @ca: CA instance.
 * @slot: Slot concerned.
 * @change_type: One of the DVB_CA_CAMCHANGE_* values.
 */
void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221 *pubca, int slot, int change_type)
{
	struct dvb_ca_private *ca = pubca->private;

	dprintk("CAMCHANGE IRQ slot:%i change_type:%i\n", slot, change_type);

	switch (change_type) {
	case DVB_CA_EN50221_CAMCHANGE_REMOVED:
	case DVB_CA_EN50221_CAMCHANGE_INSERTED:
		break;

	default:
		return;
	}

	ca->slot_info[slot].camchange_type = change_type;
	atomic_inc(&ca->slot_info[slot].camchange_count);
	dvb_ca_en50221_thread_wakeup(ca);
}
EXPORT_SYMBOL(dvb_ca_en50221_frda_irq);

/**
 * dvb_ca_en50221_camready_irq - A CAMREADY IRQ has occurred.
 *
 * @ca: CA instance.
 * @slot: Slot concerned.
 */
void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot)
{
	struct dvb_ca_private *ca = pubca->private;

	dprintk("CAMREADY IRQ slot:%i\n", slot);

	if (ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) {
		ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
		dvb_ca_en50221_thread_wakeup(ca);
	}
}

/**
 * An FR or DA IRQ has occurred.
 *
 * @ca: CA instance.
 * @slot: Slot concerned.
 */
void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221 *pubca, int slot)
{
	struct dvb_ca_private *ca = pubca->private;
	int flags;

	dprintk("FR/DA IRQ slot:%i\n", slot);

	switch (ca->slot_info[slot].slot_state) {
	case DVB_CA_SLOTSTATE_LINKINIT:
		flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
		if (flags & STATUSREG_DA) {
			dprintk("CAM supports DA IRQ\n");
			ca->slot_info[slot].da_irq_supported = 1;
		}
		break;

	case DVB_CA_SLOTSTATE_RUNNING:
		if (ca->open)
			dvb_ca_en50221_thread_wakeup(ca);
		break;
	}
}

/* ******************************************************************************** */
/* EN50221 thread functions */

/**
 * Wake up the DVB CA thread
 *
 * @ca: CA instance.
 */
static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca)
{

	dprintk("%s\n", __func__);

	ca->wakeup = 1;
	mb();
	wake_up_process(ca->thread);
}

/**
 * Update the delay used by the thread.
 *
 * @ca: CA instance.
 */
static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private *ca)
{
	int delay;
	int curdelay = 100000000;
	int slot;

	/* Beware of too high polling frequency, because one polling
	 * call might take several hundred milliseconds until timeout!
	 */
	for (slot = 0; slot < ca->slot_count; slot++) {
		switch (ca->slot_info[slot].slot_state) {
		default:
		case DVB_CA_SLOTSTATE_NONE:
			delay = HZ * 60;  /* 60s */
			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
				delay = HZ * 5;  /* 5s */
			break;
		case DVB_CA_SLOTSTATE_INVALID:
			delay = HZ * 60;  /* 60s */
			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
				delay = HZ / 10;  /* 100ms */
			break;

		case DVB_CA_SLOTSTATE_UNINITIALISED:
		case DVB_CA_SLOTSTATE_WAITREADY:
		case DVB_CA_SLOTSTATE_VALIDATE:
		case DVB_CA_SLOTSTATE_WAITFR:
		case DVB_CA_SLOTSTATE_LINKINIT:
			delay = HZ / 10;  /* 100ms */
			break;

		case DVB_CA_SLOTSTATE_RUNNING:
			delay = HZ * 60;  /* 60s */
			if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE))
				delay = HZ / 10;  /* 100ms */
			if (ca->open) {
				if ((!ca->slot_info[slot].da_irq_supported) ||
				    (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA)))
					delay = HZ / 10;  /* 100ms */
			}
			break;
		}

		if (delay < curdelay)
			curdelay = delay;
	}

	ca->delay = curdelay;
}

/**
 * Kernel thread which monitors CA slots for CAM changes, and performs data transfers.
 */
static int dvb_ca_en50221_thread(void *data)
{
	struct dvb_ca_private *ca = data;
	int slot;
	int flags;
	int status;
	int pktcount;
	void *rxbuf;

	dprintk("%s\n", __func__);

	/* choose the correct initial delay */
	dvb_ca_en50221_thread_update_delay(ca);

	/* main loop */
	while (!kthread_should_stop()) {
		/* sleep for a bit */
		if (!ca->wakeup) {
			set_current_state(TASK_INTERRUPTIBLE);
			schedule_timeout(ca->delay);
			if (kthread_should_stop())
				return 0;
		}
		ca->wakeup = 0;

		/* go through all the slots processing them */
		for (slot = 0; slot < ca->slot_count; slot++) {

			mutex_lock(&ca->slot_info[slot].slot_lock);

			// check the cam status + deal with CAMCHANGEs
			while (dvb_ca_en50221_check_camstatus(ca, slot)) {
				/* clear down an old CI slot if necessary */
				if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE)
					dvb_ca_en50221_slot_shutdown(ca, slot);

				/* if a CAM is NOW present, initialise it */
				if (ca->slot_info[slot].camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED) {
					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
				}

				/* we've handled one CAMCHANGE */
				dvb_ca_en50221_thread_update_delay(ca);
				atomic_dec(&ca->slot_info[slot].camchange_count);
			}

			// CAM state machine
			switch (ca->slot_info[slot].slot_state) {
			case DVB_CA_SLOTSTATE_NONE:
			case DVB_CA_SLOTSTATE_INVALID:
				// no action needed
				break;

			case DVB_CA_SLOTSTATE_UNINITIALISED:
				ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITREADY;
				ca->pub->slot_reset(ca->pub, slot);
				ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
				break;

			case DVB_CA_SLOTSTATE_WAITREADY:
				if (time_after(jiffies, ca->slot_info[slot].timeout)) {
					printk("dvb_ca adaptor %d: PC card did not respond :(\n",
					       ca->dvbdev->adapter->num);
					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
					dvb_ca_en50221_thread_update_delay(ca);
					break;
				}
				// no other action needed; will automatically change state when ready
				break;

			case DVB_CA_SLOTSTATE_VALIDATE:
				if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) {
					/* we need this extra check for annoying interfaces like the budget-av */
					if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) &&
					    (ca->pub->poll_slot_status)) {
						status = ca->pub->poll_slot_status(ca->pub, slot, 0);
						if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) {
							ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
							dvb_ca_en50221_thread_update_delay(ca);
							break;
						}
					}

					printk("dvb_ca adapter %d: Invalid PC card inserted :(\n",
					       ca->dvbdev->adapter->num);
					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
					dvb_ca_en50221_thread_update_delay(ca);
					break;
				}
				if (dvb_ca_en50221_set_configoption(ca, slot) != 0) {
					printk("dvb_ca adapter %d: Unable to initialise CAM :(\n",
					       ca->dvbdev->adapter->num);
					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
					dvb_ca_en50221_thread_update_delay(ca);
					break;
				}
				if (ca->pub->write_cam_control(ca->pub, slot,
							       CTRLIF_COMMAND, CMDREG_RS) != 0) {
					printk("dvb_ca adapter %d: Unable to reset CAM IF\n",
					       ca->dvbdev->adapter->num);
					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
					dvb_ca_en50221_thread_update_delay(ca);
					break;
				}
				dprintk("DVB CAM validated successfully\n");

				ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
				ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITFR;
				ca->wakeup = 1;
				break;

			case DVB_CA_SLOTSTATE_WAITFR:
				if (time_after(jiffies, ca->slot_info[slot].timeout)) {
					printk("dvb_ca adapter %d: DVB CAM did not respond :(\n",
					       ca->dvbdev->adapter->num);
					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
					dvb_ca_en50221_thread_update_delay(ca);
					break;
				}

				flags = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
				if (flags & STATUSREG_FR) {
					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
					ca->wakeup = 1;
				}
				break;

			case DVB_CA_SLOTSTATE_LINKINIT:
				if (dvb_ca_en50221_link_init(ca, slot) != 0) {
					/* we need this extra check for annoying interfaces like the budget-av */
					if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) &&
					    (ca->pub->poll_slot_status)) {
						status = ca->pub->poll_slot_status(ca->pub, slot, 0);
						if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) {
							ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
							dvb_ca_en50221_thread_update_delay(ca);
							break;
						}
					}

					printk("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n", ca->dvbdev->adapter->num);
					ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
					dvb_ca_en50221_thread_update_delay(ca);
					break;
				}

				if (ca->slot_info[slot].rx_buffer.data == NULL) {
					rxbuf = vmalloc(RX_BUFFER_SIZE);
					if (rxbuf == NULL) {
						printk("dvb_ca adapter %d: Unable to allocate CAM rx buffer :(\n", ca->dvbdev->adapter->num);
						ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
						dvb_ca_en50221_thread_update_delay(ca);
						break;
					}
					dvb_ringbuffer_init(&ca->slot_info[slot].rx_buffer, rxbuf, RX_BUFFER_SIZE);
				}

				ca->pub->slot_ts_enable(ca->pub, slot);
				ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_RUNNING;
				dvb_ca_en50221_thread_update_delay(ca);
				printk("dvb_ca adapter %d: DVB CAM detected and initialised successfully\n", ca->dvbdev->adapter->num);
				break;

			case DVB_CA_SLOTSTATE_RUNNING:
				if (!ca->open)
					break;

				// poll slots for data
				pktcount = 0;
				while ((status = dvb_ca_en50221_read_data(ca, slot, NULL, 0)) > 0) {
					if (!ca->open)
						break;

					/* if a CAMCHANGE occurred at some point, do not do any more processing of this slot */
					if (dvb_ca_en50221_check_camstatus(ca, slot)) {
						// we dont want to sleep on the next iteration so we can handle the cam change
						ca->wakeup = 1;
						break;
					}

					/* check if we've hit our limit this time */
					if (++pktcount >= MAX_RX_PACKETS_PER_ITERATION) {
						// dont sleep; there is likely to be more data to read
						ca->wakeup = 1;
						break;
					}
				}
				break;
			}

			mutex_unlock(&ca->slot_info[slot].slot_lock);
		}
	}

	return 0;
}

/* ******************************************************************************** */
/* EN50221 IO interface functions */

/**
 * Real ioctl implementation.
 * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them.
 *
 * @inode: Inode concerned.
 * @file: File concerned.
 * @cmd: IOCTL command.
 * @arg: Associated argument.
 *
 * @return 0 on success, <0 on error.
 */
static int dvb_ca_en50221_io_do_ioctl(struct file *file,
				      unsigned int cmd, void *parg)
{
	struct dvb_device *dvbdev = file->private_data;
	struct dvb_ca_private *ca = dvbdev->priv;
	int err = 0;
	int slot;

	dprintk("%s\n", __func__);

	if (mutex_lock_interruptible(&ca->ioctl_mutex))
		return -ERESTARTSYS;

	switch (cmd) {
	case CA_RESET:
		for (slot = 0; slot < ca->slot_count; slot++) {
			mutex_lock(&ca->slot_info[slot].slot_lock);
			if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) {
				dvb_ca_en50221_slot_shutdown(ca, slot);
				if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
					dvb_ca_en50221_camchange_irq(ca->pub,
								     slot,
								     DVB_CA_EN50221_CAMCHANGE_INSERTED);
			}
			mutex_unlock(&ca->slot_info[slot].slot_lock);
		}
		ca->next_read_slot = 0;
		dvb_ca_en50221_thread_wakeup(ca);
		break;

	case CA_GET_CAP: {
		struct ca_caps *caps = parg;

		caps->slot_num = ca->slot_count;
		caps->slot_type = CA_CI_LINK;
		caps->descr_num = 0;
		caps->descr_type = 0;
		break;
	}

	case CA_GET_SLOT_INFO: {
		struct ca_slot_info *info = parg;

		if ((info->num > ca->slot_count) || (info->num < 0)) {
			err = -EINVAL;
			goto out_unlock;
		}

		info->type = CA_CI_LINK;
		info->flags = 0;
		if ((ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_NONE)
			&& (ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_INVALID)) {
			info->flags = CA_CI_MODULE_PRESENT;
		}
		if (ca->slot_info[info->num].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
			info->flags |= CA_CI_MODULE_READY;
		}
		break;
	}

	default:
		err = -EINVAL;
		break;
	}

out_unlock:
	mutex_unlock(&ca->ioctl_mutex);
	return err;
}

/**
 * Wrapper for ioctl implementation.
 *
 * @inode: Inode concerned.
 * @file: File concerned.
 * @cmd: IOCTL command.
 * @arg: Associated argument.
 *
 * @return 0 on success, <0 on error.
 */
static long dvb_ca_en50221_io_ioctl(struct file *file,
				    unsigned int cmd, unsigned long arg)
{
	return dvb_usercopy(file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
}

/**
 * Implementation of write() syscall.
 *
 * @file: File structure.
 * @buf: Source buffer.
 * @count: Size of source buffer.
 * @ppos: Position in file (ignored).
 *
 * @return Number of bytes read, or <0 on error.
 */
static ssize_t dvb_ca_en50221_io_write(struct file *file,
				       const char __user * buf, size_t count, loff_t * ppos)
{
	struct dvb_device *dvbdev = file->private_data;
	struct dvb_ca_private *ca = dvbdev->priv;
	u8 slot, connection_id;
	int status;
	u8 fragbuf[HOST_LINK_BUF_SIZE];
	int fragpos = 0;
	int fraglen;
	unsigned long timeout;
	int written;

	dprintk("%s\n", __func__);

	/* Incoming packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
	if (count < 2)
		return -EINVAL;

	/* extract slot & connection id */
	if (copy_from_user(&slot, buf, 1))
		return -EFAULT;
	if (copy_from_user(&connection_id, buf + 1, 1))
		return -EFAULT;
	buf += 2;
	count -= 2;

	/* check if the slot is actually running */
	if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING)
		return -EINVAL;

	/* fragment the packets & store in the buffer */
	while (fragpos < count) {
		fraglen = ca->slot_info[slot].link_buf_size - 2;
		if (fraglen < 0)
			break;
		if (fraglen > HOST_LINK_BUF_SIZE - 2)
			fraglen = HOST_LINK_BUF_SIZE - 2;
		if ((count - fragpos) < fraglen)
			fraglen = count - fragpos;

		fragbuf[0] = connection_id;
		fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00;
		status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen);
		if (status) {
			status = -EFAULT;
			goto exit;
		}

		timeout = jiffies + HZ / 2;
		written = 0;
		while (!time_after(jiffies, timeout)) {
			/* check the CAM hasn't been removed/reset in the meantime */
			if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) {
				status = -EIO;
				goto exit;
			}

			mutex_lock(&ca->slot_info[slot].slot_lock);
			status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2);
			mutex_unlock(&ca->slot_info[slot].slot_lock);
			if (status == (fraglen + 2)) {
				written = 1;
				break;
			}
			if (status != -EAGAIN)
				goto exit;

			msleep(1);
		}
		if (!written) {
			status = -EIO;
			goto exit;
		}

		fragpos += fraglen;
	}
	status = count + 2;

exit:
	return status;
}

/**
 * Condition for waking up in dvb_ca_en50221_io_read_condition
 */
static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private *ca,
					    int *result, int *_slot)
{
	int slot;
	int slot_count = 0;
	int idx;
	size_t fraglen;
	int connection_id = -1;
	int found = 0;
	u8 hdr[2];

	slot = ca->next_read_slot;
	while ((slot_count < ca->slot_count) && (!found)) {
		if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING)
			goto nextslot;

		if (ca->slot_info[slot].rx_buffer.data == NULL) {
			return 0;
		}

		idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
		while (idx != -1) {
			dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2);
			if (connection_id == -1)
				connection_id = hdr[0];
			if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) {
				*_slot = slot;
				found = 1;
				break;
			}

			idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
		}

nextslot:
		slot = (slot + 1) % ca->slot_count;
		slot_count++;
	}

	ca->next_read_slot = slot;
	return found;
}

/**
 * Implementation of read() syscall.
 *
 * @file: File structure.
 * @buf: Destination buffer.
 * @count: Size of destination buffer.
 * @ppos: Position in file (ignored).
 *
 * @return Number of bytes read, or <0 on error.
 */
static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf,
				      size_t count, loff_t * ppos)
{
	struct dvb_device *dvbdev = file->private_data;
	struct dvb_ca_private *ca = dvbdev->priv;
	int status;
	int result = 0;
	u8 hdr[2];
	int slot;
	int connection_id = -1;
	size_t idx, idx2;
	int last_fragment = 0;
	size_t fraglen;
	int pktlen;
	int dispose = 0;

	dprintk("%s\n", __func__);

	/* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
	if (count < 2)
		return -EINVAL;

	/* wait for some data */
	if ((status = dvb_ca_en50221_io_read_condition(ca, &result, &slot)) == 0) {

		/* if we're in nonblocking mode, exit immediately */
		if (file->f_flags & O_NONBLOCK)
			return -EWOULDBLOCK;

		/* wait for some data */
		status = wait_event_interruptible(ca->wait_queue,
						  dvb_ca_en50221_io_read_condition
						  (ca, &result, &slot));
	}
	if ((status < 0) || (result < 0)) {
		if (result)
			return result;
		return status;
	}

	idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
	pktlen = 2;
	do {
		if (idx == -1) {
			printk("dvb_ca adapter %d: BUG: read packet ended before last_fragment encountered\n", ca->dvbdev->adapter->num);
			status = -EIO;
			goto exit;
		}

		dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2);
		if (connection_id == -1)
			connection_id = hdr[0];
		if (hdr[0] == connection_id) {
			if (pktlen < count) {
				if ((pktlen + fraglen - 2) > count) {
					fraglen = count - pktlen;
				} else {
					fraglen -= 2;
				}

				if ((status = dvb_ringbuffer_pkt_read_user(&ca->slot_info[slot].rx_buffer, idx, 2,
								      buf + pktlen, fraglen)) < 0) {
					goto exit;
				}
				pktlen += fraglen;
			}

			if ((hdr[1] & 0x80) == 0)
				last_fragment = 1;
			dispose = 1;
		}

		idx2 = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
		if (dispose)
			dvb_ringbuffer_pkt_dispose(&ca->slot_info[slot].rx_buffer, idx);
		idx = idx2;
		dispose = 0;
	} while (!last_fragment);

	hdr[0] = slot;
	hdr[1] = connection_id;
	status = copy_to_user(buf, hdr, 2);
	if (status) {
		status = -EFAULT;
		goto exit;
	}
	status = pktlen;

exit:
	return status;
}

/**
 * Implementation of file open syscall.
 *
 * @inode: Inode concerned.
 * @file: File concerned.
 *
 * @return 0 on success, <0 on failure.
 */
static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
{
	struct dvb_device *dvbdev = file->private_data;
	struct dvb_ca_private *ca = dvbdev->priv;
	int err;
	int i;

	dprintk("%s\n", __func__);

	if (!try_module_get(ca->pub->owner))
		return -EIO;

	err = dvb_generic_open(inode, file);
	if (err < 0) {
		module_put(ca->pub->owner);
		return err;
	}

	for (i = 0; i < ca->slot_count; i++) {

		if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
			if (ca->slot_info[i].rx_buffer.data != NULL) {
				/* it is safe to call this here without locks because
				 * ca->open == 0. Data is not read in this case */
				dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer);
			}
		}
	}

	ca->open = 1;
	dvb_ca_en50221_thread_update_delay(ca);
	dvb_ca_en50221_thread_wakeup(ca);

	return 0;
}

/**
 * Implementation of file close syscall.
 *
 * @inode: Inode concerned.
 * @file: File concerned.
 *
 * @return 0 on success, <0 on failure.
 */
static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
{
	struct dvb_device *dvbdev = file->private_data;
	struct dvb_ca_private *ca = dvbdev->priv;
	int err;

	dprintk("%s\n", __func__);

	/* mark the CA device as closed */
	ca->open = 0;
	dvb_ca_en50221_thread_update_delay(ca);

	err = dvb_generic_release(inode, file);

	module_put(ca->pub->owner);

	return err;
}

/**
 * Implementation of poll() syscall.
 *
 * @file: File concerned.
 * @wait: poll wait table.
 *
 * @return Standard poll mask.
 */
static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table * wait)
{
	struct dvb_device *dvbdev = file->private_data;
	struct dvb_ca_private *ca = dvbdev->priv;
	unsigned int mask = 0;
	int slot;
	int result = 0;

	dprintk("%s\n", __func__);

	if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
		mask |= POLLIN;
	}

	/* if there is something, return now */
	if (mask)
		return mask;

	/* wait for something to happen */
	poll_wait(file, &ca->wait_queue, wait);

	if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
		mask |= POLLIN;
	}

	return mask;
}
EXPORT_SYMBOL(dvb_ca_en50221_init);

static const struct file_operations dvb_ca_fops = {
	.owner = THIS_MODULE,
	.read = dvb_ca_en50221_io_read,
	.write = dvb_ca_en50221_io_write,
	.unlocked_ioctl = dvb_ca_en50221_io_ioctl,
	.open = dvb_ca_en50221_io_open,
	.release = dvb_ca_en50221_io_release,
	.poll = dvb_ca_en50221_io_poll,
	.llseek = noop_llseek,
};

static const struct dvb_device dvbdev_ca = {
	.priv = NULL,
	.users = 1,
	.readers = 1,
	.writers = 1,
#if defined(CONFIG_MEDIA_CONTROLLER_DVB)
	.name = "dvb-ca-en50221",
#endif
	.fops = &dvb_ca_fops,
};

/* ******************************************************************************** */
/* Initialisation/shutdown functions */

/**
 * Initialise a new DVB CA EN50221 interface device.
 *
 * @dvb_adapter: DVB adapter to attach the new CA device to.
 * @ca: The dvb_ca instance.
 * @flags: Flags describing the CA device (DVB_CA_FLAG_*).
 * @slot_count: Number of slots supported.
 *
 * @return 0 on success, nonzero on failure
 */
int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
			struct dvb_ca_en50221 *pubca, int flags, int slot_count)
{
	int ret;
	struct dvb_ca_private *ca = NULL;
	int i;

	dprintk("%s\n", __func__);

	if (slot_count < 1)
		return -EINVAL;

	/* initialise the system data */
	if ((ca = kzalloc(sizeof(struct dvb_ca_private), GFP_KERNEL)) == NULL) {
		ret = -ENOMEM;
		goto exit;
	}
	ca->pub = pubca;
	ca->flags = flags;
	ca->slot_count = slot_count;
	if ((ca->slot_info = kcalloc(slot_count, sizeof(struct dvb_ca_slot), GFP_KERNEL)) == NULL) {
		ret = -ENOMEM;
		goto free_ca;
	}
	init_waitqueue_head(&ca->wait_queue);
	ca->open = 0;
	ca->wakeup = 0;
	ca->next_read_slot = 0;
	pubca->private = ca;

	/* register the DVB device */
	ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA);
	if (ret)
		goto free_slot_info;

	/* now initialise each slot */
	for (i = 0; i < slot_count; i++) {
		memset(&ca->slot_info[i], 0, sizeof(struct dvb_ca_slot));
		ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE;
		atomic_set(&ca->slot_info[i].camchange_count, 0);
		ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
		mutex_init(&ca->slot_info[i].slot_lock);
	}

	mutex_init(&ca->ioctl_mutex);

	if (signal_pending(current)) {
		ret = -EINTR;
		goto unregister_device;
	}
	mb();

	/* create a kthread for monitoring this CA device */
	ca->thread = kthread_run(dvb_ca_en50221_thread, ca, "kdvb-ca-%i:%i",
				 ca->dvbdev->adapter->num, ca->dvbdev->id);
	if (IS_ERR(ca->thread)) {
		ret = PTR_ERR(ca->thread);
		printk("dvb_ca_init: failed to start kernel_thread (%d)\n",
			ret);
		goto unregister_device;
	}
	return 0;

unregister_device:
	dvb_unregister_device(ca->dvbdev);
free_slot_info:
	kfree(ca->slot_info);
free_ca:
	kfree(ca);
exit:
	pubca->private = NULL;
	return ret;
}
EXPORT_SYMBOL(dvb_ca_en50221_release);

/**
 * Release a DVB CA EN50221 interface device.
 *
 * @ca_dev: The dvb_device_t instance for the CA device.
 * @ca: The associated dvb_ca instance.
 */
void dvb_ca_en50221_release(struct dvb_ca_en50221 *pubca)
{
	struct dvb_ca_private *ca = pubca->private;
	int i;

	dprintk("%s\n", __func__);

	/* shutdown the thread if there was one */
	kthread_stop(ca->thread);

	for (i = 0; i < ca->slot_count; i++) {
		dvb_ca_en50221_slot_shutdown(ca, i);
		vfree(ca->slot_info[i].rx_buffer.data);
	}
	kfree(ca->slot_info);
	dvb_unregister_device(ca->dvbdev);
	kfree(ca);
	pubca->private = NULL;
}


import createShippingInfoData from './../../../helpers/createShippingInfoData';

describe('Cart createShippingInfoData', () => {
  it('returns methods data', async () => {
    const methodsData = {
      country: 'UK',
      carrier_code: 'XX',
      method_code: 'YY'
    };
    const shippingInfoData = createShippingInfoData(methodsData);
    expect(shippingInfoData).toEqual({
      billingAddress: {},
      shippingAddress: {
        countryId: 'UK'
      },
      shippingCarrierCode: 'XX',
      shippingMethodCode: 'YY'
    });
  });

  it('returns methods data with shipping address', async () => {
    const methodsData = {
      country: 'UK',
      carrier_code: 'XX',
      method_code: 'YY',
      shippingAddress: {
        city: 'London',
        firstname: 'John',
        lastname: 'Doe',
        postcode: 'EC123',
        street: ['JohnDoe street']
      }
    };
    const shippingInfoData = createShippingInfoData(methodsData);
    expect(shippingInfoData).toEqual({
      billingAddress: {},
      shippingAddress: {
        city: 'London',
        countryId: 'UK',
        firstname: 'John',
        lastname: 'Doe',
        postcode: 'EC123',
        street: ['JohnDoe street']
      },
      shippingCarrierCode: 'XX',
      shippingMethodCode: 'YY'
    });
  });

  it('returns methods data with billing address', async () => {
    const methodsData = {
      country: 'UK',
      carrier_code: 'XX',
      method_code: 'YY',
      billingAddress: {
        city: 'London',
        countryId: 'UK',
        firstname: 'John',
        lastname: 'Doe',
        postcode: 'EC123',
        street: ['JohnDoe street']
      }
    };
    const shippingInfoData = createShippingInfoData(methodsData);
    expect(shippingInfoData).toEqual({
      shippingAddress: { countryId: 'UK' },
      billingAddress: {
        city: 'London',
        countryId: 'UK',
        firstname: 'John',
        lastname: 'Doe',
        postcode: 'EC123',
        street: ['JohnDoe street']
      },
      shippingCarrierCode: 'XX',
      shippingMethodCode: 'YY'
    });
  });

  it('doesn\t add shippingCarrierCode or shippingMethodCode if missing carrier_code or method_code', async () => {
    const methodsData = {
      country: 'UK'
    };
    const shippingInfoData = createShippingInfoData(methodsData);
    expect(shippingInfoData).toEqual({
      billingAddress: {},
      shippingAddress: {
        countryId: 'UK'
      }
    });
  });
});


module Mint
  class Formatter
    def format(node : Ast::ArrayAccess) : String
      index =
        case node.index
        when Int64
          node.index
        else
          format node.index.as(Ast::Expression)
        end

      lhs =
        format node.lhs

      "#{lhs}[#{index}]"
    end
  end
end


./test/relaxng/tutor13_1_1.xml validates


# Translation of Odoo Server.
# This file contains the translation of the following modules:
# 	* phone_validation
#
# Translators:
# Zahed Alfak <tamass4116@gmail.com>, 2020
# Martin Trigaux, 2020
# Morovat Guivi <tarjomac@gmail.com>, 2020
# Hamid Darabi, 2020
# Hamed Mohammadi <hamed@dehongi.com>, 2020
# Hamid Reza Kaveh <hamidrkp@riseup.net>, 2020
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~12.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-09-13 11:29+0000\n"
"PO-Revision-Date: 2019-08-26 09:12+0000\n"
"Last-Translator: Hamid Reza Kaveh <hamidrkp@riseup.net>, 2020\n"
"Language-Team: Persian (https://www.transifex.com/odoo/teams/41243/fa/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Language: fa\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_needaction
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_needaction
msgid "Action Needed"
msgstr "اقدام مورد نیاز است"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__active
msgid "Active"
msgstr "فعال"

#. module: phone_validation
#: model_terms:ir.actions.act_window,help:phone_validation.phone_blacklist_action
msgid "Add a phone number in the blacklist"
msgstr ""

#. module: phone_validation
#: model_terms:ir.ui.view,arch_db:phone_validation.phone_blacklist_view_search
msgid "Archived"
msgstr "بایگانی شده"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_attachment_count
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_attachment_count
msgid "Attachment Count"
msgstr "تعداد پیوست"

#. module: phone_validation
#: model_terms:ir.ui.view,arch_db:phone_validation.phone_blacklist_view_tree
msgid "Blacklist Date"
msgstr ""

#. module: phone_validation
#: model_terms:ir.actions.act_window,help:phone_validation.phone_blacklist_action
msgid ""
"Blacklisted phone numbers means that the recipient won't receive SMS "
"Marketing anymore."
msgstr ""

#. module: phone_validation
#: model:ir.model,name:phone_validation.model_res_partner
msgid "Contact"
msgstr "تماس"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__create_uid
msgid "Created by"
msgstr "ایجاد شده توسط"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__create_date
msgid "Created on"
msgstr "ایجاد شده در"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__display_name
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__display_name
#: model:ir.model.fields,field_description:phone_validation.field_phone_validation_mixin__display_name
msgid "Display Name"
msgstr "نام نمایشی"

#. module: phone_validation
#: model:ir.model.fields,help:phone_validation.field_mail_thread_phone__phone_sanitized
msgid ""
"Field used to store sanitized phone number. Helps speeding up searches and "
"comparisons."
msgstr ""

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_follower_ids
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_follower_ids
msgid "Followers"
msgstr "دنبال‌کنندگان"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_channel_ids
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_channel_ids
msgid "Followers (Channels)"
msgstr "پیروان (کانال ها)"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_partner_ids
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_partner_ids
msgid "Followers (Partners)"
msgstr "پیروان (شرکاء)"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__id
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__id
#: model:ir.model.fields,field_description:phone_validation.field_phone_validation_mixin__id
msgid "ID"
msgstr "شناسه"

#. module: phone_validation
#: model:ir.model.fields,help:phone_validation.field_mail_thread_phone__message_needaction
#: model:ir.model.fields,help:phone_validation.field_mail_thread_phone__message_unread
#: model:ir.model.fields,help:phone_validation.field_phone_blacklist__message_needaction
#: model:ir.model.fields,help:phone_validation.field_phone_blacklist__message_unread
msgid "If checked, new messages require your attention."
msgstr ""
"اگر این گزینه را انتخاب کنید، پیام‌های جدید به توجه شما نیاز خواهند داشت."

#. module: phone_validation
#: model:ir.model.fields,help:phone_validation.field_mail_thread_phone__message_has_error
#: model:ir.model.fields,help:phone_validation.field_phone_blacklist__message_has_error
msgid "If checked, some messages have a delivery error."
msgstr "در صورت بررسی ، برخی پیام ها خطای تحویل دارند."

#. module: phone_validation
#: model:ir.model.fields,help:phone_validation.field_mail_thread_phone__phone_blacklisted
msgid ""
"If the email address is on the blacklist, the contact won't receive mass "
"mailing anymore, from any list"
msgstr ""

#. module: phone_validation
#: code:addons/phone_validation/tools/phone_validation.py:0
#, python-format
msgid "Impossible number %s: probably invalid number of digits"
msgstr ""

#. module: phone_validation
#: code:addons/phone_validation/models/phone_blacklist.py:0
#: code:addons/phone_validation/models/phone_blacklist.py:0
#, python-format
msgid "Invalid number %s"
msgstr ""

#. module: phone_validation
#: code:addons/phone_validation/tools/phone_validation.py:0
#, python-format
msgid "Invalid number %s: probably incorrect prefix"
msgstr ""

#. module: phone_validation
#: code:addons/phone_validation/models/mail_thread_phone.py:0
#: code:addons/phone_validation/models/mail_thread_phone.py:0
#, python-format
msgid "Invalid primary phone field on model %s"
msgstr ""

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_is_follower
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_is_follower
msgid "Is Follower"
msgstr "دنبال می کند"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone____last_update
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist____last_update
#: model:ir.model.fields,field_description:phone_validation.field_phone_validation_mixin____last_update
msgid "Last Modified on"
msgstr "آخرین تغییر در"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__write_uid
msgid "Last Updated by"
msgstr "آخرین تغییر توسط"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__write_date
msgid "Last Updated on"
msgstr "آخرین به روز رسانی در"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_main_attachment_id
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_main_attachment_id
msgid "Main Attachment"
msgstr "پیوست اصلی"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_has_error
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_has_error
msgid "Message Delivery error"
msgstr "خطای تحویل پیام"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_ids
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_ids
msgid "Messages"
msgstr "پیام‌ها"

#. module: phone_validation
#: model:ir.model.constraint,message:phone_validation.constraint_phone_blacklist_unique_number
msgid "Number already exists"
msgstr ""

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_needaction_counter
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_needaction_counter
msgid "Number of Actions"
msgstr "تعداد اقدامات"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_has_error_counter
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_has_error_counter
msgid "Number of errors"
msgstr ""

#. module: phone_validation
#: model:ir.model.fields,help:phone_validation.field_mail_thread_phone__message_needaction_counter
#: model:ir.model.fields,help:phone_validation.field_phone_blacklist__message_needaction_counter
msgid "Number of messages which requires an action"
msgstr "تعداد پیام ها که نیاز به عمل"

#. module: phone_validation
#: model:ir.model.fields,help:phone_validation.field_mail_thread_phone__message_has_error_counter
#: model:ir.model.fields,help:phone_validation.field_phone_blacklist__message_has_error_counter
msgid "Number of messages with delivery error"
msgstr "تعداد پیامهای با خطای تحویل"

#. module: phone_validation
#: model:ir.model.fields,help:phone_validation.field_mail_thread_phone__message_unread_counter
#: model:ir.model.fields,help:phone_validation.field_phone_blacklist__message_unread_counter
msgid "Number of unread messages"
msgstr "تعداد پیام‌های خوانده نشده"

#. module: phone_validation
#: model:ir.model.fields,help:phone_validation.field_phone_blacklist__number
msgid "Number should be E164 formatted"
msgstr ""

#. module: phone_validation
#: model:ir.ui.menu,name:phone_validation.phone_menu_main
msgid "Phone / SMS"
msgstr ""

#. module: phone_validation
#: model:ir.actions.act_window,name:phone_validation.phone_blacklist_action
#: model:ir.model,name:phone_validation.model_phone_blacklist
#: model:ir.ui.menu,name:phone_validation.phone_blacklist_menu
#: model_terms:ir.ui.view,arch_db:phone_validation.phone_blacklist_view_form
#: model_terms:ir.ui.view,arch_db:phone_validation.phone_blacklist_view_tree
msgid "Phone Blacklist"
msgstr ""

#. module: phone_validation
#: model:ir.model,name:phone_validation.model_mail_thread_phone
msgid "Phone Blacklist Mixin"
msgstr ""

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__phone_blacklisted
msgid "Phone Blacklisted"
msgstr ""

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__number
msgid "Phone Number"
msgstr "شماره تلفن"

#. module: phone_validation
#: model:ir.model,name:phone_validation.model_phone_validation_mixin
msgid "Phone Validation Mixin"
msgstr ""

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__phone_sanitized
msgid "Sanitized Number"
msgstr ""

#. module: phone_validation
#: code:addons/phone_validation/tools/phone_validation.py:0
#, python-format
msgid ""
"Unable to format %s:\n"
"%s"
msgstr ""

#. module: phone_validation
#: code:addons/phone_validation/tools/phone_validation.py:0
#, python-format
msgid "Unable to parse %s: %s"
msgstr ""

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_unread
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_unread
msgid "Unread Messages"
msgstr "پیام های ناخوانده"

#. module: phone_validation
#: model:ir.model.fields,field_description:phone_validation.field_mail_thread_phone__message_unread_counter
#: model:ir.model.fields,field_description:phone_validation.field_phone_blacklist__message_unread_counter
msgid "Unread Messages Counter"
msgstr "شمارنده پیام‌های خوانده‌نشده"


// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package packet

import (
	"bytes"
	"crypto"
	"crypto/dsa"
	"crypto/ecdsa"
	"encoding/binary"
	"fmt"
	"hash"
	"io"
	"strconv"
	"time"

	"github.com/keybase/go-crypto/openpgp/errors"
	"github.com/keybase/go-crypto/openpgp/s2k"
	"github.com/keybase/go-crypto/rsa"
)

const (
	// See RFC 4880, section 5.2.3.21 for details.
	KeyFlagCertify = 1 << iota
	KeyFlagSign
	KeyFlagEncryptCommunications
	KeyFlagEncryptStorage
)

// Signer can be implemented by application code to do actual signing.
type Signer interface {
	hash.Hash
	Sign(sig *Signature) error
	KeyId() uint64
	PublicKeyAlgo() PublicKeyAlgorithm
}

// RevocationKey represents designated revoker packet. See RFC 4880
// section 5.2.3.15 for details.
type RevocationKey struct {
	Class         byte
	PublicKeyAlgo PublicKeyAlgorithm
	Fingerprint   []byte
}

// KeyFlagBits holds boolean whether any usage flags were provided in
// the signature and BitField with KeyFlag* flags.
type KeyFlagBits struct {
	Valid    bool
	BitField byte
}

// Signature represents a signature. See RFC 4880, section 5.2.
type Signature struct {
	SigType    SignatureType
	PubKeyAlgo PublicKeyAlgorithm
	Hash       crypto.Hash

	// HashSuffix is extra data that is hashed in after the signed data.
	HashSuffix []byte
	// HashTag contains the first two bytes of the hash for fast rejection
	// of bad signed data.
	HashTag      [2]byte
	CreationTime time.Time

	RSASignature         parsedMPI
	DSASigR, DSASigS     parsedMPI
	ECDSASigR, ECDSASigS parsedMPI
	EdDSASigR, EdDSASigS parsedMPI

	// rawSubpackets contains the unparsed subpackets, in order.
	rawSubpackets []outputSubpacket

	// The following are optional so are nil when not included in the
	// signature.

	SigLifetimeSecs, KeyLifetimeSecs                        *uint32
	PreferredSymmetric, PreferredHash, PreferredCompression []uint8
	PreferredKeyServer                                      string
	IssuerKeyId                                             *uint64
	IsPrimaryId                                             *bool
	IssuerFingerprint                                       []byte

	// FlagsValid is set if any flags were given. See RFC 4880, section
	// 5.2.3.21 for details.
	FlagsValid                                                           bool
	FlagCertify, FlagSign, FlagEncryptCommunications, FlagEncryptStorage bool

	// RevocationReason is set if this signature has been revoked.
	// See RFC 4880, section 5.2.3.23 for details.
	RevocationReason     *uint8
	RevocationReasonText string

	// PolicyURI is optional. See RFC 4880, Section 5.2.3.20 for details
	PolicyURI string

	// Regex is a regex that can match a PGP UID. See RFC 4880, 5.2.3.14 for details
	Regex string

	// MDC is set if this signature has a feature packet that indicates
	// support for MDC subpackets.
	MDC bool

	// EmbeddedSignature, if non-nil, is a signature of the parent key, by
	// this key. This prevents an attacker from claiming another's signing
	// subkey as their own.
	EmbeddedSignature *Signature

	// StubbedOutCriticalError is not fail-stop, since it shouldn't break key parsing
	// when appearing in WoT-style cross signatures. But it should prevent a signature
	// from being applied to a primary or subkey.
	StubbedOutCriticalError error

	// DesignaterRevoker will be present if this signature certifies a
	// designated revoking key id (3rd party key that can sign
	// revocation for this key).
	DesignatedRevoker *RevocationKey

	outSubpackets []outputSubpacket
}

func (sig *Signature) parse(r io.Reader) (err error) {
	// RFC 4880, section 5.2.3
	var buf [5]byte
	_, err = readFull(r, buf[:1])
	if err != nil {
		return
	}
	if buf[0] != 4 {
		err = errors.UnsupportedError("signature packet version " + strconv.Itoa(int(buf[0])))
		return
	}

	_, err = readFull(r, buf[:5])
	if err != nil {
		return
	}
	sig.SigType = SignatureType(buf[0])
	sig.PubKeyAlgo = PublicKeyAlgorithm(buf[1])
	switch sig.PubKeyAlgo {
	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly, PubKeyAlgoDSA, PubKeyAlgoECDSA, PubKeyAlgoEdDSA:
	default:
		err = errors.UnsupportedError("public key algorithm " + strconv.Itoa(int(sig.PubKeyAlgo)))
		return
	}

	var ok bool
	sig.Hash, ok = s2k.HashIdToHash(buf[2])
	if !ok {
		return errors.UnsupportedError("hash function " + strconv.Itoa(int(buf[2])))
	}

	hashedSubpacketsLength := int(buf[3])<<8 | int(buf[4])
	l := 6 + hashedSubpacketsLength
	sig.HashSuffix = make([]byte, l+6)
	sig.HashSuffix[0] = 4
	copy(sig.HashSuffix[1:], buf[:5])
	hashedSubpackets := sig.HashSuffix[6:l]
	_, err = readFull(r, hashedSubpackets)
	if err != nil {
		return
	}
	// See RFC 4880, section 5.2.4
	trailer := sig.HashSuffix[l:]
	trailer[0] = 4
	trailer[1] = 0xff
	trailer[2] = uint8(l >> 24)
	trailer[3] = uint8(l >> 16)
	trailer[4] = uint8(l >> 8)
	trailer[5] = uint8(l)

	err = parseSignatureSubpackets(sig, hashedSubpackets, true)
	if err != nil {
		return
	}

	_, err = readFull(r, buf[:2])
	if err != nil {
		return
	}
	unhashedSubpacketsLength := int(buf[0])<<8 | int(buf[1])
	unhashedSubpackets := make([]byte, unhashedSubpacketsLength)
	_, err = readFull(r, unhashedSubpackets)
	if err != nil {
		return
	}
	err = parseSignatureSubpackets(sig, unhashedSubpackets, false)
	if err != nil {
		return
	}

	_, err = readFull(r, sig.HashTag[:2])
	if err != nil {
		return
	}

	switch sig.PubKeyAlgo {
	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
		sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r)
	case PubKeyAlgoDSA:
		sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r)
		if err == nil {
			sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r)
		}
	case PubKeyAlgoEdDSA:
		sig.EdDSASigR.bytes, sig.EdDSASigR.bitLength, err = readMPI(r)
		if err == nil {
			sig.EdDSASigS.bytes, sig.EdDSASigS.bitLength, err = readMPI(r)
		}
	case PubKeyAlgoECDSA:
		sig.ECDSASigR.bytes, sig.ECDSASigR.bitLength, err = readMPI(r)
		if err == nil {
			sig.ECDSASigS.bytes, sig.ECDSASigS.bitLength, err = readMPI(r)
		}
	default:
		panic("unreachable")
	}
	return
}

// parseSignatureSubpackets parses subpackets of the main signature packet. See
// RFC 4880, section 5.2.3.1.
func parseSignatureSubpackets(sig *Signature, subpackets []byte, isHashed bool) (err error) {
	for len(subpackets) > 0 {
		subpackets, err = parseSignatureSubpacket(sig, subpackets, isHashed)
		if err != nil {
			return
		}
	}

	if sig.CreationTime.IsZero() {
		err = errors.StructuralError("no creation time in signature")
	}

	return
}

type signatureSubpacketType uint8

const (
	creationTimeSubpacket        signatureSubpacketType = 2
	signatureExpirationSubpacket signatureSubpacketType = 3
	regularExpressionSubpacket   signatureSubpacketType = 6
	keyExpirationSubpacket       signatureSubpacketType = 9
	prefSymmetricAlgosSubpacket  signatureSubpacketType = 11
	revocationKey                signatureSubpacketType = 12
	issuerSubpacket              signatureSubpacketType = 16
	prefHashAlgosSubpacket       signatureSubpacketType = 21
	prefCompressionSubpacket     signatureSubpacketType = 22
	prefKeyServerSubpacket       signatureSubpacketType = 24
	primaryUserIdSubpacket       signatureSubpacketType = 25
	policyURISubpacket           signatureSubpacketType = 26
	keyFlagsSubpacket            signatureSubpacketType = 27
	reasonForRevocationSubpacket signatureSubpacketType = 29
	featuresSubpacket            signatureSubpacketType = 30
	embeddedSignatureSubpacket   signatureSubpacketType = 32
	issuerFingerprint            signatureSubpacketType = 33
)

// parseSignatureSubpacket parses a single subpacket. len(subpacket) is >= 1.
func parseSignatureSubpacket(sig *Signature, subpacket []byte, isHashed bool) (rest []byte, err error) {
	// RFC 4880, section 5.2.3.1
	var (
		length     uint32
		packetType signatureSubpacketType
		isCritical bool
	)
	switch {
	case subpacket[0] < 192:
		length = uint32(subpacket[0])
		subpacket = subpacket[1:]
	case subpacket[0] < 255:
		if len(subpacket) < 2 {
			goto Truncated
		}
		length = uint32(subpacket[0]-192)<<8 + uint32(subpacket[1]) + 192
		subpacket = subpacket[2:]
	default:
		if len(subpacket) < 5 {
			goto Truncated
		}
		length = uint32(subpacket[1])<<24 |
			uint32(subpacket[2])<<16 |
			uint32(subpacket[3])<<8 |
			uint32(subpacket[4])
		subpacket = subpacket[5:]
	}
	if length > uint32(len(subpacket)) {
		goto Truncated
	}
	rest = subpacket[length:]
	subpacket = subpacket[:length]
	if len(subpacket) == 0 {
		err = errors.StructuralError("zero length signature subpacket")
		return
	}
	packetType = signatureSubpacketType(subpacket[0] & 0x7f)
	isCritical = subpacket[0]&0x80 == 0x80
	subpacket = subpacket[1:]
	sig.rawSubpackets = append(sig.rawSubpackets, outputSubpacket{isHashed, packetType, isCritical, subpacket})
	switch packetType {
	case creationTimeSubpacket:
		if !isHashed {
			err = errors.StructuralError("signature creation time in non-hashed area")
			return
		}
		if len(subpacket) != 4 {
			err = errors.StructuralError("signature creation time not four bytes")
			return
		}
		t := binary.BigEndian.Uint32(subpacket)
		sig.CreationTime = time.Unix(int64(t), 0)
	case signatureExpirationSubpacket:
		// Signature expiration time, section 5.2.3.10
		if !isHashed {
			return
		}
		if len(subpacket) != 4 {
			err = errors.StructuralError("expiration subpacket with bad length")
			return
		}
		sig.SigLifetimeSecs = new(uint32)
		*sig.SigLifetimeSecs = binary.BigEndian.Uint32(subpacket)
	case keyExpirationSubpacket:
		// Key expiration time, section 5.2.3.6
		if !isHashed {
			return
		}
		if len(subpacket) != 4 {
			err = errors.StructuralError("key expiration subpacket with bad length")
			return
		}
		sig.KeyLifetimeSecs = new(uint32)
		*sig.KeyLifetimeSecs = binary.BigEndian.Uint32(subpacket)
	case prefSymmetricAlgosSubpacket:
		// Preferred symmetric algorithms, section 5.2.3.7
		if !isHashed {
			return
		}
		sig.PreferredSymmetric = make([]byte, len(subpacket))
		copy(sig.PreferredSymmetric, subpacket)
	case issuerSubpacket:
		// Issuer, section 5.2.3.5
		if len(subpacket) != 8 {
			err = errors.StructuralError("issuer subpacket with bad length")
			return
		}
		sig.IssuerKeyId = new(uint64)
		*sig.IssuerKeyId = binary.BigEndian.Uint64(subpacket)
	case prefHashAlgosSubpacket:
		// Preferred hash algorithms, section 5.2.3.8
		if !isHashed {
			return
		}
		sig.PreferredHash = make([]byte, len(subpacket))
		copy(sig.PreferredHash, subpacket)
	case prefCompressionSubpacket:
		// Preferred compression algorithms, section 5.2.3.9
		if !isHashed {
			return
		}
		sig.PreferredCompression = make([]byte, len(subpacket))
		copy(sig.PreferredCompression, subpacket)
	case primaryUserIdSubpacket:
		// Primary User ID, section 5.2.3.19
		if !isHashed {
			return
		}
		if len(subpacket) != 1 {
			err = errors.StructuralError("primary user id subpacket with bad length")
			return
		}
		sig.IsPrimaryId = new(bool)
		if subpacket[0] > 0 {
			*sig.IsPrimaryId = true
		}
	case keyFlagsSubpacket:
		// Key flags, section 5.2.3.21
		if !isHashed {
			return
		}
		if len(subpacket) == 0 {
			err = errors.StructuralError("empty key flags subpacket")
			return
		}
		if subpacket[0] != 0 {
			sig.FlagsValid = true
			if subpacket[0]&KeyFlagCertify != 0 {
				sig.FlagCertify = true
			}
			if subpacket[0]&KeyFlagSign != 0 {
				sig.FlagSign = true
			}
			if subpacket[0]&KeyFlagEncryptCommunications != 0 {
				sig.FlagEncryptCommunications = true
			}
			if subpacket[0]&KeyFlagEncryptStorage != 0 {
				sig.FlagEncryptStorage = true
			}
		}
	case reasonForRevocationSubpacket:
		// Reason For Revocation, section 5.2.3.23
		if !isHashed {
			return
		}
		if len(subpacket) == 0 {
			err = errors.StructuralError("empty revocation reason subpacket")
			return
		}
		sig.RevocationReason = new(uint8)
		*sig.RevocationReason = subpacket[0]
		sig.RevocationReasonText = string(subpacket[1:])
	case featuresSubpacket:
		// Features subpacket, section 5.2.3.24 specifies a very general
		// mechanism for OpenPGP implementations to signal support for new
		// features. In practice, the subpacket is used exclusively to
		// indicate support for MDC-protected encryption.
		sig.MDC = len(subpacket) >= 1 && subpacket[0]&1 == 1
	case embeddedSignatureSubpacket:
		// Only usage is in signatures that cross-certify
		// signing subkeys. section 5.2.3.26 describes the
		// format, with its usage described in section 11.1
		if sig.EmbeddedSignature != nil {
			err = errors.StructuralError("Cannot have multiple embedded signatures")
			return
		}
		sig.EmbeddedSignature = new(Signature)
		// Embedded signatures are required to be v4 signatures see
		// section 12.1. However, we only parse v4 signatures in this
		// file anyway.
		if err := sig.EmbeddedSignature.parse(bytes.NewBuffer(subpacket)); err != nil {
			return nil, err
		}
		if sigType := sig.EmbeddedSignature.SigType; sigType != SigTypePrimaryKeyBinding {
			return nil, errors.StructuralError("cross-signature has unexpected type " + strconv.Itoa(int(sigType)))
		}
	case policyURISubpacket:
		// See RFC 4880, Section 5.2.3.20
		sig.PolicyURI = string(subpacket[:])
	case regularExpressionSubpacket:
		sig.Regex = string(subpacket[:])
		if isCritical {
			sig.StubbedOutCriticalError = errors.UnsupportedError("regex support is stubbed out")
		}
	case prefKeyServerSubpacket:
		sig.PreferredKeyServer = string(subpacket[:])
	case issuerFingerprint:
		// The first byte is how many bytes the fingerprint is, but we'll just
		// read until the end of the subpacket, so we'll ignore it.
		sig.IssuerFingerprint = append([]byte{}, subpacket[1:]...)
	case revocationKey:
		// Authorizes the specified key to issue revocation signatures
		// for a key.

		// TODO: Class octet must have bit 0x80 set. If the bit 0x40
		// is set, then this means that the revocation information is
		// sensitive.
		sig.DesignatedRevoker = &RevocationKey{
			Class:         subpacket[0],
			PublicKeyAlgo: PublicKeyAlgorithm(subpacket[1]),
			Fingerprint:   append([]byte{}, subpacket[2:]...),
		}
	default:
		if isCritical {
			err = errors.UnsupportedError("unknown critical signature subpacket type " + strconv.Itoa(int(packetType)))
			return
		}
	}
	return

Truncated:
	err = errors.StructuralError("signature subpacket truncated")
	return
}

// subpacketLengthLength returns the length, in bytes, of an encoded length value.
func subpacketLengthLength(length int) int {
	if length < 192 {
		return 1
	}
	if length < 16320 {
		return 2
	}
	return 5
}

// serializeSubpacketLength marshals the given length into to.
func serializeSubpacketLength(to []byte, length int) int {
	// RFC 4880, Section 4.2.2.
	if length < 192 {
		to[0] = byte(length)
		return 1
	}
	if length < 16320 {
		length -= 192
		to[0] = byte((length >> 8) + 192)
		to[1] = byte(length)
		return 2
	}
	to[0] = 255
	to[1] = byte(length >> 24)
	to[2] = byte(length >> 16)
	to[3] = byte(length >> 8)
	to[4] = byte(length)
	return 5
}

// subpacketsLength returns the serialized length, in bytes, of the given
// subpackets.
func subpacketsLength(subpackets []outputSubpacket, hashed bool) (length int) {
	for _, subpacket := range subpackets {
		if subpacket.hashed == hashed {
			length += subpacketLengthLength(len(subpacket.contents) + 1)
			length += 1 // type byte
			length += len(subpacket.contents)
		}
	}
	return
}

// serializeSubpackets marshals the given subpackets into to.
func serializeSubpackets(to []byte, subpackets []outputSubpacket, hashed bool) {
	for _, subpacket := range subpackets {
		if subpacket.hashed == hashed {
			n := serializeSubpacketLength(to, len(subpacket.contents)+1)
			to[n] = byte(subpacket.subpacketType)
			to = to[1+n:]
			n = copy(to, subpacket.contents)
			to = to[n:]
		}
	}
	return
}

// KeyExpired returns whether sig is a self-signature of a key that has
// expired.
func (sig *Signature) KeyExpired(currentTime time.Time) bool {
	if sig.KeyLifetimeSecs == nil {
		return false
	}
	expiry := sig.CreationTime.Add(time.Duration(*sig.KeyLifetimeSecs) * time.Second)
	return currentTime.After(expiry)
}

// ExpiresBeforeOther checks if other signature has expiration at
// later date than sig.
func (sig *Signature) ExpiresBeforeOther(other *Signature) bool {
	if sig.KeyLifetimeSecs == nil {
		// This sig never expires, or has infinitely long expiration
		// time.
		return false
	} else if other.KeyLifetimeSecs == nil {
		// This sig expires at some non-infinite point, but the other
		// sig never expires.
		return true
	}

	getExpiryDate := func(s *Signature) time.Time {
		return s.CreationTime.Add(time.Duration(*s.KeyLifetimeSecs) * time.Second)
	}

	return getExpiryDate(other).After(getExpiryDate(sig))
}

// buildHashSuffix constructs the HashSuffix member of sig in preparation for signing.
func (sig *Signature) buildHashSuffix() (err error) {
	hashedSubpacketsLen := subpacketsLength(sig.outSubpackets, true)

	var ok bool
	l := 6 + hashedSubpacketsLen
	sig.HashSuffix = make([]byte, l+6)
	sig.HashSuffix[0] = 4
	sig.HashSuffix[1] = uint8(sig.SigType)
	sig.HashSuffix[2] = uint8(sig.PubKeyAlgo)
	sig.HashSuffix[3], ok = s2k.HashToHashId(sig.Hash)
	if !ok {
		sig.HashSuffix = nil
		return errors.InvalidArgumentError("hash cannot be represented in OpenPGP: " + strconv.Itoa(int(sig.Hash)))
	}
	sig.HashSuffix[4] = byte(hashedSubpacketsLen >> 8)
	sig.HashSuffix[5] = byte(hashedSubpacketsLen)
	serializeSubpackets(sig.HashSuffix[6:l], sig.outSubpackets, true)
	trailer := sig.HashSuffix[l:]
	trailer[0] = 4
	trailer[1] = 0xff
	trailer[2] = byte(l >> 24)
	trailer[3] = byte(l >> 16)
	trailer[4] = byte(l >> 8)
	trailer[5] = byte(l)
	return
}

func (sig *Signature) signPrepareHash(h hash.Hash) (digest []byte, err error) {
	err = sig.buildHashSuffix()
	if err != nil {
		return
	}

	h.Write(sig.HashSuffix)
	digest = h.Sum(nil)
	copy(sig.HashTag[:], digest)
	return
}

// Sign signs a message with a private key. The hash, h, must contain
// the hash of the message to be signed and will be mutated by this function.
// On success, the signature is stored in sig. Call Serialize to write it out.
// If config is nil, sensible defaults will be used.
func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err error) {
	signer, hashIsSigner := h.(Signer)

	if !hashIsSigner && (priv == nil || priv.PrivateKey == nil) {
		err = errors.InvalidArgumentError("attempting to sign with nil PrivateKey")
		return
	}

	sig.outSubpackets = sig.buildSubpackets()
	digest, err := sig.signPrepareHash(h)
	if err != nil {
		return
	}

	if hashIsSigner {
		err = signer.Sign(sig)
		return
	}

	// Parameter check, if this is wrong we will make a signature but
	// not serialize it later.
	if sig.PubKeyAlgo != priv.PubKeyAlgo {
		err = errors.InvalidArgumentError("signature pub key algo does not match priv key")
		return
	}

	switch priv.PubKeyAlgo {
	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
		sig.RSASignature.bytes, err = rsa.SignPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), sig.Hash, digest)
		sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes))
	case PubKeyAlgoDSA:
		dsaPriv := priv.PrivateKey.(*dsa.PrivateKey)

		// Need to truncate hashBytes to match FIPS 186-3 section 4.6.
		subgroupSize := (dsaPriv.Q.BitLen() + 7) / 8
		if len(digest) > subgroupSize {
			digest = digest[:subgroupSize]
		}
		r, s, err := dsa.Sign(config.Random(), dsaPriv, digest)
		if err != nil {
			return err
		}
		sig.DSASigR.bytes = r.Bytes()
		sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes))
		sig.DSASigS.bytes = s.Bytes()
		sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes))
	case PubKeyAlgoECDSA:
		r, s, err := ecdsa.Sign(config.Random(), priv.PrivateKey.(*ecdsa.PrivateKey), digest)
		if err != nil {
			return err
		}
		sig.ECDSASigR = FromBig(r)
		sig.ECDSASigS = FromBig(s)
	case PubKeyAlgoEdDSA:
		r, s, err := priv.PrivateKey.(*EdDSAPrivateKey).Sign(digest)
		if err != nil {
			return err
		}
		sig.EdDSASigR = FromBytes(r)
		sig.EdDSASigS = FromBytes(s)
	default:
		err = errors.UnsupportedError("public key algorithm for signing: " + strconv.Itoa(int(priv.PubKeyAlgo)))
	}

	return
}

// SignUserId computes a signature from priv, asserting that pub is a valid
// key for the identity id.  On success, the signature is stored in sig. Call
// Serialize to write it out.
// If config is nil, sensible defaults will be used.
func (sig *Signature) SignUserId(id string, pub *PublicKey, priv *PrivateKey, config *Config) error {
	h, err := userIdSignatureHash(id, pub, sig.Hash)
	if err != nil {
		return err
	}
	return sig.Sign(h, priv, config)
}

// SignUserIdWithSigner computes a signature from priv, asserting that pub is a
// valid key for the identity id.  On success, the signature is stored in sig.
// Call Serialize to write it out.
// If config is nil, sensible defaults will be used.
func (sig *Signature) SignUserIdWithSigner(id string, pub *PublicKey, s Signer, config *Config) error {
	updateUserIdSignatureHash(id, pub, s)

	return sig.Sign(s, nil, config)
}

// SignKey computes a signature from priv, asserting that pub is a subkey. On
// success, the signature is stored in sig. Call Serialize to write it out.
// If config is nil, sensible defaults will be used.
func (sig *Signature) SignKey(pub *PublicKey, priv *PrivateKey, config *Config) error {
	h, err := keySignatureHash(&priv.PublicKey, pub, sig.Hash)
	if err != nil {
		return err
	}
	return sig.Sign(h, priv, config)
}

// SignKeyWithSigner computes a signature using s, asserting that
// signeePubKey is a subkey. On success, the signature is stored in sig. Call
// Serialize to write it out. If config is nil, sensible defaults will be used.
func (sig *Signature) SignKeyWithSigner(signeePubKey *PublicKey, signerPubKey *PublicKey, s Signer, config *Config) error {
	updateKeySignatureHash(signerPubKey, signeePubKey, s)

	return sig.Sign(s, nil, config)
}

// CrossSignKey creates PrimaryKeyBinding signature in sig.EmbeddedSignature by
// signing `primary` key's hash using `priv` subkey private key. Primary public
// key is the `signee` here.
func (sig *Signature) CrossSignKey(primary *PublicKey, priv *PrivateKey, config *Config) error {
	if len(sig.outSubpackets) > 0 {
		return fmt.Errorf("outSubpackets already exists, looks like CrossSignKey was called after Sign")
	}

	sig.EmbeddedSignature = &Signature{
		CreationTime: sig.CreationTime,
		SigType:      SigTypePrimaryKeyBinding,
		PubKeyAlgo:   priv.PubKeyAlgo,
		Hash:         sig.Hash,
	}

	h, err := keySignatureHash(primary, &priv.PublicKey, sig.Hash)
	if err != nil {
		return err
	}
	return sig.EmbeddedSignature.Sign(h, priv, config)
}

// Serialize marshals sig to w. Sign, SignUserId or SignKey must have been
// called first.
func (sig *Signature) Serialize(w io.Writer) (err error) {
	if len(sig.outSubpackets) == 0 {
		sig.outSubpackets = sig.rawSubpackets
	}
	if sig.RSASignature.bytes == nil &&
		sig.DSASigR.bytes == nil &&
		sig.ECDSASigR.bytes == nil &&
		sig.EdDSASigR.bytes == nil {
		return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize")
	}

	sigLength := 0
	switch sig.PubKeyAlgo {
	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
		sigLength = 2 + len(sig.RSASignature.bytes)
	case PubKeyAlgoDSA:
		sigLength = 2 + len(sig.DSASigR.bytes)
		sigLength += 2 + len(sig.DSASigS.bytes)
	case PubKeyAlgoEdDSA:
		sigLength = 2 + len(sig.EdDSASigR.bytes)
		sigLength += 2 + len(sig.EdDSASigS.bytes)
	case PubKeyAlgoECDSA:
		sigLength = 2 + len(sig.ECDSASigR.bytes)
		sigLength += 2 + len(sig.ECDSASigS.bytes)
	default:
		panic("impossible")
	}

	unhashedSubpacketsLen := subpacketsLength(sig.outSubpackets, false)
	length := len(sig.HashSuffix) - 6 /* trailer not included */ +
		2 /* length of unhashed subpackets */ + unhashedSubpacketsLen +
		2 /* hash tag */ + sigLength
	err = serializeHeader(w, packetTypeSignature, length)
	if err != nil {
		return
	}

	_, err = w.Write(sig.HashSuffix[:len(sig.HashSuffix)-6])
	if err != nil {
		return
	}

	unhashedSubpackets := make([]byte, 2+unhashedSubpacketsLen)
	unhashedSubpackets[0] = byte(unhashedSubpacketsLen >> 8)
	unhashedSubpackets[1] = byte(unhashedSubpacketsLen)
	serializeSubpackets(unhashedSubpackets[2:], sig.outSubpackets, false)

	_, err = w.Write(unhashedSubpackets)
	if err != nil {
		return
	}
	_, err = w.Write(sig.HashTag[:])
	if err != nil {
		return
	}

	switch sig.PubKeyAlgo {
	case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
		err = writeMPIs(w, sig.RSASignature)
	case PubKeyAlgoDSA:
		err = writeMPIs(w, sig.DSASigR, sig.DSASigS)
	case PubKeyAlgoEdDSA:
		err = writeMPIs(w, sig.EdDSASigR, sig.EdDSASigS)
	case PubKeyAlgoECDSA:
		err = writeMPIs(w, sig.ECDSASigR, sig.ECDSASigS)
	default:
		panic("impossible")
	}
	return
}

// outputSubpacket represents a subpacket to be marshaled.
type outputSubpacket struct {
	hashed        bool // true if this subpacket is in the hashed area.
	subpacketType signatureSubpacketType
	isCritical    bool
	contents      []byte
}

func (sig *Signature) buildSubpackets() (subpackets []outputSubpacket) {
	creationTime := make([]byte, 4)
	binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix()))
	subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, false, creationTime})

	if sig.IssuerKeyId != nil {
		keyId := make([]byte, 8)
		binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId)
		subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId})
	}

	if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 {
		sigLifetime := make([]byte, 4)
		binary.BigEndian.PutUint32(sigLifetime, *sig.SigLifetimeSecs)
		subpackets = append(subpackets, outputSubpacket{true, signatureExpirationSubpacket, true, sigLifetime})
	}

	// Key flags may only appear in self-signatures or certification signatures.

	if sig.FlagsValid {
		subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, false, []byte{sig.GetKeyFlags().BitField}})
	}

	// The following subpackets may only appear in self-signatures

	if sig.KeyLifetimeSecs != nil && *sig.KeyLifetimeSecs != 0 {
		keyLifetime := make([]byte, 4)
		binary.BigEndian.PutUint32(keyLifetime, *sig.KeyLifetimeSecs)
		subpackets = append(subpackets, outputSubpacket{true, keyExpirationSubpacket, true, keyLifetime})
	}

	if sig.IsPrimaryId != nil && *sig.IsPrimaryId {
		subpackets = append(subpackets, outputSubpacket{true, primaryUserIdSubpacket, false, []byte{1}})
	}

	if len(sig.PreferredSymmetric) > 0 {
		subpackets = append(subpackets, outputSubpacket{true, prefSymmetricAlgosSubpacket, false, sig.PreferredSymmetric})
	}

	if len(sig.PreferredHash) > 0 {
		subpackets = append(subpackets, outputSubpacket{true, prefHashAlgosSubpacket, false, sig.PreferredHash})
	}

	if len(sig.PreferredCompression) > 0 {
		subpackets = append(subpackets, outputSubpacket{true, prefCompressionSubpacket, false, sig.PreferredCompression})
	}

	if sig.EmbeddedSignature != nil {
		buf := bytes.NewBuffer(nil)
		if err := sig.EmbeddedSignature.Serialize(buf); err == nil {
			byteContent := buf.Bytes()[2:] // skip 2-byte length header
			subpackets = append(subpackets, outputSubpacket{false, embeddedSignatureSubpacket, true, byteContent})
		}
	}

	return
}

func (sig *Signature) GetKeyFlags() (ret KeyFlagBits) {
	if !sig.FlagsValid {
		return ret
	}

	ret.Valid = true
	if sig.FlagCertify {
		ret.BitField |= KeyFlagCertify
	}
	if sig.FlagSign {
		ret.BitField |= KeyFlagSign
	}
	if sig.FlagEncryptCommunications {
		ret.BitField |= KeyFlagEncryptCommunications
	}
	if sig.FlagEncryptStorage {
		ret.BitField |= KeyFlagEncryptStorage
	}
	return ret
}

func (f *KeyFlagBits) HasFlagCertify() bool {
	return f.BitField&KeyFlagCertify != 0
}

func (f *KeyFlagBits) HasFlagSign() bool {
	return f.BitField&KeyFlagSign != 0
}

func (f *KeyFlagBits) HasFlagEncryptCommunications() bool {
	return f.BitField&KeyFlagEncryptCommunications != 0
}

func (f *KeyFlagBits) HasFlagEncryptStorage() bool {
	return f.BitField&KeyFlagEncryptStorage != 0
}

func (f *KeyFlagBits) Merge(other KeyFlagBits) {
	if other.Valid {
		f.Valid = true
		f.BitField |= other.BitField
	}
}


'use strict'

var tough = require('tough-cookie')

var Cookie = tough.Cookie
  , CookieJar = tough.CookieJar

exports.parse = function(str) {
  if (str && str.uri) {
    str = str.uri
  }
  if (typeof str !== 'string') {
    throw new Error('The cookie function only accepts STRING as param')
  }
  return Cookie.parse(str, {loose: true})
}

// Adapt the sometimes-Async api of tough.CookieJar to our requirements
function RequestJar(store) {
  var self = this
  self._jar = new CookieJar(store, {looseMode: true})
}
RequestJar.prototype.setCookie = function(cookieOrStr, uri, options) {
  var self = this
  return self._jar.setCookieSync(cookieOrStr, uri, options || {})
}
RequestJar.prototype.getCookieString = function(uri) {
  var self = this
  return self._jar.getCookieStringSync(uri)
}
RequestJar.prototype.getCookies = function(uri) {
  var self = this
  return self._jar.getCookiesSync(uri)
}

exports.jar = function(store) {
  return new RequestJar(store)
}


#ifndef BOOST_PP_IS_ITERATING
// Copyright David Abrahams 2002.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
# ifndef TYPE_LIST_IMPL_DWA2002913_HPP
#  define TYPE_LIST_IMPL_DWA2002913_HPP

#  include <boost/python/detail/type_list.hpp>

#  include <boost/preprocessor/enum_params.hpp>
#  include <boost/preprocessor/enum_params_with_a_default.hpp>
#  include <boost/preprocessor/repetition/enum.hpp>
#  include <boost/preprocessor/comma_if.hpp>
#  include <boost/preprocessor/arithmetic/sub.hpp>
#  include <boost/preprocessor/iterate.hpp>
#  include <boost/preprocessor/repetition/enum_trailing.hpp>

namespace boost { namespace python { namespace detail {

template <BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(BOOST_PYTHON_LIST_SIZE, class T, mpl::void_)>
struct type_list
    : BOOST_PP_CAT(mpl::vector,BOOST_PYTHON_LIST_SIZE)<BOOST_PP_ENUM_PARAMS_Z(1, BOOST_PYTHON_LIST_SIZE, T)>
{
};

#  define BOOST_PP_ITERATION_PARAMS_1                                                                   \
        (3, (0, BOOST_PP_DEC(BOOST_PYTHON_LIST_SIZE), <boost/python/detail/type_list_impl.hpp>))
#  include BOOST_PP_ITERATE()

}}} // namespace boost::python::detail

# endif // TYPE_LIST_IMPL_DWA2002913_HPP

#else // BOOST_PP_IS_ITERATING

# define N BOOST_PP_ITERATION()
# define BOOST_PYTHON_VOID_ARGS BOOST_PP_SUB_D(1,BOOST_PYTHON_LIST_SIZE,N)

template <
    BOOST_PP_ENUM_PARAMS_Z(1, N, class T)
    >
struct type_list<
    BOOST_PP_ENUM_PARAMS_Z(1, N, T)
    BOOST_PP_COMMA_IF(N)
    BOOST_PP_ENUM(
        BOOST_PYTHON_VOID_ARGS, BOOST_PYTHON_FIXED, mpl::void_)
    >
   : BOOST_PP_CAT(mpl::vector,N)<BOOST_PP_ENUM_PARAMS_Z(1, N, T)>
{
};

# undef BOOST_PYTHON_VOID_ARGS
# undef N

#endif // BOOST_PP_IS_ITERATING


Template: lcl-utils${PACKAGESUFFIX}/rename_cfg
Type: boolean
Default: true
_Description: Rename "/etc/lazarus" to "/etc/lazarus.bak"?
 The Lazarus suite now supports keeping multiple versions installed
 at the same time and using the alternatives system to set proper
 defaults. Normally, the latest version of any component is used.
 .
 To use the alternatives system on the system-wide configuration
 of the Lazarus suite, /etc/lazarus needs to be under control of the
 alternatives system. Currently there is a real directory at
 /etc/lazarus, probably from a previous installation. In order to
 start using the alternatives system on the configuration you must
 accept renaming "/etc/lazarus". If you don't, you will need to
 review the configuration on every version update of Lazarus as,
 unfortunately, the configuration files are not always
 backward-compatible. Also switching between different versions
 might need more intervention.
 .
 If you have made changes to your configuration files, you will
 probably need to review them and apply them to all versioned
 configurations, as they will not automatically propagate.


import { Module } from 'vuex'
import Vue from 'Vue'
import { State as RootState } from '../..'

import { propLens, over, set } from '../../../shims/vuex-lens'
const imagesLen = propLens('images')

export class State {
  images: any[] = []
  all = 1
  page = 1
}

const Index: Module<State, RootState> = {
  namespaced: true,
  state: new State(),
  mutations: {
    PUSH_IMAGES: (state, payload) => {
      over(imagesLen, (images: any) => R.concat(images, payload), state)
    },
    SET_IMAGES: (state, payload) => {
      set(imagesLen, payload, state)
    },
    ALL_PAGE: (state, payload) => {
      state.all = parseInt(payload)
    },
    PAGE: (state, payload) => {
      state.page = parseInt(payload)
    }
  },
  actions: {
    setImages({ commit }, images) {
      commit('SET_IMAGES', images.data)
      commit('ALL_PAGE', images.allPage)
      commit('PAGE', images.currentPage)
    },
    pushImages({ commit }, images) {
      commit('PUSH_IMAGES', images.data)
      commit('ALL_PAGE', images.allPage)
      commit('PAGE', images.currentPage)
    }
  }
}

export default Index


//==================================================================================
// Copyright (c) 2016 , Advanced Micro Devices, Inc.  All rights reserved.
//
/// \author AMD Developer Tools Team
/// \file afLineEdit.h
///
//==================================================================================

#ifndef __AFLINEEDIT_H
#define __AFLINEEDIT_H

// Qt
#include <QLineEdit>

// Local:
#include <AMDTApplicationFramework/Include/afApplicationFrameworkDLLBuild.h>

// ----------------------------------------------------------------------------------
// Class Name:          afLineEdit
// General Description: This class inherits QLineEdit. Use this class when a user edit
//                      completion is required. Make sure to store a unique object name.
//                      This object name is used to store the edit history in the framework.
// ----------------------------------------------------------------------------------
class AF_API afLineEdit : public QLineEdit
{
    Q_OBJECT

public:

    /// Constructor:
    afLineEdit(const QString& objectName, QWidget* pParent = nullptr);
    afLineEdit(const QString& objectName, const QString& contents, QWidget* pParent = nullptr);

    /// Destructor:
    ~afLineEdit();

protected slots:

    /// Is handling the editing finished signal:
    void OnEditFinished();

private:

    /// Initializes the line edit completer:
    void InitilaizeCompleter();

};

#endif //__AFLINEEDIT_H