From 222e406a6936bbf9001b9fc72cfc1f9b1a97d1c8 Mon Sep 17 00:00:00 2001 From: "Andrew C. Oliver" Date: Wed, 18 Jul 2007 17:00:14 +0000 Subject: [PATCH] you can now protect files with writeProtectWorkbook git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@557333 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/hssf/dev/BiffViewer.java | 3 + .../org/apache/poi/hssf/model/Workbook.java | 93 +++++++- .../poi/hssf/model/WorkbookRecordList.java | 5 + .../poi/hssf/record/FileSharingRecord.java | 209 ++++++++++++++++++ .../apache/poi/hssf/record/RecordFactory.java | 2 +- .../poi/hssf/usermodel/HSSFWorkbook.java | 16 ++ 6 files changed, 322 insertions(+), 6 deletions(-) create mode 100644 src/java/org/apache/poi/hssf/record/FileSharingRecord.java diff --git a/src/java/org/apache/poi/hssf/dev/BiffViewer.java b/src/java/org/apache/poi/hssf/dev/BiffViewer.java index 2d5f02beb..2e343012d 100644 --- a/src/java/org/apache/poi/hssf/dev/BiffViewer.java +++ b/src/java/org/apache/poi/hssf/dev/BiffViewer.java @@ -512,6 +512,9 @@ public class BiffViewer { case NoteRecord.sid: retval = new NoteRecord( in ); break; + case FileSharingRecord.sid: + retval = new FileSharingRecord( in ); + break; default: retval = new UnknownRecord( in ); } diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java index 5071735b6..28970820d 100644 --- a/src/java/org/apache/poi/hssf/model/Workbook.java +++ b/src/java/org/apache/poi/hssf/model/Workbook.java @@ -44,13 +44,13 @@ import java.util.Locale; * before even attempting to use this. * * + * @author Luc Girardin (luc dot girardin at macrofocus dot com) + * @author Sergei Kozello (sergeikozello at mail.ru) * @author Shawn Laubach (slaubach at apache dot org) (Data Formats) * @author Andrew C. Oliver (acoliver at apache dot org) - * @author Glen Stampoultzis (glens at apache.org) - * @author Sergei Kozello (sergeikozello at mail.ru) - * @author Luc Girardin (luc dot girardin at macrofocus dot com) - * @author Dan Sherman (dsherman at isisph.com) * @author Brian Sanders (bsanders at risklabs dot com) - custom palette + * @author Dan Sherman (dsherman at isisph.com) + * @author Glen Stampoultzis (glens at apache.org) * @see org.apache.poi.hssf.usermodel.HSSFWorkbook * @version 1.0-pre */ @@ -101,6 +101,9 @@ public class Workbook implements Model private DrawingManager2 drawingManager; private List escherBSERecords = new ArrayList(); // EscherBSERecord private WindowOneRecord windowOne; + private FileSharingRecord fileShare; + private WriteAccessRecord writeAccess; + private WriteProtectRecord writeProtect; private static POILogger log = POILogFactory.getLogger(Workbook.class); @@ -220,7 +223,22 @@ public class Workbook implements Model case WindowOneRecord.sid: if (log.check( POILogger.DEBUG )) log.log(DEBUG, "found WindowOneRecord at " + k); - retval.windowOne = (WindowOneRecord) rec; + retval.windowOne = (WindowOneRecord) rec; + break; + case WriteAccessRecord.sid: + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found WriteAccess at " + k); + retval.writeAccess = (WriteAccessRecord) rec; + break; + case WriteProtectRecord.sid: + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found WriteProtect at " + k); + retval.writeProtect = (WriteProtectRecord) rec; + break; + case FileSharingRecord.sid: + if (log.check( POILogger.DEBUG )) + log.log(DEBUG, "found FileSharing at " + k); + retval.fileShare = (FileSharingRecord) rec; default : } records.add(rec); @@ -2235,5 +2253,70 @@ public class Workbook implements Model return drawingManager; } + public WriteProtectRecord getWriteProtect() { + if (this.writeProtect == null) { + this.writeProtect = new WriteProtectRecord(); + int i = 0; + for (i = 0; + i < records.size() && !(records.get(i) instanceof BOFRecord); + i++) { + } + records.add(i+1,this.writeProtect); + } + return this.writeProtect; + } + + public WriteAccessRecord getWriteAccess() { + if (this.writeAccess == null) { + this.writeAccess = (WriteAccessRecord)createWriteAccess(); + int i = 0; + for (i = 0; + i < records.size() && !(records.get(i) instanceof InterfaceEndRecord); + i++) { + } + records.add(i+1,this.writeAccess); + } + return this.writeAccess; + } + + public FileSharingRecord getFileSharing() { + if (this.fileShare == null) { + this.fileShare = new FileSharingRecord(); + int i = 0; + for (i = 0; + i < records.size() && !(records.get(i) instanceof WriteAccessRecord); + i++) { + } + records.add(i+1,this.fileShare); + } + return this.fileShare; + } + + /** + * protect a workbook with a password (not encypted, just sets writeprotect + * flags and the password. + * @param password to set + */ + public void writeProtectWorkbook( String password, String username ) { + int protIdx = -1; + FileSharingRecord frec = getFileSharing(); + WriteAccessRecord waccess = getWriteAccess(); + WriteProtectRecord wprotect = getWriteProtect(); + frec.setReadOnly((short)1); + frec.setPassword(FileSharingRecord.hashPassword(password)); + frec.setUsername(username); + waccess.setUsername(username); + } + + /** + * removes the write protect flag + */ + public void unwriteProtectWorkbook() { + records.remove(fileShare); + records.remove(writeProtect); + fileShare = null; + writeProtect = null; + } + } diff --git a/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java b/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java index e9e0b8853..60d0081aa 100644 --- a/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java +++ b/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java @@ -79,6 +79,11 @@ public class WorkbookRecordList return records.iterator(); } + public void remove( Object record ) { + int i = records.indexOf(record); + this.remove(i); + } + public void remove( int pos ) { records.remove(pos); diff --git a/src/java/org/apache/poi/hssf/record/FileSharingRecord.java b/src/java/org/apache/poi/hssf/record/FileSharingRecord.java new file mode 100644 index 000000000..17f8cda64 --- /dev/null +++ b/src/java/org/apache/poi/hssf/record/FileSharingRecord.java @@ -0,0 +1,209 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + +package org.apache.poi.hssf.record; + +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.StringUtil; + +/** + * Title: FileSharing

