xpdf version 0.80 - Derek B. Noonburg
Decryption patch Leo J.B. Smiers
Decryption reworked for xpdf-0.80
Tony Nugent <Tony.Nugent@usq.edu.au>
December 1998

diff -urN xpdf-0.80.orig/xpdf/Makefile.in xpdf-0.80/xpdf/Makefile.in
--- xpdf-0.80.orig/xpdf/Makefile.in	Sat Nov 28 13:49:23 1998
+++ xpdf-0.80/xpdf/Makefile.in	Wed Dec 16 17:03:00 1998
@@ -76,7 +76,7 @@
 XPDF_OBJS = Array.o Catalog.o Dict.o Error.o Gfx.o GfxFont.o \
 	GfxState.o Lexer.o Link.o LTKOutputDev.o Object.o OutputDev.o \
 	Page.o Params.o Parser.o PDFDoc.o PSOutputDev.o Stream.o \
-	TextOutputDev.o XOutputDev.o XRef.o xpdf.o
+	TextOutputDev.o XOutputDev.o XRef.o xpdf.o md5.o rc4.o
 XPDF_LIBS = -L$(LTKLIBDIR) -lLTK -L$(GOOLIBDIR) -lGoo $(XLIBS) $(OTHERLIBS) -lm
 
 xpdf$(EXE): $(XPDF_OBJS)
@@ -92,7 +92,7 @@
 
 PDFTOPS_OBJS = Array.o Catalog.o Dict.o Error.o Gfx.o GfxFont.o \
 	GfxState.o Lexer.o Link.o Object.o OutputDev.o Page.o Params.o \
-	Parser.o PDFDoc.o PSOutputDev.o Stream.o XRef.o pdftops.o
+	Parser.o PDFDoc.o PSOutputDev.o Stream.o XRef.o pdftops.o md5.o rc4.o
 PDFTOPS_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
 
 pdftops$(EXE): $(PDFTOPS_OBJS)
@@ -103,7 +103,7 @@
 
 PDFTOTEXT_OBJS = Array.o Catalog.o Dict.o Error.o Gfx.o GfxFont.o \
 	GfxState.o Lexer.o Link.o Object.o OutputDev.o Page.o Params.o \
-	Parser.o PDFDoc.o TextOutputDev.o Stream.o XRef.o pdftotext.o
+	Parser.o PDFDoc.o TextOutputDev.o Stream.o XRef.o pdftotext.o md5.o rc4.o
 PDFTOTEXT_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
 
 pdftotext$(EXE): $(PDFTOTEXT_OBJS)
@@ -115,7 +115,7 @@
 
 PDFINFO_OBJS = Array.o Catalog.o Dict.o Error.o Gfx.o GfxFont.o \
 	GfxState.o Lexer.o Link.o Object.o OutputDev.o Page.o \
-	Params.o Parser.o PDFDoc.o Stream.o XRef.o pdfinfo.o
+	Params.o Parser.o PDFDoc.o Stream.o XRef.o pdfinfo.o md5.o rc4.o
 PDFINFO_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
 
 pdfinfo$(EXE): $(PDFINFO_OBJS)
@@ -127,7 +127,7 @@
 PDFTOPBM_OBJS = Array.o Catalog.o Dict.o Error.o Gfx.o GfxFont.o \
 	GfxState.o Lexer.o Link.o Object.o OutputDev.o PBMOutputDev.o \
 	Page.o Params.o Parser.o PDFDoc.o Stream.o TextOutputDev.o \
-	XOutputDev.o XRef.o pdftopbm.o
+	XOutputDev.o XRef.o pdftopbm.o md5.o rc4.o
 PDFTOPBM_LIBS = -L$(GOOLIBDIR) -lGoo $(XLIBS) $(OTHERLIBS) -lm
 
 pdftopbm$(EXE): $(PDFTOPBM_OBJS)
@@ -138,7 +138,7 @@
 
 PDFIMAGES_OBJS = Array.o Catalog.o Dict.o Error.o Gfx.o GfxFont.o \
 	GfxState.o ImageOutputDev.o Lexer.o Link.o Object.o OutputDev.o \
-	Page.o Params.o Parser.o PDFDoc.o Stream.o XRef.o pdfimages.o
+	Page.o Params.o Parser.o PDFDoc.o Stream.o XRef.o pdfimages.o md5.o rc4.o
 PDFIMAGES_LIBS = -L$(GOOLIBDIR) -lGoo $(OTHERLIBS) -lm
 
 pdfimages$(EXE): $(PDFIMAGES_OBJS)
diff -urN xpdf-0.80.orig/xpdf/Parser.cc xpdf-0.80/xpdf/Parser.cc
--- xpdf-0.80.orig/xpdf/Parser.cc	Sat Nov 28 13:47:03 1998
+++ xpdf-0.80/xpdf/Parser.cc	Thu Dec 17 23:28:17 1998
@@ -16,6 +16,8 @@
 #include "Dict.h"
 #include "Parser.h"
 #include "Error.h"
