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 + -
显示快捷键?