+ * Description: stores the encrypted readonly for a workbook (write protect) + * REFERENCE: PG 314 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)

+ * @author Andrew C. Oliver (acoliver at apache dot org) + */ + +public class FileSharingRecord extends Record { + public final static short sid = 0x5b; + private short field_1_readonly; + private short field_2_password; + private byte field_3_username_length; + private short field_4_unknown; // not documented + private String field_5_username; + + public FileSharingRecord() {} + + + /** + * Constructs a FileSharing record and sets its fields appropriately. + * @param in the RecordInputstream to read the record from + */ + + public FileSharingRecord(RecordInputStream in) { + super(in); + } + + protected void validateSid(short id) { + if (id != sid) { + throw new RecordFormatException("NOT A FILESHARING RECORD"); + } + } + + protected void fillFields(RecordInputStream in) { + field_1_readonly = in.readShort(); + field_2_password = in.readShort(); + field_3_username_length = in.readByte(); + field_4_unknown = in.readShort(); + field_5_username = in.readCompressedUnicode(field_3_username_length); + } + + //this is the world's lamest "security". thanks to Wouter van Vugt for making me + //not have to try real hard. -ACO + public static short hashPassword(String password) { + byte[] passwordCharacters = password.getBytes(); + int hash = 0; + if (passwordCharacters.length > 0) { + int charIndex = passwordCharacters.length; + while (charIndex-- > 0) { + hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff); + hash ^= passwordCharacters[charIndex]; + } + // also hash with charcount + hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff); + hash ^= passwordCharacters.length; + hash ^= (0x8000 | ('N' << 8) | 'K'); + } + return (short)hash; + } + + /** + * set the readonly flag + * + * @param readonly 1 for true, not 1 for false + */ + public void setReadOnly(short readonly) { + field_1_readonly = readonly; + } + + /** + * get the readonly + * + * @return short representing if this is read only (1 = true) + */ + public short getReadOnly() { + return field_1_readonly; + } + + /** + * @param hashed password + */ + public void setPassword(short password) { + field_2_password = password; + } + + /** + * @returns password hashed with hashPassword() (very lame) + */ + public short getPassword() { + return field_2_password; + } + + /** + * @returns byte representing the length of the username field + */ + public byte getUsernameLength() { + return field_3_username_length ; + } + + /** + * @param byte representing the length of the username field + */ + public void setUsernameLength(byte length) { + this.field_3_username_length = length; + } + + /** + * @returns username of the user that created the file + */ + public String getUsername() { + return this.field_5_username; + } + + /** + * @param username of the user that created the file + */ + public void setUsername(String username) { + this.field_5_username = username; + this.field_3_username_length = (byte)username.length(); + } + + /** + * @return short value of a "bonus field" in Excel that was not doc'd + */ + public short getUnknown() { + return field_4_unknown; + } + + /** + * @param unknown field value to set (bonus field that is not doc'd) + */ + public void setUnknown(short unk) { + field_4_unknown = unk; + } + + public String toString() { + StringBuffer buffer = new StringBuffer(); + + buffer.append("[FILESHARING]\n"); + buffer.append(" .readonly = ") + .append(getReadOnly() == 1 ? "true" : "false").append("\n"); + buffer.append(" .password = ") + .append(Integer.toHexString(getPassword())).append("\n"); + buffer.append(" .userlen = ") + .append(Integer.toHexString(getUsernameLength())).append("\n"); + buffer.append(" .unknown = ") + .append(Integer.toHexString(getUnknown())).append("\n"); + buffer.append(" .username = ") + .append(getUsername()).append("\n"); + buffer.append("[/FILESHARING]\n"); + return buffer.toString(); + } + + public int serialize(int offset, byte [] data) { + LittleEndian.putShort(data, 0 + offset, sid); + LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize()-4)); + LittleEndian.putShort(data, 4 + offset, getReadOnly()); + LittleEndian.putShort(data, 6 + offset, getPassword()); + data[ 8 + offset ] = getUsernameLength(); + LittleEndian.putShort(data, 9 + offset, getUnknown()); + StringUtil.putCompressedUnicode( getUsername(), data, 11 + offset ); + return getRecordSize(); + } + + public int getRecordSize() { + return 11+getUsernameLength(); + } + + public short getSid() { + return sid; + } + + /** + * Clone this record. + */ + public Object clone() { + FileSharingRecord clone = new FileSharingRecord(); + clone.setReadOnly(field_1_readonly); + clone.setPassword(field_2_password); + clone.setUsernameLength(field_3_username_length); + clone.setUnknown(field_4_unknown); + clone.setUsername(field_5_username); + return clone; + } + +} diff --git a/src/java/org/apache/poi/hssf/record/RecordFactory.java b/src/java/org/apache/poi/hssf/record/RecordFactory.java index 48b0d3fd8..8f7821261 100644 --- a/src/java/org/apache/poi/hssf/record/RecordFactory.java +++ b/src/java/org/apache/poi/hssf/record/RecordFactory.java @@ -74,7 +74,7 @@ public class RecordFactory PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class, HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class, WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class, - NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class + NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class, FileSharingRecord.class }; } private static Map recordsMap = recordsToMap(records); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index f05179001..a50d890bb 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -1372,6 +1372,22 @@ public class HSSFWorkbook } } + /** + * protect a workbook with a password (not encypted, just sets writeprotect + * flags and the password. + * @param password to set + */ + public void writeProtectWorkbook( String password, String username ) { + this.workbook.writeProtectWorkbook(password, username); + } + + /** + * removes the write protect flag + */ + public void unwriteProtectWorkbook() { + this.workbook.unwriteProtectWorkbook(); + } + private byte[] newUID() { byte[] bytes = new byte[16];