+#include "md5.h"
+#include "XRef.h"
 
 Parser::Parser(Lexer *lexer1) {
   lexer = lexer1;
@@ -158,4 +160,122 @@
     buf2.initNull();
   else
     lexer->getObj(&buf2);
+}
+
+Object *Parser::getEncryptedObj(Object *obj, int num, int gen) {
+  char *key;
+  Stream *str;
+  Object obj2;
+  MD5 context;
+  RC4KEY rc4Key;
+  GString MD5Key;
+  GString Key;
+
+  // refill buffer after inline image data
+  if (inlineImg == 2) {
+    buf1.free();
+    buf2.free();
+    lexer->getObj(&buf1);
+    lexer->getObj(&buf2);
+    inlineImg = 0;
+  }
+
+  // array
+  if (buf1.isCmd("[")) {
+    shift();
+    obj->initArray();
+    while (!buf1.isCmd("]") && !buf1.isEOF())
+      obj->arrayAdd(getEncryptedObj(&obj2, num, gen));
+    if (buf1.isEOF())
+      error(getPos(), "End of file inside array");
+    shift();
+
+  // dictionary or stream
+  } else if (buf1.isCmd("<<")) {
+    shift();
+    obj->initDict();
+    while (!buf1.isCmd(">>") && !buf1.isEOF()) {
+      if (!buf1.isName()) {
+ 	error(getPos(), "Dictionary key must be a name object");
+	shift();
+      } else {
+	key = copyString(buf1.getName());
+	shift();
+	if (buf1.isEOF() || buf1.isError())
+	  break;
+	obj->dictAdd(key, getEncryptedObj(&obj2, num, gen));
+      }
+    }
+    if (buf1.isEOF())
+      error(getPos(), "End of file inside dictionary");
+    if (buf2.isCmd("stream")) {
+      if ((str = makeStream(obj))) {
+	obj->initStream(str);
+        // enable Decryption on base File Stream
+        str = obj->getStream()->getBaseStream();
+        Key.append(xref->getEncryptionKey());
+        Key.append((char)( num       & 0xff));
+        Key.append((char)((num >> 8) & 0xff));
+        Key.append((char)((num >>16) & 0xff));
+        Key.append((char)( gen       & 0xff));
+        Key.append((char)((gen >> 8) & 0xff));
+        context.update((unsigned char *)Key.getCString(), 10);
+        context.finalize();
+        MD5Key.clear();
+        char *p = (char *)context.raw_digest();
+        MD5Key.append(p,10);
+        delete p;
+        // determine rc4Key
+        rc4ExpandKey(&rc4Key, (unsigned char *)MD5Key.getCString(), 10);
+        str->enableEncryption(&rc4Key);
+      } else {
+	obj->free();
+	obj->initError();
+      }
+    } else {
+      shift();
+    }
+
+  // indirect reference or integer
+  } else if (buf1.isInt()) {
+    num = buf1.getInt();
+    shift();
+    if (buf1.isInt() && buf2.isCmd("R")) {
+      obj->initRef(num, buf1.getInt());
+      shift();
+      shift();
+    } else {
+      obj->initInt(num);
+    }
+
+  // decrypt strings
+  } else if (buf1.isString()) {
+    buf1.copy(obj);
+    GString *s;
+    s = obj->getString();
+    Key.append(xref->getEncryptionKey());
+    Key.append((char)( num       & 0xff));
+    Key.append((char)((num >> 8) & 0xff));
+    Key.append((char)((num >>16) & 0xff));
+    Key.append((char)( gen       & 0xff));
+    Key.append((char)((gen >> 8) & 0xff));
+    context.update((unsigned char *)Key.getCString(),10);
+    context.finalize();
+    MD5Key.clear();
+    char *p = (char *)context.raw_digest();
+    MD5Key.append(p,10);
+    delete p;
+    // determine rc4Key
+    rc4ExpandKey(&rc4Key, (unsigned char *)MD5Key.getCString(),10);
+    rc4Crypt (&rc4Key,
+      (unsigned char *) obj->getString()->getCString(),
+      obj->getString()->getLength());
+    shift();
+
+  } else {
+    buf1.copy(obj);
+    shift();
+  }
+
+  return obj;
 }
diff -urN xpdf-0.80.orig/xpdf/Parser.h xpdf-0.80/xpdf/Parser.h
--- xpdf-0.80.orig/xpdf/Parser.h	Sat Nov 28 13:47:09 1998
+++ xpdf-0.80/xpdf/Parser.h	Thu Dec 17 23:28:17 1998
@@ -30,6 +30,8 @@
 
   // Get the next object from the input stream.
   Object *getObj(Object *obj);
+  // if encrypted...
+  Object *getEncryptedObj(Object *obj, int num, int gen);
 
   // Get stream.
   Stream *getStream() { return lexer->getStream(); }
diff -urN xpdf-0.80.orig/xpdf/Stream.cc xpdf-0.80/xpdf/Stream.cc
--- xpdf-0.80.orig/xpdf/Stream.cc	Sat Nov 28 13:47:24 1998
+++ xpdf-0.80/xpdf/Stream.cc	Wed Dec 16 17:03:01 1998
@@ -415,6 +415,7 @@
   bufPos = start;
   savePos = -1;
   dict = *dict1;
+  encryption = gFalse;
 }
 
 FileStream::~FileStream() {
@@ -443,9 +444,19 @@
     n = 256;
   n = fread(buf, 1, n, f);
   bufEnd = buf + n;
+  if (encryption == gTrue) {
+    rc4Crypt(&rc4Key, (unsigned char *)buf, n);
+  }
   if (bufPtr >= bufEnd)
     return gFalse;
   return gTrue;
+}
+
+void FileStream::enableEncryption(RC4KEY *prc4Key)
+{
+  // prepare RC4 key
+  rc4Key = *prc4Key;
+  encryption = gTrue;
 }
 
 void FileStream::setPos(int pos1) {
diff -urN xpdf-0.80.orig/xpdf/Stream.h xpdf-0.80/xpdf/Stream.h
--- xpdf-0.80.orig/xpdf/Stream.h	Sat Nov 28 13:47:33 1998
+++ xpdf-0.80/xpdf/Stream.h	Thu Dec 17 23:28:17 1998
@@ -16,6 +16,8 @@
 #include <stdio.h>
 #include "gtypes.h"
 #include "Object.h"
+#include "Error.h"
+#include "rc4.h"
 
 //------------------------------------------------------------------------
 
@@ -104,6 +106,8 @@
   // Returns the new stream.
   Stream *addFilters(Object *dict);
 
+  virtual void enableEncryption(RC4KEY *rc4Key) {error(-1, "No FileStream, no decryption");};
+
 private:
 
   Stream *makeFilter(char *name, Stream *str, Object *params);
@@ -156,8 +160,12 @@
 
 private:
 
+  void enableEncryption(RC4KEY *rc4Key);
+
   GBool fillBuf();
 
+  RC4KEY rc4Key;
+  GBool encryption;
   FILE *f;
   int start;
   int length;
diff -urN xpdf-0.80.orig/xpdf/XRef.cc xpdf-0.80/xpdf/XRef.cc
--- xpdf-0.80.orig/xpdf/XRef.cc	Sat Nov 28 13:48:14 1998
+++ xpdf-0.80/xpdf/XRef.cc	Thu Dec 17 23:28:17 1998
@@ -22,6 +22,8 @@
 #include "Dict.h"
 #include "Error.h"
 #include "XRef.h"
+#include "md5.h"
+#include "rc4.h"
 
 //------------------------------------------------------------------------
 
@@ -46,6 +48,7 @@
   ok = gTrue;
   size = 0;
   entries = NULL;
+  encrypted = gFalse;
 
   // get rid of old xref (otherwise it will try to fetch the Root object
   // in the new document, using the old xref)
@@ -91,16 +94,23 @@
   xref = this;
 
   // check for encryption
+  m_okToPrint = gTrue;
+  m_okToCopy = gTrue;
+  m_okToChange = gTrue;
+  m_okToAddNotes = gTrue;
   if (checkEncrypted()) {
-    ok = gFalse;
-    xref = oldXref;
-    return;
+    if (setupDecryption() == gFalse) {
+      ok = gFalse;
+      xref = oldXref;
+      return;
+    }
   }
 }
 
 XRef::~XRef() {
   gfree(entries);
   trailerDict.free();
+  encryptionDict.free();
 }
 
 // Read startxref position, xref table size, and root.  Returns
