pgpprsasc.c

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

C
2,441
字号
	if (arglen > sizeof (ctx->cmdarg))
		return PGPERR_CMD_TOOBIG;

	if (pgpFifoWrite (ctx->fifod, ctx->ann, (byte const *)&ctx->written,
			sizeof (ctx->written)) != sizeof (ctx->written))
		return PGPERR_NOMEM;

	if (pgpFifoWrite (ctx->fifod, ctx->ann, &cmd, sizeof (cmd))
		!= sizeof (cmd))
		return PGPERR_NOMEM;

	if (pgpFifoWrite (ctx->fifod, ctx->ann, (byte const *)&arglen,
			  sizeof (arglen)) != sizeof (arglen))
		return PGPERR_NOMEM;

	if (pgpFifoWrite (ctx->fifod, ctx->ann, arg, arglen) != arglen)
		return PGPERR_NOMEM;


	ctx->written = 0;
	return 0;
}

/* Buffer up a write command for a particular message */
static int
bufferWriteCommand (struct Context *ctx, struct Message *msg)
{
	byte arg[sizeof (msg)];
	memcpy (arg, (byte const *)&msg, sizeof (arg));

	return bufferCommand (ctx, CMD_WRITE, arg, sizeof (arg));
}

/* Process a buffered Write command for a particular message */
static int
parseWriteCommand (struct Context *ctx)
{
	struct Message *msg;

	pgpAssert (ctx->cmdlen == sizeof (msg));
	memcpy ((byte *)&msg, ctx->cmdarg, sizeof (msg));
	return writeMessage (ctx, msg);
}

/* Buffer up an annotation */
static int
bufferAnnotation (struct Context *ctx, struct PgpPipeline *origin, int type,
		byte const *string, size_t size)
{
	byte *arg;
	byte *argp;
	int retval;

	arg = argp = (byte *)pgpMemAlloc (sizeof (origin) + sizeof (type) +
					  size + sizeof (size));
	if (!arg)
		return PGPERR_NOMEM;

	memcpy (argp, (byte const *)&origin, sizeof (origin));
	argp += sizeof (origin);

	memcpy (argp, (byte const *)&type, sizeof (type));
	argp += sizeof (type);

	memcpy (argp, (byte const *)&size, sizeof (size));
	argp += sizeof (size);

	memcpy (argp, string, size);
	argp += size;

	retval = bufferCommand (ctx, CMD_ANNOTATE, arg, argp-arg);
	pgpMemFree (arg);
	return retval;
}

static int
parseAnnotation (struct Context *ctx)
{
	struct PgpPipeline *origin;
	int type;
	size_t size;
	byte *string, *argp = ctx->cmdarg;
	int retval;

	pgpAssert (ctx->cmdlen >= sizeof (origin) + sizeof (type) +
		sizeof (size));

	memcpy ((byte *)&origin, argp, sizeof (origin));
	argp += sizeof (origin);

	memcpy ((byte *)&type, argp, sizeof (type));
	argp += sizeof (type);

	memcpy ((byte *)&size, argp, sizeof (size));
	argp += sizeof (size);

	/* make sure we have the right number of bytes remaining */
	pgpAssert ((ctx->cmdlen - size) == (unsigned)(argp - ctx->cmdarg));

	string = (byte *)pgpAlloc(size);
	if (string == NULL)
		return PGPERR_NOMEM;

	pgpCopyMemory (argp, string, size);

	retval = sendAnnotate (ctx, origin, type, string, size);

	pgpFree(string);
	return retval;
}

/*
 * Given a buffered command, process it appropriately.  The cmd, args,
 * and arglen are currently held in the ctx.  Set arglen to 0 and
 * return 0 on success, otherwise return an error.
 */
static int
processCommand (struct Context *ctx)
{
	int error = 0;

	switch (ctx->command) {
	case CMD_WRITE:
		error = parseWriteCommand (ctx);
		break;
	case CMD_ANNOTATE:
		error = parseAnnotation (ctx);
		break;
	default:
		/* unknown command type.  This should NEVER happen */
		pgpAssert (0);
	}

	if (!error)
		ctx->cmdlen = 0;
	return error;
}

/*
 * Write a buffer of data which is a message part.  This will either
 * write or buffer depending on what the current state of the world
 * is.
 *
 * This will either perform a write() or a pgpJoinBuffer() depending
 * on what it is and what the state is.  We perform a write if this is
 * the writing message, otherwise we just buffer it.
 */
static size_t
writePartData (struct MsgPart *part, byte const *buf, size_t size, int *error)
{
	struct Message *msg = part->msg;

	/* Ignore parts that we've already finished */
	if (part->done)
		return size;

	if (part->sig)
		return part->sig->write (part->sig, buf, size, error);

	if (msg->writing)
		return part->mod->write (part->mod, buf, size, error);

	return pgpJoinBuffer (part->mod, buf, size);
}

