📄 pgpaddhdr.c
字号:
/*____________________________________________________________________________
Copyright (C) 2002 PGP Corporation
All rights reserved.
$Id: pgpAddHdr.c,v 1.10 2002/08/06 20:11:03 dallen Exp $
____________________________________________________________________________*/
/*
* pgpAddHdr.c -- Add a PGP Packet Header to a data stream.
*
* Written by: Derek Atkins <warlord@MIT.EDU>
*/
#include "pgpConfig.h"
#include <stdio.h>
#include <string.h>
#include "pgpDebug.h"
#include "pgpAddHdr.h"
#include "pgpPktByte.h"
#include "pgpAnnotate.h"
#include "pgpFIFO.h"
#include "pgpMem.h"
#include "pgpPipeline.h"
#include "pgpErrors.h"
#include "pgpContext.h"
#define ADDHEADERMAGIC 0xadd0b11e
/* Define BIGBLOCKS to get larger buffers */
#ifndef BIGBLOCKS
#define BLOCK_SIZE 12
#define BUFFER_SIZE (1 << BLOCK_SIZE) /* 2^10 == 4k */
#else /* BIGBLOCKS */
#define BLOCK_SIZE 18
#define BUFFER_SIZE (1 << BLOCK_SIZE) /* 2^18 == 256K */
#endif /* BIGBLOCKS */
/* This module works in two different ways. For V3 and earlier, it
* uses "old-format" (PGP 2 compatible) packet lengths, which go at
* the front of the packet. For V4 and later, it uses new-format
* packets. The V3 mode uses the fifo field, and the V4 mode uses the
* buffer, bufptr and bufwrite fields.
*
* The V3 mode uses the Write and SizeAdvise functions, and V4 uses
* BufferWrite and BufferSizeAdvise.
*
* In V3 mode, dowrite is false while we are buffering up the packet.
* It is set true once we get the final length, at which point we
* can output the header and flush out the data.
*
* In V4 mode, dowrite is false until we have filled up the buffer,
* then we set it to true and output the partial body length byte
* and the buffer full of data, then set it to false again.
*/
typedef struct AddHdrContext {
PGPPipeline pipe;
PGPFifoDesc const *fifod;
PGPFifoContext *fifo;
PGPPipeline *tail;
PGPByte header[5]; /* pktbyte + 4byte length */
PGPByte *ptr;
int hdrlen; /* actual header length */
int dowrite;
int sizeknown; /* bytes is valid */
PGPFileOffset bytes; /* sizeAdvise() promise */
PgpVersion version;
PGPByte *buffer; /* Buffer, used in V4 mode */
PGPByte *bufwrite; /* "Fill" pointer for buffer */
PGPByte *bufptr; /* "Empty" pointer for buffer */
int buflen; /* Number of bytes in buffer */
int midflush; /* V4 mode: buffer bytes to be literally sent */
#ifdef BIGBLOCKS
int lastblock; /* V4 mode: true if on last block of data */
#endif /* BIGBLOCKS */
int scope_depth;
DEBUG_STRUCT_CONSTRUCTOR( AddHdrContext )
} AddHdrContext;
/*
* Return the largest x such that 2^x <= n, n>0
* Return -1 if n==0
*/
static int
NumToBits(unsigned int n)
{
int i;
if (!n)
return -1;
for (i=0; n > 1; i++)
n >>= 1;
return i;
}
/* Used in V3 or V4 mode. Flush out header. Only does anything once. */
static PGPError
FlushHeader(AddHdrContext *context)
{
PGPError error;
size_t retlen;
while (context->hdrlen) {
retlen = context->tail->write(context->tail,
context->ptr,
context->hdrlen,
&error);
context->ptr += retlen;
context->hdrlen -= retlen;
if (error)
return error;
}
return( kPGPError_NoErr );
}
/* Used in V4 mode. Flush out all bytes from the buffer. */
static PGPError
FlushBuffer(AddHdrContext *context)
{
PGPError error;
size_t retlen;
while (context->buflen) {
retlen = context->tail->write(context->tail,
context->bufptr,
context->buflen,
&error);
context->bufptr += retlen;
context->buflen -= retlen;
if (error)
return error;
}
return( kPGPError_NoErr );
}
/* Used only in V4 mode. Flush out all bytes from the buffer; first
* midflush of them, then the remainder with power-of-two sized
* partial body length packets until they're all flushed.
*/
static PGPError
ForceFlush(AddHdrContext *context)
{
PGPError error;
int bufl, flushed;
while (context->buflen) {
/* First, flush out any mid-flush we might have */
bufl = context->buflen;
flushed = context->midflush;
context->buflen = context->midflush;
error = FlushBuffer(context);
flushed -= context->buflen;
context->midflush = context->buflen;
context->buflen = bufl - flushed;
if (error || (context->buflen == 0))
return error;
#ifndef BIGBLOCKS
/*
* Now, figure out how much should be sent in the
* next sub-packet
*/
bufl = NumToBits(context->buflen);
if (bufl < 0)
break;
context->bufptr--;
context->buflen++;
*(context->bufptr) = 0xE0 + bufl;
context->midflush = (1<<bufl) + 1;
#else /* BIGBLOCKS */
if (context->lastblock && PKTLEN_ONE_BYTE(context->buflen)) {
context->bufptr--;
context->buflen++;
context->bufptr[0] = PKTLEN_1BYTE(context->buflen);
context->midflush = context->buflen;
} else if (context->lastblock && PKTLEN_TWO_BYTES(context->buflen)) {
context->bufptr -= 2;
context->buflen += 2;
context->bufptr[0] = PKTLEN_BYTE0(context->buflen);
context->bufptr[1] = PKTLEN_BYTE1(context->buflen);
context->midflush = context->buflen;
} else {
bufl = NumToBits(context->buflen);
if (bufl < 0)
break;
if( context->lastblock && ((1<<bufl)==context->buflen) ) {
/* Last block must not be partial body length */
bufl--;
}
context->bufptr--;
context->buflen++;
*(context->bufptr) = 0xE0 + bufl;
context->midflush = (1<<bufl) + 1;
}
#endif /* BIGBLOCKS */
}
return( kPGPError_NoErr );
}
/* This is used only in V3 (old-style packets) mode. Flush out header,
* and everything from the fifo.
*/
static PGPError
DoFlush(AddHdrContext *context)
{
PGPError error = kPGPError_NoErr;
size_t retlen;
PGPByte const *p;
PGPSize len;
error = FlushHeader(context);
if (error)
return error;
/* Next, try to flush anything that we have buffered in the fifo */
p = pgpFifoPeek (context->fifod, context->fifo, &len);
while (len) {
retlen = context->tail->write(context->tail,
p, len, &error);
pgpFifoSeek (context->fifod, context->fifo, retlen);
if (error)
return error;
p = pgpFifoPeek (context->fifod, context->fifo, &len);
}
return error;
}
/* This is called in both V3 and V4 mode. */
static PGPError
Flush(PGPPipeline *myself)
{
AddHdrContext *context;
PGPError error;
pgpAssert(myself);
pgpAssert(myself->magic == ADDHEADERMAGIC);
context = (AddHdrContext *)myself->priv;
pgpAssert(context);
pgpAssert(context->tail);
if (context->buffer) {
/* V4 mode */
error = FlushHeader(context);
if (error)
return error;
/* Using the new packets -- force a flush of the buffer */
if (context->dowrite) {
error = FlushBuffer(context);
if (error)
return error;
} else {
error = ForceFlush(context);
if (error)
return error;
}
} else {
/* V3 mode */
/* Using the fifo */
if (!context->dowrite)
return kPGPError_BadParams;
error = DoFlush(context);
if (error)
return error;
}
return context->tail->flush(context->tail);
}
/* This is only used in V4 mode */
/* Note the order here; once we fill a buffer we don't write it out
* until the next time we are called. This way we won't write the
* last buffer here in case the file is a multiple of the buffer size.
* We are required to use a non-partial-body-length buffer for the
* last one. Possible hole: if flush is called when we are holding
* exactly one buffer-full, we will write it out, and if that then
* turns out to be the last buffer in the message, we would not
* terminate it properly. However we do not in fact call flush at
* present.
*/
static size_t
BufferWrite(PGPPipeline *myself, PGPByte const *buf, size_t size,
PGPError *error)
{
AddHdrContext *context;
size_t size0 = size;
int t;
pgpAssert(myself);
pgpAssert(myself->magic == ADDHEADERMAGIC);
pgpAssert(error);
context = (AddHdrContext *)myself->priv;
pgpAssert(context);
pgpAssert(context->tail);
pgpAssert(context->buffer);
*error = FlushHeader(context);
if (*error)
return 0;
do {
/* If we are in the middle of a block, flush it out */
if (context->dowrite) {
*error = FlushBuffer(context);
if (*error)
break;
context->dowrite = 0;
context->bufptr = context->buffer + 2;
context->bufwrite = context->bufptr;
}
/* Try to fill up the buffer */
t = pgpMin(((size_t)(BUFFER_SIZE - context->buflen)), size);
if (t) {
memcpy(context->bufwrite, buf, t);
buf += t;
size -= t;
context->bufwrite += t;
context->buflen += t;
}
/*
* If we have a full block, write out the length
* and then write out the block
*/
if (context->buflen == BUFFER_SIZE) {
context->dowrite = 1;
context->bufptr--;
context->buflen++;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -