pgparmor.c

来自「著名的加密软件的应用于电子邮件中」· C语言 代码 · 共 1,332 行 · 第 1/3 页

C
1,332
字号
 * input buffer.
 */
static size_t
armorWriteBytes (struct PgpPipeline *myself, byte const *buf, size_t size,
		 int *error)
{
	struct Context *context = (struct Context *)myself->priv;
	size_t size0 = size;
	size_t t;

	pgpAssert(!context->outlen);

	/* Repeat for each line... */
	while (size) {
		t = min ((size_t)(ARMOR_LINE_INPUT - context->inlen), size);
		memcpy (context->input + context->inlen, buf, t);
		buf += t;
		size -= t;
		context->inlen += (byte)t;

		/* If we've filled it up, then flush it out */
		if (context->inlen == ARMOR_LINE_INPUT) {
			*error = armorWriteLine (myself);
			if (*error)
				return size0-size;
		}
	}
	return size0-size;
}

/*
 * Write out as much of the input FIFO as is possible.
 *
 * If context->version > PGPVERSION_2_6 then we only start writing things
 * out if context->armorlines == 0 or when we have a full block worth of
 * data (ARMOR_LINE_BYTES * (context->armorlines - context->lineno)).
 * Otherwise, we wait until we have it all so we can set the number of
 * parts header properly.
 */
static int
armorFlushFifo (struct PgpPipeline *myself, size_t flushlen)
{
	struct Context *context = (struct Context *)myself->priv;
	byte const *ptr;
	unsigned len;
	size_t retlen;
	int error;

	error = armorFlushPending (myself);
	if (error)
		return error;

	ptr = pgpFifoPeek (context->fd, context->fifo, &len);
	if (len > flushlen)
		len = (unsigned)flushlen;
	while (len) {
		retlen = armorWriteBytes (myself, ptr, len, &error);
		pgpFifoSeek (context->fd, context->fifo, retlen);
		flushlen -= retlen;
		if (error)
			return error;

		ptr = pgpFifoPeek (context->fd, context->fifo, &len);
		if (len > flushlen)
			len = (unsigned)flushlen;
	}
	return 0;
}

/*
 * This is the function that processes a clearsigned message.  It uses
 * context->state to tell it what it is doing:
 *	0) at the beginning -- check it
 *	1) middle of line, pass it through
 *	2) end of line with \r -- check next character for \n
 */
static size_t
armorDoClearsign (struct PgpPipeline *myself, byte const *buf, size_t size,
		  int *error)
{
	struct Context *context = (struct Context *)myself->priv;
	size_t written, size0 = size;
	byte const *ptr;
	byte const from[] = "From ";
	int i, t, flag;

	while (size) {
		if (context->state == 2) {
			context->state = 0;
			if (*buf == '\n') {
				ptr = buf+1;
				goto do_write;
			}
		}

		/*
		 * Here, we check the beginning of the line for "-" and
		 * "From ".  We use the flag to denote what we've
		 * found/buffered:
		 *	0) no match
		 *	1) full match
		 *	2) partial match (and we buffered data)
		 */
		if (!context->state) {
			flag = 0;
			if (!context->linebuf) {
				/* nothing is buffered */
				if (*buf == '-')
					flag = 1;
			}
			if (!flag) {
				t = min (5, size - context->linebuf);
				for (i = 0; i < t; i++)
					if (buf[i] != from[context->linebuf+i])
					{
						pgpFifoWrite (context->fd,
							      context->header,
							      context->input,
							      context->linebuf);
						context->linebuf = 0;
						break;
					}
				if (i == t) {
					/* We matched the whole input buffer */
					if (i + context->linebuf == 5)
						flag = 1;
					else {
						memcpy (context->input +
							context->linebuf, buf,
							t);
						context->linebuf += t;
						size -= t;
						buf += t;
						flag = 2;
					}
				}
			}
			if (flag == 1) {
				pgpFifoWrite (context->fd, context->header,
					      (byte const *)"- ", 2);
				if (context->linebuf) {
					pgpFifoWrite (context->fd,
						      context->header,
						      context->input,
						      context->linebuf);
					context->linebuf = 0;
				}
			}

			if (flag != 2)
				context->state = 1;
		}

		*error = armorFlushPending (myself);
		if (*error)
			return size0 - size;

		for (ptr = buf; ptr < buf+size; ptr++) {
			if (*ptr == '\r' || *ptr == '\n') {
				context->state = (*ptr++ == '\r') ?
					2 : 0;
				break;
			}
		}

do_write:
		written = context->tail->write (context->tail, buf,
						ptr-buf, error);
		buf += written;
		size -= written;
		if (*error)
			return size0 - size;

	}

	return size0 - size;
}