/*
 * This will write 'extra' data out.  In general this is just
 * non-Ascii armor data.  However if we are within a "buffering
 * everything" mode, then this will get buffered into the ctx->data
 * fifo and ctx->written will be increased.
 */
static size_t
writeExtraData (struct Context *ctx, byte const *buf, size_t size, int *error)
{
	size_t written;

	if (ctx->part && ctx->part->text)
		return ctx->part->text->write (ctx->part->text, buf, size,
					error);

	pgpAssert (ctx->tail);

	if (ctx->buffering) {
		written = pgpFifoWrite (ctx->fifod, ctx->data, buf, size);
		ctx->written += written;
		return written;
	}

	return ctx->tail->write (ctx->tail, buf, size, error);
}

/*
 * This will start with the first part that we have. For each part,
 * it will see if it is done. If not, return 0. If it is done, then
 * see if there is another part sitting after it. If so, close this
 * part, return an error if one exists, or move on to the next part if
 * there was no error closing the part. If there is not another part
 * after this one, then only close this one if this is the last part
 * (msg->size && part->num == msg->size). Otherwise return 0.
 *
 * If we flushed the whole message, free it.
 */
static int
flushMessage (struct Context *ctx, struct Message *msg)
{
	struct MsgPart *temp, *part = msg->parts;
	int error = 0;

	do {
		if (!part->done)
			break;

		if (part->next) {
			error = closePart (part);
			if (error)
				break;
			temp = part->next;
			freePart (part);
			part = temp;

		} else if (part->num == msg->size) {
			pgpAssert (!part->next);

			error = closePart (part);
			if (error)
				break;
			error = ctx->tail->annotate (ctx->tail, ctx->myself,
						     PGPANN_ARMOR_END, NULL,
						     0);
			if (error)
				break;
			freeMessage (ctx, msg);
			/* freeMessage will free the last part */
			break;
			} else
			break;

	} while (part);

	return error;
}

/*
 * Set the state so that we are writing out this message. It means that
 * we are probably going to be buffering everything else until this message
 * is done. If we are already "buffering" everything, then buffer up this
 * command for later execution.
 */
static int
writeMessage (struct Context *ctx, struct Message *msg)
{
	int error;

	msg->foundpart1 = 1;
	if (ctx->buffering)
		return bufferWriteCommand (ctx, msg);

	error = ctx->tail->annotate (ctx->tail, ctx->myself,
				     PGPANN_ARMOR_BEGIN, NULL, 0);
	if (error)
		return error;

	ctx->buffering = 1;
	msg->writing = 1;

	/* Start flushing the message now */

	return flushMessage (ctx, msg);
}

/*
 * This is the main logic in flushing out the buffered data.  This
 * function will try to flush the data fifo and command fifo in order
 * to create the appropriate output stream.
 *
 * There are two streams which need to be merged.  Since the command
 * stream gives deltas into the datastream, we need to use the command
 * stream as the control.  No problem.
 *
 * First, we flush out any data in the data stream until the next
 * command, until ctx->left == 0.  Next, if ctx->cmdlen != 0 we
 * process the command in the ctx command buffer.  If that returns
 * without error we read in the next command/offset into ctx (->left
 * and ->cmdlen) and then repeat.  It at any time an error occurs,
 * stop and return the error code.
 */
static int
flushBuffers (struct Context *ctx)
{
	unsigned datalen, cmdlen, len;
	byte const *ptr;
	int error = 0;
	size_t written;

	pgpAssert (! ctx->buffering);

	datalen = pgpFifoSize (ctx->fifod, ctx->data);
	cmdlen = pgpFifoSize (ctx->fifod, ctx->ann);

	/* If nothing is buffered, just return ok */
	if (!datalen && !cmdlen)
		return 0;

	/* Now try to flush the buffers... */
	do {
		/*
		 * If buffering gets turned on processing a command,
		 * just exit this function so more data can be read in.
		 */
		if (ctx->buffering)
			return 0;

		/* First flush any data until the next command */
		while (ctx->left) {
			ptr = pgpFifoPeek (ctx->fifod, ctx->data, &len);
			pgpAssert (len);
			pgpAssert (ptr);

			if (len > ctx->left)
				len = ctx->left;
			written = writeExtraData (ctx, ptr, len, &error);
			pgpFifoSeek (ctx->fifod, ctx->data, written);
			ctx->left -= written;
			datalen -= written;
			if (error)
				return error;
		}

		/* Process the buffered command, if any */
		if (ctx->cmdlen) {
			error = processCommand (ctx);
			if (error)
				return error;
		}

		/*
		 * Now, read in the data for the next command. The
		 * syntax is: <offset><cmd><arglen><arg>
		 */
		if (cmdlen) {
			if (pgpFifoRead (ctx->fifod, ctx->ann,
					(byte *)&ctx->left,
					sizeof (ctx->left)) !=
			    sizeof (ctx->left))
				return PGPERR_FIFO_READ;
			cmdlen -= sizeof (ctx->left);

			if (pgpFifoRead (ctx->fifod, ctx->ann, &ctx->command,
					 sizeof (ctx->command)) !=
			    sizeof (ctx->command))
				return PGPERR_FIFO_READ;
			cmdlen -= sizeof (ctx->command);

			if (pgpFifoRead (ctx->fifod, ctx->ann,
					 (byte *)&ctx->cmdlen,
					 sizeof (ctx->cmdlen)) !=
			    sizeof (ctx->cmdlen))
				return PGPERR_FIFO_READ;
			cmdlen -= sizeof (ctx->cmdlen);

			if (pgpFifoRead (ctx->fifod, ctx->ann, ctx->cmdarg,
					 ctx->cmdlen) != ctx->cmdlen)
				return PGPERR_FIFO_READ;
			cmdlen -= ctx->cmdlen;
		} else {
			/*
			 * If this can happen then we need to do this
			 * and keep track of the amount of data left in
			 * the fifo.
			 */

			ctx->left = datalen;
			ctx->written = datalen;
		}

	} while (datalen || cmdlen || ctx->cmdlen);

	/* If we get here then everything has been written out. */
	ctx->written = 0;

	return 0;
}

