mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Moving to root below branch label
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2005 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.smb.dcerpc.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.alfresco.filesys.smb.dcerpc.DCEBuffer;
|
||||
import org.alfresco.filesys.smb.server.SMBSrvException;
|
||||
import org.alfresco.filesys.smb.server.SMBSrvSession;
|
||||
|
||||
/**
|
||||
* DCE Request Handler Interface
|
||||
*/
|
||||
public interface DCEHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* Process a DCE/RPC request
|
||||
*
|
||||
* @param sess SMBSrvSession
|
||||
* @param inBuf DCEBuffer
|
||||
* @param pipeFile DCEPipeFile
|
||||
* @exception IOException
|
||||
* @exception SMBSrvException
|
||||
*/
|
||||
public void processRequest(SMBSrvSession sess, DCEBuffer inBuf, DCEPipeFile pipeFile) throws IOException,
|
||||
SMBSrvException;
|
||||
}
|
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.smb.dcerpc.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.alfresco.filesys.server.filesys.NetworkFile;
|
||||
import org.alfresco.filesys.smb.dcerpc.DCEBuffer;
|
||||
import org.alfresco.filesys.smb.dcerpc.DCEPipeType;
|
||||
|
||||
/**
|
||||
* DCE/RPC Pipe File Class
|
||||
* <p>
|
||||
* Contains the details and state of a DCE/RPC special named pipe.
|
||||
*/
|
||||
public class DCEPipeFile extends NetworkFile
|
||||
{
|
||||
|
||||
// Maximum receive/transmit DCE fragment size
|
||||
|
||||
private int m_maxRxFragSize;
|
||||
private int m_maxTxFragSize;
|
||||
|
||||
// Named pipe state flags
|
||||
|
||||
private int m_state;
|
||||
|
||||
// DCE/RPC handler for this named pipe
|
||||
|
||||
private DCEHandler m_handler;
|
||||
|
||||
// Current DCE buffered data
|
||||
|
||||
private DCEBuffer m_dceData;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @param id int
|
||||
*/
|
||||
public DCEPipeFile(int id)
|
||||
{
|
||||
super(id);
|
||||
setName(DCEPipeType.getTypeAsString(id));
|
||||
|
||||
// Set the DCE/RPC request handler for the pipe
|
||||
|
||||
setRequestHandler(DCEPipeHandler.getHandlerForType(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum receive fragment size
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getMaxReceiveFragmentSize()
|
||||
{
|
||||
return m_maxRxFragSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum transmit fragment size
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getMaxTransmitFragmentSize()
|
||||
{
|
||||
return m_maxTxFragSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the named pipe state
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getPipeState()
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pipe type id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getPipeId()
|
||||
{
|
||||
return getFileId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the pipe has a request handler
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean hasRequestHandler()
|
||||
{
|
||||
return m_handler != null ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pipes DCE/RPC handler
|
||||
*
|
||||
* @return DCEHandler
|
||||
*/
|
||||
public final DCEHandler getRequestHandler()
|
||||
{
|
||||
return m_handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the pipe has any buffered data
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean hasBufferedData()
|
||||
{
|
||||
return m_dceData != null ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the buffered data for the pipe
|
||||
*
|
||||
* @return DCEBuffer
|
||||
*/
|
||||
public final DCEBuffer getBufferedData()
|
||||
{
|
||||
return m_dceData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set buffered data for the pipe
|
||||
*
|
||||
* @param buf DCEBuffer
|
||||
*/
|
||||
public final void setBufferedData(DCEBuffer buf)
|
||||
{
|
||||
m_dceData = buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum receive fragment size
|
||||
*
|
||||
* @param siz int
|
||||
*/
|
||||
public final void setMaxReceiveFragmentSize(int siz)
|
||||
{
|
||||
m_maxRxFragSize = siz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum transmit fragment size
|
||||
*
|
||||
* @param siz int
|
||||
*/
|
||||
public final void setMaxTransmitFragmentSize(int siz)
|
||||
{
|
||||
m_maxTxFragSize = siz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the named pipe state flags
|
||||
*
|
||||
* @param state int
|
||||
*/
|
||||
public final void setPipeState(int state)
|
||||
{
|
||||
m_state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the pipes DCE/RPC handler
|
||||
*
|
||||
* @param handler DCEHandler
|
||||
*/
|
||||
public final void setRequestHandler(DCEHandler handler)
|
||||
{
|
||||
m_handler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the file details
|
||||
*/
|
||||
public final void DumpFile()
|
||||
{
|
||||
System.out.println("** DCE/RPC Named Pipe: " + getName());
|
||||
System.out.println(" File ID : " + getFileId());
|
||||
System.out.println(" State : 0x" + Integer.toHexString(getPipeState()));
|
||||
System.out.println(" Max Rx : " + getMaxReceiveFragmentSize());
|
||||
System.out.println(" Max Tx : " + getMaxTransmitFragmentSize());
|
||||
System.out.println(" Handler : " + getRequestHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NetworkFile#closeFile()
|
||||
*/
|
||||
public void closeFile() throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NetworkFile#openFile(boolean)
|
||||
*/
|
||||
public void openFile(boolean createFlag) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NetworkFile#readFile(byte[], int, int, long)
|
||||
*/
|
||||
public int readFile(byte[] buf, int len, int pos, long fileOff) throws IOException
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush any buffered output to the file
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void flushFile() throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NetworkFile#seekFile(long, int)
|
||||
*/
|
||||
public long seekFile(long pos, int typ) throws IOException
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NetworkFile#truncateFile(long)
|
||||
*/
|
||||
public void truncateFile(long siz) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @see NetworkFile#writeFile(byte[], int, int, long)
|
||||
*/
|
||||
public void writeFile(byte[] buf, int len, int pos, long fileOff) throws IOException
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.smb.dcerpc.server;
|
||||
|
||||
/**
|
||||
* DCE Pipe Handler Class
|
||||
* <p>
|
||||
* Contains a list of the available DCE pipe handlers.
|
||||
*/
|
||||
public class DCEPipeHandler
|
||||
{
|
||||
|
||||
// DCE/RPC pipe request handlers
|
||||
|
||||
private static DCEHandler[] _handlers = {
|
||||
new SrvsvcDCEHandler(),
|
||||
null, // samr
|
||||
null, // winreg
|
||||
new WkssvcDCEHandler(),
|
||||
null, // NETLOGON
|
||||
null, // lsarpc
|
||||
null, // spoolss
|
||||
null, // netdfs
|
||||
null, // service control
|
||||
null, // eventlog
|
||||
null // netlogon1
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the DCE/RPC request handler for the pipe type
|
||||
*
|
||||
* @param typ int
|
||||
* @return DCEHandler
|
||||
*/
|
||||
public final static DCEHandler getHandlerForType(int typ)
|
||||
{
|
||||
if (typ >= 0 && typ < _handlers.length)
|
||||
return _handlers[typ];
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.smb.dcerpc.server;
|
||||
|
||||
import org.alfresco.filesys.netbios.RFCNetBIOSProtocol;
|
||||
import org.alfresco.filesys.smb.PacketType;
|
||||
import org.alfresco.filesys.smb.dcerpc.DCECommand;
|
||||
import org.alfresco.filesys.smb.dcerpc.DCEDataPacker;
|
||||
import org.alfresco.filesys.smb.server.SMBTransPacket;
|
||||
import org.alfresco.filesys.util.DataPacker;
|
||||
|
||||
/**
|
||||
* DCE/RPC Server Packet Class
|
||||
*/
|
||||
public class DCESrvPacket extends SMBTransPacket
|
||||
{
|
||||
|
||||
// DCE/RPC header offsets
|
||||
|
||||
private static final int VERSIONMAJOR = 0;
|
||||
private static final int VERSIONMINOR = 1;
|
||||
private static final int PDUTYPE = 2;
|
||||
private static final int HEADERFLAGS = 3;
|
||||
private static final int PACKEDDATAREP = 4;
|
||||
private static final int FRAGMENTLEN = 8;
|
||||
private static final int AUTHLEN = 10;
|
||||
private static final int CALLID = 12;
|
||||
private static final int DCEDATA = 16;
|
||||
|
||||
// DCE/RPC Request offsets
|
||||
|
||||
private static final int ALLOCATIONHINT = 16;
|
||||
private static final int PRESENTIDENT = 20;
|
||||
private static final int OPERATIONID = 22;
|
||||
private static final int OPERATIONDATA = 24;
|
||||
|
||||
// Header flags
|
||||
|
||||
public static final int FLG_FIRSTFRAG = 0x01;
|
||||
public static final int FLG_LASTFRAG = 0x02;
|
||||
public static final int FLG_ONLYFRAG = 0x03;
|
||||
|
||||
// DCE/RPC header constants
|
||||
|
||||
private static final byte HDR_VERSIONMAJOR = 5;
|
||||
private static final byte HDR_VERSIONMINOR = 0;
|
||||
private static final int HDR_PACKEDDATAREP = 0x00000010;
|
||||
|
||||
// Offset to DCE/RPC header
|
||||
|
||||
private int m_offset;
|
||||
|
||||
/**
|
||||
* Construct a DCE/RPC transaction packet
|
||||
*
|
||||
* @param buf Buffer that contains the SMB transaction packet.
|
||||
*/
|
||||
public DCESrvPacket(byte[] buf)
|
||||
{
|
||||
super(buf);
|
||||
// m_offset = getParameterOffset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a DCE/RPC transaction packet
|
||||
*
|
||||
* @param siz Size of packet to allocate.
|
||||
*/
|
||||
public DCESrvPacket(int siz)
|
||||
{
|
||||
super(siz);
|
||||
|
||||
// Set the multiplex id for this transaction
|
||||
|
||||
setMultiplexId(getNextMultiplexId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the major version number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getMajorVersion()
|
||||
{
|
||||
return (int) (getBuffer()[m_offset + VERSIONMAJOR] & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minor version number
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getMinorVersion()
|
||||
{
|
||||
return (int) (getBuffer()[m_offset + VERSIONMINOR] & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the PDU packet type
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getPDUType()
|
||||
{
|
||||
return (int) (getBuffer()[m_offset + PDUTYPE] & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the header flags
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getHeaderFlags()
|
||||
{
|
||||
return (int) (getBuffer()[m_offset + HEADERFLAGS] & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the packed data representation
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getPackedDataRepresentation()
|
||||
{
|
||||
return DataPacker.getIntelInt(getBuffer(), m_offset + PACKEDDATAREP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fragment length
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getFragmentLength()
|
||||
{
|
||||
return DataPacker.getIntelShort(getBuffer(), m_offset + FRAGMENTLEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the fragment length
|
||||
*
|
||||
* @param len int
|
||||
*/
|
||||
public final void setFragmentLength(int len)
|
||||
{
|
||||
|
||||
// Set the DCE header fragment length
|
||||
|
||||
DataPacker.putIntelShort(len, getBuffer(), m_offset + FRAGMENTLEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the authentication length
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getAuthenticationLength()
|
||||
{
|
||||
return DataPacker.getIntelShort(getBuffer(), m_offset + AUTHLEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the call id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getCallId()
|
||||
{
|
||||
return DataPacker.getIntelInt(getBuffer(), m_offset + CALLID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this is the first fragment
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean isFirstFragment()
|
||||
{
|
||||
if ((getHeaderFlags() & FLG_FIRSTFRAG) != 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this is the last fragment
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean isLastFragment()
|
||||
{
|
||||
if ((getHeaderFlags() & FLG_LASTFRAG) != 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this is the only fragment in the request
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public final boolean isOnlyFragment()
|
||||
{
|
||||
if ((getHeaderFlags() & FLG_ONLYFRAG) == FLG_ONLYFRAG)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the offset to the DCE/RPC data within the SMB packet
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getDCEDataOffset()
|
||||
{
|
||||
|
||||
// Determine the data offset from the DCE/RPC packet type
|
||||
|
||||
int dataOff = -1;
|
||||
switch (getPDUType())
|
||||
{
|
||||
|
||||
// Bind/bind acknowledge
|
||||
|
||||
case DCECommand.BIND:
|
||||
case DCECommand.BINDACK:
|
||||
dataOff = m_offset + DCEDATA;
|
||||
break;
|
||||
|
||||
// Request/response
|
||||
|
||||
case DCECommand.REQUEST:
|
||||
case DCECommand.RESPONSE:
|
||||
dataOff = m_offset + OPERATIONDATA;
|
||||
break;
|
||||
}
|
||||
|
||||
// Return the data offset
|
||||
|
||||
return dataOff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request allocation hint
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getAllocationHint()
|
||||
{
|
||||
return DataPacker.getIntelInt(getBuffer(), m_offset + ALLOCATIONHINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the allocation hint
|
||||
*
|
||||
* @param alloc int
|
||||
*/
|
||||
public final void setAllocationHint(int alloc)
|
||||
{
|
||||
DataPacker.putIntelInt(alloc, getBuffer(), m_offset + ALLOCATIONHINT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request presentation identifier
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getPresentationIdentifier()
|
||||
{
|
||||
return DataPacker.getIntelShort(getBuffer(), m_offset + PRESENTIDENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the presentation identifier
|
||||
*
|
||||
* @param ident int
|
||||
*/
|
||||
public final void setPresentationIdentifier(int ident)
|
||||
{
|
||||
DataPacker.putIntelShort(ident, getBuffer(), m_offset + PRESENTIDENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request operation id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public final int getOperationId()
|
||||
{
|
||||
return DataPacker.getIntelShort(getBuffer(), m_offset + OPERATIONID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the DCE/RPC request. Set the SMB transaction parameter count so that the data
|
||||
* offset can be calculated.
|
||||
*
|
||||
* @param handle int
|
||||
* @param typ byte
|
||||
* @param flags int
|
||||
* @param callId int
|
||||
*/
|
||||
public final void initializeDCERequest(int handle, byte typ, int flags, int callId)
|
||||
{
|
||||
|
||||
// Initialize the transaction
|
||||
|
||||
InitializeTransact(16, null, 0, null, 0);
|
||||
|
||||
// Set the parameter byte count/offset for this packet
|
||||
|
||||
int bytPos = DCEDataPacker.longwordAlign(getByteOffset());
|
||||
|
||||
setParameter(3, 0);
|
||||
setParameter(4, bytPos - RFCNetBIOSProtocol.HEADER_LEN);
|
||||
|
||||
// Set the parameter displacement
|
||||
|
||||
setParameter(5, 0);
|
||||
|
||||
// Set the data byte count/offset for this packet
|
||||
|
||||
setParameter(6, 0);
|
||||
setParameter(7, bytPos - RFCNetBIOSProtocol.HEADER_LEN);
|
||||
|
||||
// Set the data displacement
|
||||
|
||||
setParameter(8, 0);
|
||||
|
||||
// Set up word count
|
||||
|
||||
setParameter(9, 0);
|
||||
|
||||
// Set the setup words
|
||||
|
||||
setSetupParameter(0, PacketType.TransactNmPipe);
|
||||
setSetupParameter(1, handle);
|
||||
|
||||
// Reset the DCE offset for a DCE reply
|
||||
|
||||
m_offset = bytPos;
|
||||
|
||||
// Build the DCE/RPC header
|
||||
|
||||
byte[] buf = getBuffer();
|
||||
DataPacker.putZeros(buf, m_offset, 24);
|
||||
|
||||
buf[m_offset + VERSIONMAJOR] = HDR_VERSIONMAJOR;
|
||||
buf[m_offset + VERSIONMINOR] = HDR_VERSIONMINOR;
|
||||
buf[m_offset + PDUTYPE] = typ;
|
||||
buf[m_offset + HEADERFLAGS] = (byte) (flags & 0xFF);
|
||||
DataPacker.putIntelInt(HDR_PACKEDDATAREP, buf, m_offset + PACKEDDATAREP);
|
||||
DataPacker.putIntelInt(0, buf, m_offset + AUTHLEN);
|
||||
DataPacker.putIntelInt(callId, buf, m_offset + CALLID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the DCE/RPC reply. Set the SMB transaction parameter count so that the data offset
|
||||
* can be calculated.
|
||||
*/
|
||||
public final void initializeDCEReply()
|
||||
{
|
||||
|
||||
// Set the total parameter words
|
||||
|
||||
setParameterCount(10);
|
||||
|
||||
// Set the total parameter/data bytes
|
||||
|
||||
setParameter(0, 0);
|
||||
setParameter(1, 0);
|
||||
|
||||
// Set the parameter byte count/offset for this packet
|
||||
|
||||
int bytPos = DCEDataPacker.longwordAlign(getByteOffset());
|
||||
|
||||
setParameter(3, 0);
|
||||
setParameter(4, bytPos - RFCNetBIOSProtocol.HEADER_LEN);
|
||||
|
||||
// Set the parameter displacement
|
||||
|
||||
setParameter(5, 0);
|
||||
|
||||
// Set the data byte count/offset for this packet
|
||||
|
||||
setParameter(6, 0);
|
||||
setParameter(7, bytPos - RFCNetBIOSProtocol.HEADER_LEN);
|
||||
|
||||
// Set the data displacement
|
||||
|
||||
setParameter(8, 0);
|
||||
|
||||
// Set up word count
|
||||
|
||||
setParameter(9, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the DCE/RPC header details
|
||||
*/
|
||||
public final void DumpHeader()
|
||||
{
|
||||
|
||||
// Dump the PDU type
|
||||
|
||||
System.out.println("** DCE/RPC Header - PDU Type = " + DCECommand.getCommandString(getPDUType()));
|
||||
System.out.println(" Version : " + getMajorVersion() + "." + getMinorVersion());
|
||||
System.out.println(" Flags : 0x" + getHeaderFlags());
|
||||
System.out.println(" Packed Data Rep : 0x" + getPackedDataRepresentation());
|
||||
System.out.println(" Fragment Length : " + getFragmentLength());
|
||||
System.out.println(" Auth Length : " + getAuthenticationLength());
|
||||
System.out.println(" Call ID : " + getCallId());
|
||||
}
|
||||
}
|
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.smb.dcerpc.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Vector;
|
||||
|
||||
import org.alfresco.filesys.server.auth.acl.AccessControlManager;
|
||||
import org.alfresco.filesys.server.config.ServerConfiguration;
|
||||
import org.alfresco.filesys.server.core.ShareType;
|
||||
import org.alfresco.filesys.server.core.SharedDevice;
|
||||
import org.alfresco.filesys.server.core.SharedDeviceList;
|
||||
import org.alfresco.filesys.smb.Dialect;
|
||||
import org.alfresco.filesys.smb.SMBStatus;
|
||||
import org.alfresco.filesys.smb.dcerpc.DCEBuffer;
|
||||
import org.alfresco.filesys.smb.dcerpc.DCEBufferException;
|
||||
import org.alfresco.filesys.smb.dcerpc.Srvsvc;
|
||||
import org.alfresco.filesys.smb.dcerpc.info.ServerInfo;
|
||||
import org.alfresco.filesys.smb.dcerpc.info.ShareInfo;
|
||||
import org.alfresco.filesys.smb.dcerpc.info.ShareInfoList;
|
||||
import org.alfresco.filesys.smb.server.SMBServer;
|
||||
import org.alfresco.filesys.smb.server.SMBSrvException;
|
||||
import org.alfresco.filesys.smb.server.SMBSrvSession;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Srvsvc DCE/RPC Handler Class
|
||||
*/
|
||||
public class SrvsvcDCEHandler implements DCEHandler
|
||||
{
|
||||
|
||||
// Debug logging
|
||||
|
||||
private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol");
|
||||
|
||||
/**
|
||||
* Process a SrvSvc DCE/RPC request
|
||||
*
|
||||
* @param sess SMBSrvSession
|
||||
* @param inBuf DCEBuffer
|
||||
* @param pipeFile DCEPipeFile
|
||||
* @exception IOException
|
||||
* @exception SMBSrvException
|
||||
*/
|
||||
public void processRequest(SMBSrvSession sess, DCEBuffer inBuf, DCEPipeFile pipeFile) throws IOException,
|
||||
SMBSrvException
|
||||
{
|
||||
|
||||
// Get the operation code and move the buffer pointer to the start of the request data
|
||||
|
||||
int opNum = inBuf.getHeaderValue(DCEBuffer.HDR_OPCODE);
|
||||
try
|
||||
{
|
||||
inBuf.skipBytes(DCEBuffer.OPERATIONDATA);
|
||||
}
|
||||
catch (DCEBufferException ex)
|
||||
{
|
||||
}
|
||||
|
||||
// Debug
|
||||
|
||||
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
|
||||
logger.debug("DCE/RPC SrvSvc request=" + Srvsvc.getOpcodeName(opNum));
|
||||
|
||||
// Create the output DCE buffer and add the response header
|
||||
|
||||
DCEBuffer outBuf = new DCEBuffer();
|
||||
outBuf.putResponseHeader(inBuf.getHeaderValue(DCEBuffer.HDR_CALLID), 0);
|
||||
|
||||
// Process the request
|
||||
|
||||
boolean processed = false;
|
||||
|
||||
switch (opNum)
|
||||
{
|
||||
|
||||
// Enumerate shares
|
||||
|
||||
case Srvsvc.NetrShareEnum:
|
||||
processed = netShareEnum(sess, inBuf, outBuf);
|
||||
break;
|
||||
|
||||
// Enumerate all shares
|
||||
|
||||
case Srvsvc.NetrShareEnumSticky:
|
||||
processed = netShareEnum(sess, inBuf, outBuf);
|
||||
break;
|
||||
|
||||
// Get share information
|
||||
|
||||
case Srvsvc.NetrShareGetInfo:
|
||||
processed = netShareGetInfo(sess, inBuf, outBuf);
|
||||
break;
|
||||
|
||||
// Get server information
|
||||
|
||||
case Srvsvc.NetrServerGetInfo:
|
||||
processed = netServerGetInfo(sess, inBuf, outBuf);
|
||||
break;
|
||||
|
||||
// Unsupported function
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Return an error status if the request was not processed
|
||||
|
||||
if (processed == false)
|
||||
{
|
||||
sess.sendErrorResponseSMB(SMBStatus.SRVNotSupported, SMBStatus.ErrSrv);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the allocation hint for the response
|
||||
|
||||
outBuf.setHeaderValue(DCEBuffer.HDR_ALLOCHINT, outBuf.getLength());
|
||||
|
||||
// Attach the output buffer to the pipe file
|
||||
|
||||
pipeFile.setBufferedData(outBuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a share enumeration request
|
||||
*
|
||||
* @param sess SMBSrvSession
|
||||
* @param inBuf DCEPacket
|
||||
* @param outBuf DCEPacket
|
||||
* @return boolean
|
||||
*/
|
||||
protected final boolean netShareEnum(SMBSrvSession sess, DCEBuffer inBuf, DCEBuffer outBuf)
|
||||
{
|
||||
|
||||
// Decode the request
|
||||
|
||||
String srvName = null;
|
||||
ShareInfoList shrInfo = null;
|
||||
|
||||
try
|
||||
{
|
||||
inBuf.skipPointer();
|
||||
srvName = inBuf.getString(DCEBuffer.ALIGN_INT);
|
||||
shrInfo = new ShareInfoList(inBuf);
|
||||
}
|
||||
catch (DCEBufferException ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Debug
|
||||
|
||||
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
|
||||
logger.debug("NetShareEnum srvName=" + srvName + ", shrInfo=" + shrInfo.toString());
|
||||
|
||||
// Get the share list from the server
|
||||
|
||||
SharedDeviceList shareList = sess.getServer().getShareMapper().getShareList(srvName, sess, false);
|
||||
|
||||
// Check if there is an access control manager configured
|
||||
|
||||
if (sess.getServer().hasAccessControlManager())
|
||||
{
|
||||
|
||||
// Filter the list of available shares by applying any access control rules
|
||||
|
||||
AccessControlManager aclMgr = sess.getServer().getAccessControlManager();
|
||||
|
||||
shareList = aclMgr.filterShareList(sess, shareList);
|
||||
}
|
||||
|
||||
// Create a list of share information objects of the required information level
|
||||
|
||||
Vector infoList = new Vector();
|
||||
Enumeration<SharedDevice> enm = shareList.enumerateShares();
|
||||
|
||||
while (enm.hasMoreElements())
|
||||
{
|
||||
|
||||
// Get the current shared device details
|
||||
|
||||
SharedDevice share = enm.nextElement();
|
||||
|
||||
// Determine the share type
|
||||
|
||||
int shrTyp = ShareInfo.Disk;
|
||||
|
||||
if (share.getType() == ShareType.PRINTER)
|
||||
shrTyp = ShareInfo.PrintQueue;
|
||||
else if (share.getType() == ShareType.NAMEDPIPE)
|
||||
shrTyp = ShareInfo.IPC;
|
||||
else if (share.getType() == ShareType.ADMINPIPE)
|
||||
shrTyp = ShareInfo.IPC + ShareInfo.Hidden;
|
||||
|
||||
// Create a share information object with the basic information
|
||||
|
||||
ShareInfo info = new ShareInfo(shrInfo.getInformationLevel(), share.getName(), shrTyp, share.getComment());
|
||||
infoList.add(info);
|
||||
|
||||
// Add additional information
|
||||
|
||||
switch (shrInfo.getInformationLevel())
|
||||
{
|
||||
|
||||
// Level 2
|
||||
|
||||
case 2:
|
||||
if (share.getContext() != null)
|
||||
info.setPath(share.getContext().getDeviceName());
|
||||
break;
|
||||
|
||||
// Level 502
|
||||
|
||||
case 502:
|
||||
if (share.getContext() != null)
|
||||
info.setPath(share.getContext().getDeviceName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the share information list in the server share information and write the
|
||||
// share information to the output DCE buffer.
|
||||
|
||||
shrInfo.setShareList(infoList);
|
||||
try
|
||||
{
|
||||
shrInfo.writeList(outBuf);
|
||||
outBuf.putInt(0); // status code
|
||||
}
|
||||
catch (DCEBufferException ex)
|
||||
{
|
||||
}
|
||||
|
||||
// Indicate that the request was processed successfully
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a get share information request
|
||||
*
|
||||
* @param sess SMBSrvSession
|
||||
* @param inBuf DCEPacket
|
||||
* @param outBuf DCEPacket
|
||||
* @return boolean
|
||||
*/
|
||||
protected final boolean netShareGetInfo(SMBSrvSession sess, DCEBuffer inBuf, DCEBuffer outBuf)
|
||||
{
|
||||
|
||||
// Decode the request
|
||||
|
||||
String srvName = null;
|
||||
String shrName = null;
|
||||
int infoLevel = 0;
|
||||
|
||||
try
|
||||
{
|
||||
inBuf.skipPointer();
|
||||
srvName = inBuf.getString(DCEBuffer.ALIGN_INT);
|
||||
shrName = inBuf.getString(DCEBuffer.ALIGN_INT);
|
||||
infoLevel = inBuf.getInt();
|
||||
}
|
||||
catch (DCEBufferException ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Debug
|
||||
|
||||
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
|
||||
logger.debug("netShareGetInfo srvname=" + srvName + ", share=" + shrName + ", infoLevel=" + infoLevel);
|
||||
|
||||
// Find the required shared device
|
||||
|
||||
SharedDevice share = null;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
// Get the shared device details
|
||||
|
||||
share = sess.getServer().findShare(srvName, shrName, ShareType.UNKNOWN, sess, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
}
|
||||
|
||||
// Check if the share details are valid
|
||||
|
||||
if (share == null)
|
||||
return false;
|
||||
|
||||
// Determine the share type
|
||||
|
||||
int shrTyp = ShareInfo.Disk;
|
||||
|
||||
if (share.getType() == ShareType.PRINTER)
|
||||
shrTyp = ShareInfo.PrintQueue;
|
||||
else if (share.getType() == ShareType.NAMEDPIPE)
|
||||
shrTyp = ShareInfo.IPC;
|
||||
else if (share.getType() == ShareType.ADMINPIPE)
|
||||
shrTyp = ShareInfo.IPC + ShareInfo.Hidden;
|
||||
|
||||
// Create the share information
|
||||
|
||||
ShareInfo shrInfo = new ShareInfo(infoLevel, share.getName(), shrTyp, share.getComment());
|
||||
|
||||
// Pack the information level, structure pointer and share information
|
||||
|
||||
outBuf.putInt(infoLevel);
|
||||
outBuf.putPointer(true);
|
||||
|
||||
shrInfo.writeObject(outBuf, outBuf);
|
||||
|
||||
// Add the status and return a success status
|
||||
|
||||
outBuf.putInt(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a get server information request
|
||||
*
|
||||
* @param sess SMBSrvSession
|
||||
* @param inBuf DCEPacket
|
||||
* @param outBuf DCEPacket
|
||||
* @return boolean
|
||||
*/
|
||||
protected final boolean netServerGetInfo(SMBSrvSession sess, DCEBuffer inBuf, DCEBuffer outBuf)
|
||||
{
|
||||
|
||||
// Decode the request
|
||||
|
||||
String srvName = null;
|
||||
int infoLevel = 0;
|
||||
|
||||
try
|
||||
{
|
||||
inBuf.skipPointer();
|
||||
srvName = inBuf.getString(DCEBuffer.ALIGN_INT);
|
||||
infoLevel = inBuf.getInt();
|
||||
}
|
||||
catch (DCEBufferException ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Debug
|
||||
|
||||
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
|
||||
logger.debug("netServerGetInfo srvname=" + srvName + ", infoLevel=" + infoLevel);
|
||||
|
||||
// Create the server information and set the common values
|
||||
|
||||
ServerInfo srvInfo = new ServerInfo(infoLevel);
|
||||
|
||||
SMBServer srv = sess.getSMBServer();
|
||||
srvInfo.setServerName(srv.getServerName());
|
||||
srvInfo.setComment(srv.getComment());
|
||||
srvInfo.setServerType(srv.getServerType());
|
||||
|
||||
// Determine if the server is using the NT SMB dialect and set the platofmr id accordingly
|
||||
|
||||
ServerConfiguration srvConfig = srv.getConfiguration();
|
||||
if (srvConfig != null && srvConfig.getEnabledDialects().hasDialect(Dialect.NT) == true)
|
||||
{
|
||||
srvInfo.setPlatformId(ServerInfo.PLATFORM_NT);
|
||||
srvInfo.setVersion(5, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
srvInfo.setPlatformId(ServerInfo.PLATFORM_OS2);
|
||||
srvInfo.setVersion(4, 0);
|
||||
}
|
||||
|
||||
// Write the server information to the DCE response
|
||||
|
||||
srvInfo.writeObject(outBuf, outBuf);
|
||||
outBuf.putInt(0);
|
||||
|
||||
// Indicate that the request was processed successfully
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* 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.alfresco.filesys.smb.dcerpc.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.alfresco.filesys.server.config.ServerConfiguration;
|
||||
import org.alfresco.filesys.smb.Dialect;
|
||||
import org.alfresco.filesys.smb.SMBStatus;
|
||||
import org.alfresco.filesys.smb.dcerpc.DCEBuffer;
|
||||
import org.alfresco.filesys.smb.dcerpc.DCEBufferException;
|
||||
import org.alfresco.filesys.smb.dcerpc.Wkssvc;
|
||||
import org.alfresco.filesys.smb.dcerpc.info.ServerInfo;
|
||||
import org.alfresco.filesys.smb.dcerpc.info.WorkstationInfo;
|
||||
import org.alfresco.filesys.smb.server.SMBServer;
|
||||
import org.alfresco.filesys.smb.server.SMBSrvException;
|
||||
import org.alfresco.filesys.smb.server.SMBSrvSession;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Wkssvc DCE/RPC Handler Class
|
||||
*/
|
||||
public class WkssvcDCEHandler implements DCEHandler
|
||||
{
|
||||
|
||||
// Debug logging
|
||||
|
||||
private static final Log logger = LogFactory.getLog("org.alfresco.smb.protocol");
|
||||
|
||||
/**
|
||||
* Process a WksSvc DCE/RPC request
|
||||
*
|
||||
* @param sess SMBSrvSession
|
||||
* @param inBuf DCEBuffer
|
||||
* @param pipeFile DCEPipeFile
|
||||
* @exception IOException
|
||||
* @exception SMBSrvException
|
||||
*/
|
||||
public void processRequest(SMBSrvSession sess, DCEBuffer inBuf, DCEPipeFile pipeFile) throws IOException,
|
||||
SMBSrvException
|
||||
{
|
||||
|
||||
// Get the operation code and move the buffer pointer to the start of the request data
|
||||
|
||||
int opNum = inBuf.getHeaderValue(DCEBuffer.HDR_OPCODE);
|
||||
try
|
||||
{
|
||||
inBuf.skipBytes(DCEBuffer.OPERATIONDATA);
|
||||
}
|
||||
catch (DCEBufferException ex)
|
||||
{
|
||||
}
|
||||
|
||||
// Debug
|
||||
|
||||
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
|
||||
logger.debug("DCE/RPC WksSvc request=" + Wkssvc.getOpcodeName(opNum));
|
||||
|
||||
// Create the output DCE buffer and add the response header
|
||||
|
||||
DCEBuffer outBuf = new DCEBuffer();
|
||||
outBuf.putResponseHeader(inBuf.getHeaderValue(DCEBuffer.HDR_CALLID), 0);
|
||||
|
||||
// Process the request
|
||||
|
||||
boolean processed = false;
|
||||
|
||||
switch (opNum)
|
||||
{
|
||||
|
||||
// Get workstation information
|
||||
|
||||
case Wkssvc.NetWkstaGetInfo:
|
||||
processed = netWkstaGetInfo(sess, inBuf, outBuf);
|
||||
break;
|
||||
|
||||
// Unsupported function
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Return an error status if the request was not processed
|
||||
|
||||
if (processed == false)
|
||||
{
|
||||
sess.sendErrorResponseSMB(SMBStatus.SRVNotSupported, SMBStatus.ErrSrv);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the allocation hint for the response
|
||||
|
||||
outBuf.setHeaderValue(DCEBuffer.HDR_ALLOCHINT, outBuf.getLength());
|
||||
|
||||
// Attach the output buffer to the pipe file
|
||||
|
||||
pipeFile.setBufferedData(outBuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get workstation infomation
|
||||
*
|
||||
* @param sess SMBSrvSession
|
||||
* @param inBuf DCEPacket
|
||||
* @param outBuf DCEPacket
|
||||
* @return boolean
|
||||
*/
|
||||
protected final boolean netWkstaGetInfo(SMBSrvSession sess, DCEBuffer inBuf, DCEBuffer outBuf)
|
||||
{
|
||||
|
||||
// Decode the request
|
||||
|
||||
String srvName = null;
|
||||
int infoLevel = 0;
|
||||
|
||||
try
|
||||
{
|
||||
inBuf.skipPointer();
|
||||
srvName = inBuf.getString(DCEBuffer.ALIGN_INT);
|
||||
infoLevel = inBuf.getInt();
|
||||
}
|
||||
catch (DCEBufferException ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Debug
|
||||
|
||||
if (logger.isDebugEnabled() && sess.hasDebug(SMBSrvSession.DBG_DCERPC))
|
||||
logger.debug("NetWkstaGetInfo srvName=" + srvName + ", infoLevel=" + infoLevel);
|
||||
|
||||
// Create the workstation information and set the common values
|
||||
|
||||
WorkstationInfo wkstaInfo = new WorkstationInfo(infoLevel);
|
||||
|
||||
SMBServer srv = sess.getSMBServer();
|
||||
wkstaInfo.setWorkstationName(srv.getServerName());
|
||||
wkstaInfo.setDomain(srv.getConfiguration().getDomainName());
|
||||
|
||||
// Determine if the server is using the NT SMB dialect and set the platofmr id accordingly
|
||||
|
||||
ServerConfiguration srvConfig = sess.getServer().getConfiguration();
|
||||
if (srvConfig != null && srvConfig.getEnabledDialects().hasDialect(Dialect.NT) == true)
|
||||
{
|
||||
wkstaInfo.setPlatformId(ServerInfo.PLATFORM_NT);
|
||||
wkstaInfo.setVersion(5, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
wkstaInfo.setPlatformId(ServerInfo.PLATFORM_OS2);
|
||||
wkstaInfo.setVersion(4, 0);
|
||||
}
|
||||
|
||||
// Write the server information to the DCE response
|
||||
|
||||
wkstaInfo.writeObject(outBuf, outBuf);
|
||||
outBuf.putInt(0);
|
||||
|
||||
// Indicate that the request was processed successfully
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user