/*
 * This function creates a pgp-mime multipart/signed (clearsigned) message.
 * It performs the MIME quoted printable canonicalization, except for
 * handling end of line characters (=20, etc.).
 * context->output is used to build up an output line.  MIME quoted printable
 * limits its size.
 * context->state is used to record its status:
 * 2 - just after '\r'
 * 1 - just after '\n'
 * 0 - otherwise
 */
static size_t
armorDoMimesign (struct PgpPipeline *myself, byte const *buf, size_t size,
		 int *error)
{
	struct Context *context = (struct Context *)myself->priv;
	size_t written, size0 = size;
	char const *ptr;	/* point into buf */
	char *ptr2;		/* point into context->output */
	char c;			/* character we are manipulating */
	char pushback[2];	/* pushback char array */
	unsigned pbsize = 0;	/* chars in pushback */
	int mimequote;		/* true if char needs mime quoting (=XX) */
	char const from[] = "From";

	ptr = (char *)buf;
	ptr2 = context->output + context->outlen;

	/* Loop till we have consumed all input */
	while (size  || pbsize) {
		/* Loop per line of output */
		while (ptr < (char *)buf + size  ||  pbsize) {
			if (pbsize) {
				c = pushback[--pbsize];
			} else {
				c = *ptr++;
			}
			/* state = 2 means prev line termed with cr */
			if (context->state == 2) {
				/* eat lf */
				if (c == '\n') {
					context->state = 1;
					continue;
				}
			}
			context->state = 0;
			mimequote = 0;
			if (c == '\r' || c == '\n') {
				if (ptr2 > context->output &&
				    (ptr2[-1] == ' ' || ptr2[-1] == '\t')) {
					/* Fixup space at end of line */
					--ptr2;
					pushback[pbsize++] = c;
					c = ptr2[0];
					mimequote = 1;
				} else {
					context->state = (c == '\r') ? 2 : 1;
					break;
				}
			} else if ((c!='\t' && c<32) || c == '=' || c > 126) {
				mimequote = 1;
			} else if (ptr2 - context->output >= MIMEMAX) {
				/* need soft break */
				pushback[pbsize++] = c;
				pushback[pbsize++] = ptr2[-1];
				ptr2[-1] = '=';
				context->state = 1; /* force newline */
				break;
			} else if (c == ' ' &&
				   ptr2 - context->output == sizeof(from)-1 &&
				   strncmp((char *)context->output, from,
					   sizeof(from) - 1) == 0) {
				mimequote = 1;
			} else {
				*ptr2++ = c;
			}
			if (mimequote) {
				mimequote = 0;
				if (ptr2 - context->output > MIMEMAX - 3) {
					/* Need soft break */
					pushback[pbsize++] = c;
					if (ptr2 - context->output >=
					    MIMEMAX) {
						/* Need another pushback */
						pgpAssert(pbsize <
							sizeof(pushback));
						pushback[pbsize++] = ptr2[-1];
						ptr2[-1] = '=';
					} else {
						*ptr2++ = '=';
					}
					context->state = 1; /* force newline */
					break;
				} else {
					*ptr2++ = '=';
					*ptr2++ = "0123456789ABCDEF"[c>>4];
					*ptr2++ = "0123456789ABCDEF"[c&0xf];
				}
			}
		}
		if (context->state) {
			/* Output line now */
			*error = armorFlushPending (myself);
			if (*error)
				return size0 - size;

			*ptr2++ = '\n';
			*ptr2 = '\0';
			written = 0;
			context->outptr = context->output;
			while (written < (size_t)(ptr2 - context->outptr)) {
				written += context->tail->write (context->tail,
						 (byte *)context->outptr,
						 ptr2-context->outptr, error);
				context->outptr += written;
				if (*error)
					return ptr - (char *)buf;
			}
		}
		size -= ptr - (char *)buf;
		buf = (byte *)ptr;
		ptr2 = context->output;
	}

	context->outlen = ptr2 - context->output;
	return size0 - size;
}