/*
 * TBD for MIME support:
 * Add a filter to remove some MIME transforms.  To wit:
 *	First read headers, looking for blank line.  If see any header other
 *	than Content-Type: text/plain... or Content-Transfer-Encoding
 *	other than quoted-printable (or whatever the plain one is), we can't
 *	handle it, and we should just pass the whole thing through.  Otherwise
 *	we delete the header and trailing blank line.  We then run through the
 *	filter which undoes the content-transfer-encoding transformation if
 *	necessary, or else just passes data through.
 * This filter should be added just before the copymod, below.  It might
 * even replace the copymod, but I don't remember what it is for.  Something
 * to do with some error recovery state, where we needed to know we were
 * pointing at the last thing in the pipeline before context->tail?
 */

/*
 * This is a clearsigned message.  We need to setup the verification
 * pipeline to get this to work.  First we have a join module.  It is a
 * placeholder for the message.  The clearsigned text goes into the
 * second input of the join module.  The output of the join module goes
 * to a split.  The first output of the split goes to the annotation
 * reader.  The second output of the split goes into a textfilt module
 * to strip the spaces and then into a signature verification module.
 */
static int
createClearsig (struct Context *ctx, struct Message *msg, struct MsgPart *part)
{
	struct PgpPipeline *join = NULL, *split = NULL, *text = NULL;
	struct PgpPipeline **tail, **temp;
	struct PgpPipeline *sighead = NULL, **sigtail;

	/* First, build up a replacement "join/split" module */
	tail = pgpJoinCreate (&join, ctx->fifod);
	if (!tail)
		return PGPERR_NOMEM;

	temp = tail;
	tail = pgpSplitCreate (temp);
	if (!tail) {
		return PGPERR_NOMEM;
	}
	split = *temp;

	/* Splice in the join/split in place of the old join/parser */
	*(msg->tail) = NULL;
	part->mod->teardown (part->mod);

	part->mod = join;
	msg->tail = tail;

#if MIMEPARSE
	if (ctx->mime_signed) {
		/* Clean up MIME body part, remove quoted-printable etc. */
		msg->tail = pgpDeMimeModCreate (msg->tail);
	}
#endif

	/*
	 * Binary parser may add modules after itself, and in some
	 * error recovery situations they are still there when we try
	 * to close things. Create a copy module to act as a stable
	 * predecessor to context->tail.
	 */
	msg->tail = pgpCopyModCreate (msg->tail);

	/* And connect the tail */
	if (msg->tail)
		*(msg->tail) = ctx->tail;

	/* Create the text input */
	text = pgpJoinAppend (join);
	if (! text) {
		return PGPERR_NOMEM;
	}

	/* add a textfilt module */
	tail = pgpSplitAdd (split);
	if (!tail) {
		text->teardown (text);
		return PGPERR_NOMEM;
	}

#if MIMEPARSE
	tail = pgpTextFiltCreate (tail, charMapIdentity, 1,
			(ctx->mime_signed ? PGP_TEXTFILT_CRLF : PGP_TEXTFILT_NONE));
#else
	tail = pgpTextFiltCreate (tail, charMapIdentity, 1, PGP_TEXTFILT_NONE);
#endif
	if (!tail) {
		text->teardown (text);
		return PGPERR_NOMEM;
	}

	/* Create the signature parser... */
	sigtail = pgpParseBinCreate (&sighead, ctx->env);
	if (!sigtail) {
		text->teardown (text);
		return PGPERR_NOMEM;
	}

	/* ...and the signature verifier */
	if (!pgpVerifyReaderCreate (tail, sigtail, ctx->env, ctx->fifod,
				    (ctx->hashlen ? ctx->hashlist : NULL),
				    ctx->hashlen, (ctx->hashlen ? 1 : 0),
				    ctx->ui, ctx->ui_arg)) {

⌨️ 快捷键说明

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