mirror of https://github.com/apache/poi.git
you can now protect files with writeProtectWorkbook
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@557333 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
a21c4c4594
commit
4f804edf40
|
@ -512,6 +512,9 @@ public class BiffViewer {
|
||||||
case NoteRecord.sid:
|
case NoteRecord.sid:
|
||||||
retval = new NoteRecord( in );
|
retval = new NoteRecord( in );
|
||||||
break;
|
break;
|
||||||
|
case FileSharingRecord.sid:
|
||||||
|
retval = new FileSharingRecord( in );
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
retval = new UnknownRecord( in );
|
retval = new UnknownRecord( in );
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,13 +44,13 @@ import java.util.Locale;
|
||||||
* before even attempting to use this.
|
* 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 Shawn Laubach (slaubach at apache dot org) (Data Formats)
|
||||||
* @author Andrew C. Oliver (acoliver at apache dot org)
|
* @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 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
|
* @see org.apache.poi.hssf.usermodel.HSSFWorkbook
|
||||||
* @version 1.0-pre
|
* @version 1.0-pre
|
||||||
*/
|
*/
|
||||||
|
@ -101,6 +101,9 @@ public class Workbook implements Model
|
||||||
private DrawingManager2 drawingManager;
|
private DrawingManager2 drawingManager;
|
||||||
private List escherBSERecords = new ArrayList(); // EscherBSERecord
|
private List escherBSERecords = new ArrayList(); // EscherBSERecord
|
||||||
private WindowOneRecord windowOne;
|
private WindowOneRecord windowOne;
|
||||||
|
private FileSharingRecord fileShare;
|
||||||
|
private WriteAccessRecord writeAccess;
|
||||||
|
private WriteProtectRecord writeProtect;
|
||||||
|
|
||||||
private static POILogger log = POILogFactory.getLogger(Workbook.class);
|
private static POILogger log = POILogFactory.getLogger(Workbook.class);
|
||||||
|
|
||||||
|
@ -220,7 +223,22 @@ public class Workbook implements Model
|
||||||
case WindowOneRecord.sid:
|
case WindowOneRecord.sid:
|
||||||
if (log.check( POILogger.DEBUG ))
|
if (log.check( POILogger.DEBUG ))
|
||||||
log.log(DEBUG, "found WindowOneRecord at " + k);
|
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 :
|
default :
|
||||||
}
|
}
|
||||||
records.add(rec);
|
records.add(rec);
|
||||||
|
@ -2235,5 +2253,70 @@ public class Workbook implements Model
|
||||||
return drawingManager;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,11 @@ public class WorkbookRecordList
|
||||||
return records.iterator();
|
return records.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void remove( Object record ) {
|
||||||
|
int i = records.indexOf(record);
|
||||||
|
this.remove(i);
|
||||||
|
}
|
||||||
|
|
||||||
public void remove( int pos )
|
public void remove( int pos )
|
||||||
{
|
{
|
||||||
records.remove(pos);
|
records.remove(pos);
|
||||||
|
|
|
@ -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<P>
|
||||||
|
* Description: stores the encrypted readonly for a workbook (write protect)
|
||||||
|
* REFERENCE: PG 314 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -74,7 +74,7 @@ public class RecordFactory
|
||||||
PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class,
|
PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class,
|
||||||
HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class,
|
HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class,
|
||||||
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.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);
|
private static Map recordsMap = recordsToMap(records);
|
||||||
|
|
|
@ -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()
|
private byte[] newUID()
|
||||||
{
|
{
|
||||||
byte[] bytes = new byte[16];
|
byte[] bytes = new byte[16];
|
||||||
|
|
Loading…
Reference in New Issue