/*
 *
 * Do we have to buffer?  There are three cases:
 * - If maxparts is known (non-zero), we can exit immediately.
 * - Otherwise, if version > PGPVERSION_2_6, we can write as soon
 *   as we have *more* than one part's data, since we can postpone
 *   figuring out maxparts once we know it's more than the current
 *   part.
 * - Otherwise, we need to wait until maxlines is known
 */
static size_t
armorMustBuffer (struct Context *context)
{
	unsigned long needed;

	if (context->maxparts)	/* armorlines = 0 -> no buffering */
		return 0;
	pgpAssert(context->armorlines);
	if (context->version <= PGPVERSION_2_6)
		return 1;	/* Must buffer */
	needed = context->armorlines * ARMOR_LINE_INPUT;
	return pgpFifoSize (context->fd, context->fifo) > needed;
}

static size_t
Write (struct PgpPipeline *myself, byte const *buf, size_t size, int *error)
{
	struct Context *context;

	pgpAssert (myself);
	pgpAssert (myself->magic == ARMORMAGIC);
	pgpAssert (error);

	context = (struct Context *)myself->priv;
	pgpAssert (context);
	pgpAssert (context->tail);
	pgpAssert (!context->at_eof);	/* No writes after sizeAdvise(0)! */

	*error = armorFlushPending (myself);
	if (*error)
		return 0;

	/* XXX Should just change function pointers. */
	if (context->clearsign) {
		if (context->pgpmime)
			return armorDoMimesign (myself, buf, size, error);
		else
			return armorDoClearsign (myself, buf, size, error);
	}

	/* Can we write it straight through? */
	if (context->maxparts && !pgpFifoSize (context->fd, context->fifo))
		return armorWriteBytes (myself, buf, size, error);

	/* Nope, have to buffer it... */
	size = pgpFifoWrite (context->fd, context->fifo, buf, size);

	if (context->maxparts) {
		*error = armorFlushFifo (myself, (size_t)-1);
		return size;
	}

	/*
	 * Can we write out any of the buffered data?  The new format
	 * (Omitting the "/05" of "PART 01/05" on all but the last part)
	 * allows us to if we have enough to be sure that this isn't
	 * the last part.
	 */
	if (context->version > PGPVERSION_2_6) {
		unsigned long needed;

		for (;;) {
			needed = context->armorlines - context->lineno;
			if (!needed)
				needed = context->armorlines;
			needed *= ARMOR_LINE_INPUT;
			if (pgpFifoSize (context->fd, context->fifo) <= needed)
				break;
			pgpFifoSize (context->fd, context->fifo);
			*error = armorFlushFifo (myself, needed);
			if (*error)
				break;
		}
	}
	return size;
}

/*
 * We mostly ingore annotations, since they can't be passed through
 * interleaved with our own.
 */
static int
Annotate (struct PgpPipeline *myself, struct PgpPipeline *origin, int type,
	  byte const *string, size_t size)
{
	struct Context *context;
	int error = 0;

	pgpAssert (myself);
	pgpAssert (myself->magic == ARMORMAGIC);

	context = (struct Context *)myself->priv;
	pgpAssert (context);

#if 0
	/* Suppress - this could confuse our downstream neighbours */
	error = context->tail->annotate (context->tail, origin, type,
					 string, size);
	if (!error)
#else
	(void)origin;
	(void)string;
	(void)size;
#endif
		PGP_SCOPE_DEPTH_UPDATE(context->scope_depth, type);
	pgpAssert(context->scope_depth != -1);
	return error;
}

/*
 * We could in theory generate downstream SizeAdvises,
 * but it's not worth the bother until somone wants the hint.
 */
static int
SizeAdvise (struct PgpPipeline *myself, unsigned long bytes)
{
	struct Context *context;
	int error = 0;
	unsigned long total;

	pgpAssert (myself);
	pgpAssert (myself->magic == ARMORMAGIC);

	context = (struct Context *)myself->priv;
	pgpAssert (context);
	pgpAssert (context->tail);

	/* Ignore inner-scope SizeAdvise */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?