@@ -380,26 +390,25 @@
 }
 
 GBool XRef::checkEncrypted() {
-  Object obj;
-  GBool encrypted;
-
-  trailerDict.dictLookup("Encrypt", &obj);
-  if ((encrypted = !obj.isNull())) {
-    error(-1, "PDF file is encrypted and cannot be displayed");
-    error(-1, "* Decryption support is currently not included in xpdf");
-    error(-1, "* due to legal restrictions: the U.S.A. still has bogus");
-    error(-1, "* export controls on cryptography software.");
-  }
-  obj.free();
+  trailerDict.dictLookup("Encrypt", &encryptionDict);
+  encrypted = encryptionDict.isDict();
   return encrypted;
 }
 
 GBool XRef::okToPrint() {
-  return gTrue;
+  return m_okToPrint;
 }
 
 GBool XRef::okToCopy() {
-  return gTrue;
+  return m_okToCopy;
+}
+
+GBool XRef::okToChange() {
+  return m_okToChange;
+}
+
+GBool XRef::okToAddNotes() {
+  return m_okToAddNotes;
 }
 
 Object *XRef::fetch(int num, int gen, Object *obj) {
@@ -424,7 +433,11 @@
     if (obj1.isInt() && obj1.getInt() == num &&
 	obj2.isInt() && obj2.getInt() == gen &&
 	obj3.isCmd("obj")) {
-      parser->getObj(obj);
+      if (encrypted) {
+        parser->getEncryptedObj(obj, num, gen);
+      } else {
+        parser->getObj(obj);
+      }
     } else {
       obj->initNull();
     }
@@ -440,4 +453,149 @@
 
 Object *XRef::getDocInfo(Object *obj) {
   return trailerDict.dictLookup("Info", obj);
+}
+
+GBool XRef::setupDecryption() {
+  Object obj;
+  GBool encrypted;
+  GBool passwordOk;
+  GString userPassword;
+
+  // check filter
+  encryptionDict.dictLookupNF("Filter", &obj);
+  if (!obj.isName()) {
+    error(-1, "No filter specified, asume Standard filter");
+  } else if (strcmp(obj.getName(), "Standard") != 0) {
+    error(-1, "File is encrypted with a non Standard Filter.\
+xpdf supports only the standard encryption filter");
+    obj.free();
+    return gFalse;
+  }
+  obj.free();
+
+  // check for no user password
+  userPassword.clear();
+  if (checkUserPassword(&userPassword) == gFalse) {
+    // ask for user password  ...  check with user password
+    error(-1, "pdf file is encrypted with a user password.\
+xpdf does not support passwords please contact the author of the pdf file\
+and ask him for a non password encrypted version");
+    return gFalse;
+  }
+
+  // set permissions
+  encryptionDict.dictLookupNF("P", &obj);
+  if (obj.isNull() || !obj.isInt()) {
+    error(-1, "No permissions specified");
+    return gFalse;
+  }
+  int Permissions = obj.getInt();
+  obj.free();
+  m_okToPrint = Permissions & 0x04 ? gTrue : gFalse;
+  m_okToChange = Permissions & 0x08 ? gTrue : gFalse;
+  m_okToCopy = Permissions & 0x10 ? gTrue : gFalse;
+  m_okToAddNotes = Permissions & 0x20 ? gTrue : gFalse;
+
+  return gTrue;
+}
+
+GBool
+XRef::checkUserPassword(GString *userPassword)
+{
+  GString preparedPassword;
+  RC4KEY rc4Key;
+  Object obj;
+  char localPassword[32];
+
+  if (preparePassword(userPassword, &preparedPassword) == gFalse) {
+    return gFalse;
+  }
+  if (MakeEncryptionKey(&preparedPassword, &encryptionKey) == gFalse) {
+    return gFalse;
+  }
+  // get the User password
+  encryptionDict.dictLookupNF("U", &obj);
+  if (obj.isNull() || !obj.isString()) {
+    error(-1, "No user password specified");
+    return gFalse;
+  }
+  memcpy(localPassword, obj.getString()->getCString(), 32);
+  obj.free();
+  // prepary RC4 key
+  rc4ExpandKey(&rc4Key, (unsigned char *)encryptionKey.getCString(), 5);
+  // encrypt User password
+  rc4Crypt(&rc4Key, (unsigned char *)&localPassword[0], 32);
+  // compare preparedPassword and decryptedPassword
+  for (int i=0; i<32; i++) {
+    if (localPassword[i] != preparedPassword.getChar(i)) {
+      // not equal
+      return gFalse;
+    }
+  }
+  // password OK
+  return gTrue;
+}
+
+GBool
+XRef::MakeEncryptionKey(GString *password, GString *encryptionKey)
+{
+  MD5 context;
+  Object obj, obj1;
+  unsigned char Pkey[4];
+
+  context.update((unsigned char *)password->getCString(), 32);
+  // others  owner key
+  encryptionDict.dictLookupNF("O", &obj);
+  if (obj.isNull() || !obj.isString()) {
+    error(-1, "No owner password specified");
+    return gFalse;
+  }
+  context.update((unsigned char *)obj.getString()->getCString(), 32);
+  obj.free();
+  // permissions
+  encryptionDict.dictLookupNF("P", &obj);
+  if (obj.isNull() || !obj.isInt()) {
+    error(-1, "No permissions specified");
+    return gFalse;
+  }
+  int Permissions = obj.getInt();
+  Pkey[0] =  Permissions        & 0xff;
+  Pkey[1] = (Permissions >> 8 ) & 0xff;
+  Pkey[2] = (Permissions >> 16) & 0xff;
+  Pkey[3] = (Permissions >> 24) & 0xff;
+  context.update(&Pkey[0], 4);
+  obj.free();
+  // first element from ID
+  trailerDict.dictLookupNF("ID", &obj);
+  if (obj.isNull() || !obj.isArray()) {
+    error(-1, "No ID specified");
+    return gFalse;
+  }
+  obj.arrayGet(0, &obj1);
+  if (obj1.isNull() || !obj1.isString()) {
+    error(-1, "No ID elements specified");
+    return gFalse;
+  }
+  context.update((unsigned char *)(obj1.getString()->getCString()), (int)(obj1.getString()->getLength()));
+  obj1.free();
+  obj.free();
+  context.finalize();
+  encryptionKey->clear();
+  char *p = (char *)context.raw_digest();
+  encryptionKey->append(p,5);
+  delete p;
+  return gTrue;
+}
+
+GBool
+XRef::preparePassword(GString *password, GString *preparedPassword)
+{
+  static char acFill[32] = {0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41,
+  	      		    0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08,
+  	      		    0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80,
+  	      		    0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a};
+  preparedPassword->clear();
+  preparedPassword->append(password);
+  preparedPassword->append(&acFill[0], 32);
+  return gTrue;
 }
diff -urN xpdf-0.80.orig/xpdf/XRef.h xpdf-0.80/xpdf/XRef.h
--- xpdf-0.80.orig/xpdf/XRef.h	Sat Nov 28 13:48:39 1998
+++ xpdf-0.80/xpdf/XRef.h	Thu Dec 17 23:03:08 1998
@@ -43,15 +43,20 @@
   GBool isOk() { return ok; }
 
   // Is the file encrypted?
-  GBool isEncrypted() { return gFalse; }
+  GBool isEncrypted() { return encrypted; }
 
   // Are printing and copying allowed?  If not, print an error message.
   GBool okToPrint();
   GBool okToCopy();
+  GBool okToChange();
+  GBool okToAddNotes();
 
   // Get catalog object.
   Object *getCatalog(Object *obj) { return fetch(rootNum, rootGen, obj); }
 
+  // Get encryption key
+  GString *getEncryptionKey() { return &encryptionKey; };
+
   // Fetch an indirect reference.
   Object *fetch(int num, int gen, Object *obj);
 
@@ -68,6 +73,19 @@
   int rootNum, rootGen;		// catalog dict
   GBool ok;			// true if xref table is valid
   Object trailerDict;		// trailer dictionary
+
+  GBool m_okToPrint;
+  GBool m_okToCopy;
+  GBool m_okToChange;
+  GBool m_okToAddNotes;
+  Object encryptionDict;		// encryption dictionary
+  GBool encrypted;
+  GString encryptionKey;
+
+  GBool setupDecryption();
+  GBool checkUserPassword(GString *userPassword);
+  GBool MakeEncryptionKey(GString *password, GString *encryptionKey);
+  GBool preparePassword(GString *password, GString *preparedPassword);
 
   int readTrailer(FileStream *str);
   GBool readXRef(FileStream *str, int *pos);
diff -urN xpdf-0.80.orig/xpdf/config.h xpdf-0.80/xpdf/config.h
--- xpdf-0.80.orig/xpdf/config.h	Sat Nov 28 13:42:17 1998
+++ xpdf-0.80/xpdf/config.h	Thu Dec 17 23:28:17 1998
@@ -4,6 +4,9 @@
 //
 // Copyright 1996 Derek B. Noonburg
 //
+// original decryption patch by Leo J.B. Smiers
+// patch re-taken for v0.7a and reworked for v0.80 by Tony Nugent
+//
 //========================================================================
 
 #ifndef CONFIG_H
@@ -14,7 +17,7 @@
 //------------------------------------------------------------------------
 
 // xpdf version
-#define xpdfVersion "0.80"
+#define xpdfVersion "0.80 (decryption)"
 
 // supported PDF version
 #define pdfVersion "1.2"
@@ -22,6 +25,7 @@
 
 // copyright notice
 #define xpdfCopyright "Copyright \251 1996-1998 Derek B. Noonburg"
+#define xpdfCopyright_Encryption "Decryption (originally) by Leo J.B. Smiers"
 
 // default paper size (in points) for PostScript output
 #ifdef A4_PAPER
diff -urN xpdf-0.80.orig/xpdf/md5.cc xpdf-0.80/xpdf/md5.cc
--- xpdf-0.80.orig/xpdf/md5.cc	Thu Jan  1 10:00:00 1970
+++ xpdf-0.80/xpdf/md5.cc	Wed Dec 16 17:03:01 1998
@@ -0,0 +1,544 @@
+// MD5.CC - source code for the C++/object oriented translation and
+//          modification of MD5.
+
+// Translation and modification (c) 1995 by Mordechai T. Abzug
+
+// This translation/ modification is provided "as is," without express or
+// implied warranty of any kind.
+
+// The translator/ modifier does not claim (1) that MD5 will do what you think
+// it does; (2) that this translation/ modification is accurate; or (3) that
+// this software is "merchantible."  (Language for this disclaimer partially
+// copied from the disclaimer below).
+
+/* based on:
+
+   MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+   MDDRIVER.C - test driver for MD2, MD4 and MD5
+
+
+   Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+
+ */
+
+
+
+
+
+
+#include "md5.h"
+
+#include <assert.h>
+#include <strings.h>
+#include <iostream.h>
+
+
+
+
+// MD5 simple initialization method
+
+MD5::MD5(){
+
+  init();
+
+}
+
+
+
+
+// MD5 block update operation. Continues an MD5 message-digest
+// operation, processing another message block, and updating the
+// context.
+
+void MD5::update (uint1 *input, uint4 input_length) {
+
+  uint4 input_index, buffer_index;
+  uint4 buffer_space;                // how much space is left in buffer
+
+  if (finalized){  // so we can't update!
+    cerr << "MD5::update:  Can't update a finalized digest!" << endl;
+    return;
+  }
+
+  // Compute number of bytes mod 64
+  buffer_index = (unsigned int)((count[0] >> 3) & 0x3F);
+
+  // Update number of bits
+  if (  (count[0] += ((uint4) input_length << 3))<((uint4) input_length << 3) )
+    count[1]++;
+
+  count[1] += ((uint4)input_length >> 29);
+
+
+  buffer_space = 64 - buffer_index;  // how much space is left in buffer
+
+  // Transform as many times as possible.
+  if (input_length >= buffer_space) { // ie. we have enough to fill the buffer
+    // fill the rest of the buffer and transform
+    memcpy (buffer + buffer_index, input, buffer_space);
+    transform (buffer);
+
+    // now, transform each 64-byte piece of the input, bypassing the buffer
+    for (input_index = buffer_space; input_index + 63 < input_length;
+	 input_index += 64)
+      transform (input+input_index);
+
+    buffer_index = 0;  // so we can buffer remaining
+  }
+  else
+    input_index=0;     // so we can buffer the whole input
+
+
+  // and here we do the buffering:
+  memcpy(buffer+buffer_index, input+input_index, input_length-input_index);
+}
+
+
+
+// MD5 update for files.
+// Like above, except that it works on files (and uses above as a primitive.)
+
+void MD5::update(FILE *file){
+
+  unsigned char buffer[1024];
+  int len;
+
+  while (len=fread(buffer, 1, 1024, file))
+    update(buffer, len);
+
+  fclose (file);
+
+}
+
+
+
+
+
+
+// MD5 update for istreams.
+// Like update for files; see above.
+
+void MD5::update(istream& stream){
+
+  unsigned char buffer[1024];
+  int len;
+
+  while (stream.good()){
+    stream.read(buffer, 1024); // note that return value of read is unusable.
+    len=stream.gcount();
+    update(buffer, len);
+  }
+
+}
+
+
+
+
+
+
+// MD5 update for ifstreams.
+// Like update for files; see above.
+
+void MD5::update(ifstream& stream){
+
+  unsigned char buffer[1024];
+  int len;
+
+  while (stream.good()){
+    stream.read(buffer, 1024); // note that return value of read is unusable.
+    len=stream.gcount();
+    update(buffer, len);
+  }
+
+}
+
+
+
+
+
+
+// MD5 finalization. Ends an MD5 message-digest operation, writing the
+// the message digest and zeroizing the context.
+
+
+void MD5::finalize (){
+
+  unsigned char bits[8];
+  unsigned int index, padLen;
+  static uint1 PADDING[64]={
+    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+
+  if (finalized){
+    cerr << "MD5::finalize:  Already finalized this digest!" << endl;
+    return;
+  }
+
+  // Save number of bits
+  encode (bits, count, 8);
+
+  // Pad out to 56 mod 64.
+  index = (uint4) ((count[0] >> 3) & 0x3f);
+  padLen = (index < 56) ? (56 - index) : (120 - index);
+  update (PADDING, padLen);
+
+  // Append length (before padding)
+  update (bits, 8);
+
+  // Store state in digest
+  encode (digest, state, 16);
+
+  // Zeroize sensitive information
+  memset (buffer, 0, sizeof(*buffer));
+
+  finalized=1;
+
+}
+
+
+
+
+MD5::MD5(FILE *file){
+
+  init();  // must be called be all constructors
+  update(file);
+  finalize ();
+}
+
+
+
+
+MD5::MD5(istream& stream){
+
+  init();  // must called by all constructors
+  update (stream);
+  finalize();
+}
+
+
+
+MD5::MD5(ifstream& stream){
+
+  init();  // must called by all constructors
+  update (stream);
+  finalize();
+}
+
+
+
+unsigned char *MD5::raw_digest(){
+
+  uint1 *s = new uint1[16];
+
+  if (!finalized){
+    cerr << "MD5::raw_digest:  Can't get digest if you haven't "<<
+      "finalized the digest!" <<endl;
+    return ( (unsigned char*) "");
+  }
+
+  memcpy(s, digest, 16);
+  return s;
+}
+
+
+
+char *MD5::hex_digest(){
+
+  int i;
+  char *s= new char[33];
+
+  if (!finalized){
+    cerr << "MD5::hex_digest:  Can't get digest if you haven't "<<
+      "finalized the digest!" <<endl;
+    return "";
+  }
+
+  for (i=0; i<16; i++)
+    sprintf(s+i*2, "%02x", digest[i]);
+
+  s[32]='\0';
+
+  return s;
+}
+
+
+
+
+
+ostream& operator<<(ostream &stream, MD5 context){
+
+  stream << context.hex_digest();
+  return stream;
+}
+
+
+
+
+// PRIVATE METHODS:
+
+
+
+void MD5::init(){
+  finalized=0;  // we just started!
+
+  // Nothing counted, so count=0
+  count[0] = 0;
+  count[1] = 0;
+
+  // Load magic initialization constants.
+  state[0] = 0x67452301;
+  state[1] = 0xefcdab89;
+  state[2] = 0x98badcfe;
+  state[3] = 0x10325476;
+}
+
+
+
+// Constants for MD5Transform routine.
+// Although we could use C++ style constants, defines are actually better,
+// since they let us easily evade scope clashes.
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+
+
+
+// MD5 basic transformation. Transforms state based on block.
+void MD5::transform (uint1 block[64]){
+
+  uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+  decode (x, block, 64);
+
+  assert(!finalized);  // not just a user error, since the method is private
+
+  /* Round 1 */
+  FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+  FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+  FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+  FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+  FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+  FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+  FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+  FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+  FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+  FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+  FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+  GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+  GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+  GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
+  GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+  GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+  GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+  /* Round 3 */
+  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+  HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+  HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+  HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+  HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+  HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+  HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
+  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+  HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+  /* Round 4 */
+  II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+  II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+  II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+  II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+  II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+  II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+  II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+  II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+  II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+  II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+  II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+  II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+  state[0] += a;
+  state[1] += b;
+  state[2] += c;
+  state[3] += d;
+
+  // Zeroize sensitive information.
+  memset ( (uint1 *) x, 0, sizeof(x));
+
+}
+
+
+
+// Encodes input (UINT4) into output (unsigned char). Assumes len is
+// a multiple of 4.
+void MD5::encode (uint1 *output, uint4 *input, uint4 len) {
+
+  unsigned int i, j;
+
+  for (i = 0, j = 0; j < len; i++, j += 4) {
+    output[j]   = (uint1)  (input[i] & 0xff);
+    output[j+1] = (uint1) ((input[i] >> 8) & 0xff);
+    output[j+2] = (uint1) ((input[i] >> 16) & 0xff);
+    output[j+3] = (uint1) ((input[i] >> 24) & 0xff);
+  }
+}
+
+
+
+
+// Decodes input (unsigned char) into output (UINT4). Assumes len is
+// a multiple of 4.
+void MD5::decode (uint4 *output, uint1 *input, uint4 len){
+
+  unsigned int i, j;
+
+  for (i = 0, j = 0; j < len; i++, j += 4)
+    output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) |
+      (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24);
+}
+
+
+
+
+
+// Note: Replace "for loop" with standard memcpy if possible.
+void MD5::memcpy (uint1 *output, uint1 *input, uint4 len){
+
+  unsigned int i;
+
+  for (i = 0; i < len; i++)
+    output[i] = input[i];
+}
+
+
+
+// Note: Replace "for loop" with standard memset if possible.
+void MD5::memset (uint1 *output, uint1 value, uint4 len){
+
+  unsigned int i;
+
+  for (i = 0; i < len; i++)
+    output[i] = value;
+}
+
+
+
+// ROTATE_LEFT rotates x left n bits.
+
+inline unsigned int MD5::rotate_left  (uint4 x, uint4 n){
+  return (x << n) | (x >> (32-n))  ;
+}
+
+
+
+
+// F, G, H and I are basic MD5 functions.
+
+inline unsigned int MD5::F            (uint4 x, uint4 y, uint4 z){
+  return (x & y) | (~x & z);
+}
+
+inline unsigned int MD5::G            (uint4 x, uint4 y, uint4 z){
+  return (x & z) | (y & ~z);
+}
+
+inline unsigned int MD5::H            (uint4 x, uint4 y, uint4 z){
+  return x ^ y ^ z;
+}
+
+inline unsigned int MD5::I            (uint4 x, uint4 y, uint4 z){
+  return y ^ (x | ~z);
+}
+
+
+
+// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+// Rotation is separate from addition to prevent recomputation.
+
+
+inline void MD5::FF(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
+		    uint4  s, uint4 ac){
+ a += F(b, c, d) + x + ac;
+ a = rotate_left (a, s) +b;
+}
+
+inline void MD5::GG(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
+		    uint4 s, uint4 ac){
+ a += G(b, c, d) + x + ac;
+ a = rotate_left (a, s) +b;
+}
+
+inline void MD5::HH(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
+		    uint4 s, uint4 ac){
+ a += H(b, c, d) + x + ac;
+ a = rotate_left (a, s) +b;
+}
+
+inline void MD5::II(uint4& a, uint4 b, uint4 c, uint4 d, uint4 x,
+			     uint4 s, uint4 ac){
+ a += I(b, c, d) + x + ac;
+ a = rotate_left (a, s) +b;
+}
diff -urN xpdf-0.80.orig/xpdf/md5.h xpdf-0.80/xpdf/md5.h
--- xpdf-0.80.orig/xpdf/md5.h	Thu Jan  1 10:00:00 1970
+++ xpdf-0.80/xpdf/md5.h	Wed Dec 16 17:03:01 1998
@@ -0,0 +1,109 @@
+// MD5.CC - source code for the C++/object oriented translation and 
+//          modification of MD5.
+
+// Translation and modification (c) 1995 by Mordechai T. Abzug 
+
+// This translation/ modification is provided "as is," without express or 
+// implied warranty of any kind.
+
+// The translator/ modifier does not claim (1) that MD5 will do what you think 
+// it does; (2) that this translation/ modification is accurate; or (3) that 
+// this software is "merchantible."  (Language for this disclaimer partially 
+// copied from the disclaimer below).
+
+/* based on:
+
+   MD5.H - header file for MD5C.C
+   MDDRIVER.C - test driver for MD2, MD4 and MD5
+
+   Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+
+*/
+
+#include <stdio.h>
+#include <fstream.h>
+#include <iostream.h>
+
+class MD5 {
+
+public:
+// methods for controlled operation:
+  MD5              ();  // simple initializer
+  void  update     (unsigned char *input, unsigned int input_length);
+  void  update     (istream& stream);
+  void  update     (FILE *file);
+  void  update     (ifstream& stream);
+  void  finalize   ();
+
+// constructors for special circumstances.  All these constructors finalize
+// the MD5 context.
+  MD5              (unsigned char *string); // digest string, finalize
+  MD5              (istream& stream);       // digest stream, finalize
+  MD5              (FILE *file);            // digest file, close, finalize
+  MD5              (ifstream& stream);      // digest stream, close, finalize
+
+// methods to acquire finalized result
+  unsigned char    *raw_digest ();  // digest as a 16-byte binary array
+  char *            hex_digest ();  // digest as a 33-byte ascii-hex string
+  friend ostream&   operator<< (ostream&, MD5 context);
+
+
+
+private:
+
+// first, some types:
+  typedef unsigned       int uint4; // assumes integer is 4 words long
+  typedef unsigned short int uint2; // assumes short integer is 2 words long
+  typedef unsigned      char uint1; // assumes char is 1 word long
+
+// next, the private data:
+  uint4 state[4];
+  uint4 count[2];     // number of *bits*, mod 2^64
+  uint1 buffer[64];   // input buffer
+  uint1 digest[16];
+  uint1 finalized;
+
+// last, the private methods, mostly static:
+  void init             ();               // called by all constructors
+  void transform        (uint1 *buffer);  // does the real update work.  Note 
+                                          // that length is implied to be 64.
+
+  static void encode    (uint1 *dest, uint4 *src, uint4 length);
+  static void decode    (uint4 *dest, uint1 *src, uint4 length);
+  static void memcpy    (uint1 *dest, uint1 *src, uint4 length);
+  static void memset    (uint1 *start, uint1 val, uint4 length);
+
+  static inline uint4  rotate_left (uint4 x, uint4 n);
+  static inline uint4  F           (uint4 x, uint4 y, uint4 z);
+  static inline uint4  G           (uint4 x, uint4 y, uint4 z);
+  static inline uint4  H           (uint4 x, uint4 y, uint4 z);
+  static inline uint4  I           (uint4 x, uint4 y, uint4 z);
+  static inline void   FF  (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, 
+			    uint4 s, uint4 ac);
+  static inline void   GG  (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, 
+			    uint4 s, uint4 ac);
+  static inline void   HH  (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, 
+			    uint4 s, uint4 ac);
+  static inline void   II  (uint4& a, uint4 b, uint4 c, uint4 d, uint4 x, 
+			    uint4 s, uint4 ac);
+
+};
diff -urN xpdf-0.80.orig/xpdf/pdftops.cc xpdf-0.80/xpdf/pdftops.cc
--- xpdf-0.80.orig/xpdf/pdftops.cc	Sat Nov 28 13:42:16 1998
+++ xpdf-0.80/xpdf/pdftops.cc	Wed Dec 16 17:03:01 1998
@@ -68,6 +68,7 @@
   if (!ok || argc < 2 || argc > 3 || printHelp) {
     fprintf(stderr, "pdftops version %s\n", xpdfVersion);
     fprintf(stderr, "%s\n", xpdfCopyright);
+    fprintf(stderr, "%s\n", xpdfCopyright_Encryption);
     printUsage("pdftops", "<PDF-file> [<PS-file>]", argDesc);
     exit(1);
   }
diff -urN xpdf-0.80.orig/xpdf/pdftotext.cc xpdf-0.80/xpdf/pdftotext.cc
--- xpdf-0.80.orig/xpdf/pdftotext.cc	Sat Nov 28 13:42:16 1998
+++ xpdf-0.80/xpdf/pdftotext.cc	Wed Dec 16 17:03:01 1998
@@ -59,6 +59,7 @@
   if (!ok || argc < 2 || argc > 3 || printHelp) {
     fprintf(stderr, "pdftotext version %s\n", xpdfVersion);
     fprintf(stderr, "%s\n", xpdfCopyright);
+    fprintf(stderr, "%s\n", xpdfCopyright_Encryption);
     printUsage("pdftotext", "<PDF-file> [<text-file>]", argDesc);
     exit(1);
   }
diff -urN xpdf-0.80.orig/xpdf/rc4.cc xpdf-0.80/xpdf/rc4.cc
--- xpdf-0.80.orig/xpdf/rc4.cc	Thu Jan  1 10:00:00 1970
+++ xpdf-0.80/xpdf/rc4.cc	Wed Dec 16 17:03:01 1998
@@ -0,0 +1,70 @@
+/************************************************************************
+ *									*
+ * RC4 Encryption Algorithm 						*
+ * Copyright Peter Gutmann 1995-1996					*
+ *									*
+ ************************************************************************/
+
+/* Optimized RC4 code, from an unknown source ("and they knew not from whence
+   it had come...") */
+
+#include "rc4.h"
+
+void
+rc4ExpandKey( RC4KEY *rc4, unsigned char const *key, int keylen )
+{
+	int x, keypos = 0;
+	rc4word sx, y = 0;
+	rc4word *state = &rc4->state[ 0 ];
+
+	rc4->x = rc4->y = 0;
+
+	for( x = 0; x < 256; x++ )
+		state[ x ] = x;
+
+	for( x = 0; x < 256; x++ )
+		{
+		sx = state[ x ];
+		y += sx + key[ keypos ];
+#ifdef USE_LONG_RC4
+		y &= 0xFF;
+#endif /* USE_LONG_RC4 */
+		state[ x ] = state[ y ];
+		state[ y ] = sx;
+
+		if( ++keypos == keylen )
+			keypos = 0;
+		}
+	}
+
+void
+rc4Crypt( RC4KEY *rc4, unsigned char *data, int len )
+{
+	rc4word x = rc4->x, y = rc4->y;
+	rc4word sx, sy;
+	rc4word *state = &rc4->state[ 0 ];
+
+	while (len--) {
+		x++;
+#ifdef USE_LONG_RC4
+		x &= 0xFF;
+#endif /* USE_LONG_RC4 */
+		sx = state[ x ];
+		y += sx;
+#ifdef USE_LONG_RC4
+		y &= 0xFF;
+#endif /* USE_LONG_RC4 */
+		sy = state[ y ];
+		state[ y ] = sx;
+		state[ x ] = sy;
+
+#ifdef USE_LONG_RC4
+		*data++ ^= state[ ( unsigned char ) ( sx+sy ) ];
+#else
+		*data++ ^= state[ ( sx+sy ) & 0xFF ];
+#endif /* USE_LONG_RC4 */
+	}
+
+	rc4->x = x;
+	rc4->y = y;
+}
diff -urN xpdf-0.80.orig/xpdf/rc4.h xpdf-0.80/xpdf/rc4.h
--- xpdf-0.80.orig/xpdf/rc4.h	Thu Jan  1 10:00:00 1970
+++ xpdf-0.80/xpdf/rc4.h	Wed Dec 16 17:03:01 1998
@@ -0,0 +1,25 @@
+#include <limits.h>
+
+#if !defined(__RC4_H__)
+#define __RC4_H__
+/* If the system can handle byte ops, we use those so we don't have to do a
+   lot of masking.  Otherwise, we use machine-word-size ops which will be
+   faster on RISC machines */
+
+#if UINT_MAX > 0xFFFFL		/* System has 32-bit ints */
+#define USE_LONG_RC4
+typedef unsigned int rc4word;
+#else
+typedef unsigned char rc4word;
+#endif /* UINT_MAX > 0xFFFFL */
+
+/* The scheduled RC4 key */
+
+typedef struct {
+	rc4word state[ 256 ];
+	rc4word x, y;
+} RC4KEY ;
+
+void rc4ExpandKey( RC4KEY *rc4, unsigned char const *key, int keylen );
+void rc4Crypt( RC4KEY *rc4, unsigned char *data, int len );
+#endif
diff -urN xpdf-0.80.orig/xpdf/xpdf.cc xpdf-0.80/xpdf/xpdf.cc
--- xpdf-0.80.orig/xpdf/xpdf.cc	Sat Nov 28 13:42:16 1998
+++ xpdf-0.80/xpdf/xpdf.cc	Wed Dec 16 17:03:01 1998
@@ -334,6 +334,7 @@
   if (!ok || printHelp) {
     fprintf(stderr, "xpdf version %s\n", xpdfVersion);
     fprintf(stderr, "%s\n", xpdfCopyright);
+    fprintf(stderr, "%s\n", xpdfCopyright_Encryption);
     printUsage("xpdf", "[<PDF-file> [<page>]]", argDesc);
     ret = 1;
     goto done2;
@@ -374,6 +375,7 @@
   // print banner
   fprintf(errFile, "xpdf version %s\n", xpdfVersion);
   fprintf(errFile, "%s\n", xpdfCopyright);
+  fprintf(stderr, "%s\n", xpdfCopyright_Encryption);
 
   // open PDF file
   defPSFileName = app->getStringResource("psFile", NULL);
diff -urN xpdf-0.80.orig/README.decryption xpdf-0.80/README.decryption
--- xpdf-0.80.orig/README.decryption	Thu Jan  1 10:00:00 1970
+++ xpdf-0.80/README.decryption	Wed Dec 16 17:03:01 1998
@@ -0,0 +1,103 @@
+xpdf version 0.80 with decryption patches
+
+With the release of version 0.80 of xpdf, I have retaken the decryption patch
+for this new version.
+
+Due to the changes that came with the 0.80 release, some re-working of parts of
+the decryption code became necessary in order to make it all work.
+
+Most of the comments (below) for version 0.7a still apply.
+
+DISCLAIMER:  I know next to nothing about decryption.  All I did was hack the
+code to make it work.
+
+Tony Nugent <Tony.Nugent@usq.edu.au>
+December 1998
+
+==========================================================================
+
+xpdf version 0.7a with decryption patches
+
+Author:		Derek B. Noonburg <derekn@foolabs.com>
+URL:		http://www.foolabs.com/xpdf/
+Sources:	ftp://ftp.foolabs.com/pub/xpdf/xpdf-0.7a.tar.gz
+		ftp://ftp.foolabs.com/pub/xpdf/xpdf-0.7a-patch1
+		ftp://ftp.foolabs.com/pub/xpdf/xpdf-0.7a-patch2
+		ftp://ftp.sci.usq.edu.au/pub/linux/xpdf/xpdf-0.7a-patch3
+Packager:	Tony Nugent <Tony.Nugent@usq.edu.au>
+
+This is a xpdf version 0.7a, but with the Makefile, divide-by-zero, and
+decryption patches added to it.
+
+For the reasons why xpdf, as distributed, is not capable of handling
+encrypted pdf files, please see:
+
+http://www.foolabs.com/xpdf/decryption.html
+
+What I have done is to add the decryption patches, and make all the sources
+available in a place off the US continent.
+
+This was born out of personal necessity... I had an encrypted pdf file that
+I *HAD* to read, so I went off to get the decryption patches for it from:
+
+http://people.a2000.nl/lsmiers/pdf/xpdf.html
+
+The patches were made available by Leo Smiers <L.Smiers@cable.A2000.nl> (in
+Europe, a place that also avoids problems with the senseless US exportation
+laws on encryption code).  Everything that was needed was in a file called
+"encryption.tar.gz", but I found that they didn't apply easily or cleanly,
+and it also involved "dropping in" some files into the xpdf source tree.
+
+Once I got it working, I simply retook the patch again so that all the
+source files and patches were all included in one file.
+
+Now all that is needed is "patch -p1 < patchfile" and everything is done.
+(Then you "configure" and "make" as usual).
+
+I then grabbed xpdf-0.7a-1.src.rpm from RedHat's "rawhide" ("5.2-beta")
+distribution and made some minor changes to it so that a decryption-capable
+version could be easily rebuilt from a .spec file.
+
+So that's what I did, and this is the result.  It seems to work just fine.
+
+I did some minor changes to RedHat's .spec file in the rpm so that it is
+installed into /usr/X11R6/bin rather than /usr/bin and also to put the
+ANNOUNCE file into the /usr/doc/xpdf-0.7a/ directory.
+
+I have contacted Derek (the author of xpdf) and got his blessing for what
+I've done.  I've also tried to contact Leo (from who's web site I got the
+decryption patches), but in almost two months he has not replied.
+
+I wrote this README file to document what I've done, then did some
+some teaking of the rpm spec file to get this added to the installation
+documation directory.
+
+From my understanding of the US laws on the export of encryption code,
+export of the sources for this patched version of the package is not
+permitted from the United States in electronic format.  (If you do this,
+you are considered to be an arms dealer!) As I understand it, the
+restrictions apply to taking such code *out* of the US in electronic
+format, not importing it nor taking it out in printed format.
+
+I don't live in the United States and we don't have silly laws like this
+here in Australia, so I can do what I like with these sources.
+
+I'm quite happy to put the resulting patch and the source and (glibc6)
+binary RPM files up on our web site and ftp server for general
+distribution.
+
+I will submitted the rpm packages to RedHat, leaving it up to them to do
+what they want (err... can) with it.  They might be able to use the binary
+version, but the probably won't be legally able to redistribute it (to
+places outside the US).
+
+Derek's web pages for xpdf are an excellent resource which I would strongly
+recommend a visit to it:
+
+http://www.foolabs.com/xpdf/
+
+ -=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-
+  Tony Nugent <Tony.Nugent@usq.edu.au>           <linux@usq.edu.au>
+  Computer Systems Officer                       Faculty of Science
+  University of Southern Queensland, Toowoomba Oueensland Australia
+ -=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-=*#*=-
