删除无用代码,加入RPC调用

This commit is contained in:
2025-03-21 17:00:13 +08:00
parent 903472de6c
commit ded0148ba6
51 changed files with 483 additions and 57881 deletions

View File

@@ -1,771 +0,0 @@
package cliprdr
import (
"bytes"
"encoding/hex"
"os"
"strings"
"unicode/utf16"
"github.com/lunixbochs/struc"
"ShotRDP/grdp/core"
"ShotRDP/grdp/glog"
"ShotRDP/grdp/plugin"
)
/**
* Initialization Sequence\n
* Client Server\n
* | |\n
* |<----------------------Server Clipboard Capabilities PDU-----------------|\n
* |<-----------------------------Monitor Ready PDU--------------------------|\n
* |-----------------------Client Clipboard Capabilities PDU---------------->|\n
* |---------------------------Temporary Directory PDU---------------------->|\n
* |-------------------------------Format List PDU-------------------------->|\n
* |<--------------------------Format List Response PDU----------------------|\n
*
*/
/**
* Data Transfer Sequences\n
* Shared Local\n
* Clipboard Owner Clipboard Owner\n
* | |\n
* |-------------------------------------------------------------------------|\n _
* |-------------------------------Format List PDU-------------------------->|\n |
* |<--------------------------Format List Response PDU----------------------|\n _| Copy
* Sequence
* |<---------------------Lock Clipboard Data PDU (Optional)-----------------|\n
* |-------------------------------------------------------------------------|\n
* |-------------------------------------------------------------------------|\n _
* |<--------------------------Format Data Request PDU-----------------------|\n | Paste
* Sequence Palette,
* |---------------------------Format Data Response PDU--------------------->|\n _| Metafile,
* File List Data
* |-------------------------------------------------------------------------|\n
* |-------------------------------------------------------------------------|\n _
* |<------------------------Format Contents Request PDU---------------------|\n | Paste
* Sequence
* |-------------------------Format Contents Response PDU------------------->|\n _| File
* Stream Data
* |<---------------------Lock Clipboard Data PDU (Optional)-----------------|\n
* |-------------------------------------------------------------------------|\n
*
*/
const (
ChannelName = plugin.CLIPRDR_SVC_CHANNEL_NAME
ChannelOption = plugin.CHANNEL_OPTION_INITIALIZED | plugin.CHANNEL_OPTION_ENCRYPT_RDP |
plugin.CHANNEL_OPTION_COMPRESS_RDP | plugin.CHANNEL_OPTION_SHOW_PROTOCOL
)
type MsgType uint16
const (
CB_MONITOR_READY = 0x0001
CB_FORMAT_LIST = 0x0002
CB_FORMAT_LIST_RESPONSE = 0x0003
CB_FORMAT_DATA_REQUEST = 0x0004
CB_FORMAT_DATA_RESPONSE = 0x0005
CB_TEMP_DIRECTORY = 0x0006
CB_CLIP_CAPS = 0x0007
CB_FILECONTENTS_REQUEST = 0x0008
CB_FILECONTENTS_RESPONSE = 0x0009
CB_LOCK_CLIPDATA = 0x000A
CB_UNLOCK_CLIPDATA = 0x000B
)
type MsgFlags uint16
const (
CB_RESPONSE_OK = 0x0001
CB_RESPONSE_FAIL = 0x0002
CB_ASCII_NAMES = 0x0004
)
type DwFlags uint32
const (
FILECONTENTS_SIZE = 0x00000001
FILECONTENTS_RANGE = 0x00000002
)
type CliprdrPDUHeader struct {
MsgType uint16 `struc:"little"`
MsgFlags uint16 `struc:"little"`
DataLen uint32 `struc:"little"`
}
func NewCliprdrPDUHeader(mType, flags uint16, ln uint32) *CliprdrPDUHeader {
return &CliprdrPDUHeader{
MsgType: mType,
MsgFlags: flags,
DataLen: ln,
}
}
func (h *CliprdrPDUHeader) serialize() []byte {
b := &bytes.Buffer{}
core.WriteUInt16LE(h.MsgType, b)
core.WriteUInt16LE(h.MsgFlags, b)
core.WriteUInt32LE(h.DataLen, b)
return b.Bytes()
}
type CliprdrGeneralCapabilitySet struct {
CapabilitySetType uint16 `struc:"little"`
CapabilitySetLength uint16 `struc:"little"`
Version uint32 `struc:"little"`
GeneralFlags uint32 `struc:"little"`
}
const (
CB_CAPSTYPE_GENERAL = 0x0001
)
type CliprdrCapabilitySets struct {
CapabilitySetType uint16 `struc:"little"`
LengthCapability uint16 `struc:"little"`
Version uint32 `struc:"little"`
GeneralFlags uint32 `struc:"little"`
//CapabilityData []byte `struc:"little"`
}
type CliprdrCapabilitiesPDU struct {
CCapabilitiesSets uint16 `struc:"little,sizeof=CapabilitySets"`
Pad1 uint16 `struc:"little"`
CapabilitySets []CliprdrGeneralCapabilitySet `struc:"little"`
}
type CliprdrMonitorReady struct {
}
type GeneralFlags uint32
const (
/* CLIPRDR_GENERAL_CAPABILITY.generalFlags */
CB_USE_LONG_FORMAT_NAMES = 0x00000002
CB_STREAM_FILECLIP_ENABLED = 0x00000004
CB_FILECLIP_NO_FILE_PATHS = 0x00000008
CB_CAN_LOCK_CLIPDATA = 0x00000010
CB_HUGE_FILE_SUPPORT_ENABLED = 0x00000020
)
const (
/* CLIPRDR_GENERAL_CAPABILITY.version */
CB_CAPS_VERSION_1 = 0x00000001
CB_CAPS_VERSION_2 = 0x00000002
)
const (
CB_CAPSTYPE_GENERAL_LEN = 12
)
const (
FD_CLSID = 0x00000001
FD_SIZEPOINT = 0x00000002
FD_ATTRIBUTES = 0x00000004
FD_CREATETIME = 0x00000008
FD_ACCESSTIME = 0x00000010
FD_WRITESTIME = 0x00000020
FD_FILESIZE = 0x00000040
FD_PROGRESSUI = 0x00004000
FD_LINKUI = 0x00008000
)
type FileGroupDescriptor struct {
CItems uint32 `struc:"little"`
Fgd []FileDescriptor `struc:"sizefrom=CItems"`
}
type FileDescriptor struct {
Flags uint32 `struc:"little"`
Clsid [16]byte `struc:"little"`
Sizel [8]byte `struc:"little"`
Pointl [8]byte `struc:"little"`
FileAttributes uint32 `struc:"little"`
CreationTime [8]byte `struc:"little"`
LastAccessTime [8]byte `struc:"little"`
LastWriteTime []byte `struc:"[8]byte"` //8
FileSizeHigh uint32 `struc:"little"`
FileSizeLow uint32 `struc:"little"`
FileName []byte `struc:"[512]byte"`
}
func (f *FileGroupDescriptor) Unpack(b []byte) error {
r := bytes.NewReader(b)
err := struc.Unpack(r, f)
if err != nil {
glog.Error(err)
}
return err
}
func (f *FileDescriptor) serialize() []byte {
b := &bytes.Buffer{}
core.WriteUInt32LE(f.Flags, b)
for i := 0; i < 32; i++ {
core.WriteByte(0, b)
}
core.WriteUInt32LE(f.FileAttributes, b)
for i := 0; i < 16; i++ {
core.WriteByte(0, b)
}
core.WriteBytes(f.LastWriteTime[:], b)
core.WriteUInt32LE(f.FileSizeHigh, b)
core.WriteUInt32LE(f.FileSizeLow, b)
name := make([]byte, 512)
copy(name, f.FileName)
core.WriteBytes(name, b)
return b.Bytes()
}
func (f *FileDescriptor) isDir() bool {
if f.Flags&FD_ATTRIBUTES != 0 {
return f.FileAttributes&FILE_ATTRIBUTE_DIRECTORY != 0
}
return false
}
func (f *FileDescriptor) hasFileSize() bool {
return f.Flags&FD_FILESIZE != 0
}
// temp dir
type CliprdrTempDirectory struct {
SzTempDir []byte `struc:"[260]byte"`
}
// format list
type CliprdrFormat struct {
FormatId uint32
FormatName string
}
type CliprdrFormatList struct {
NumFormats uint32
Formats []CliprdrFormat
}
type ClipboardFormats uint16
const (
CB_FORMAT_HTML = 0xD010
CB_FORMAT_PNG = 0xD011
CB_FORMAT_JPEG = 0xD012
CB_FORMAT_GIF = 0xD013
CB_FORMAT_TEXTURILIST = 0xD014
CB_FORMAT_GNOMECOPIEDFILES = 0xD015
CB_FORMAT_MATECOPIEDFILES = 0xD016
)
// lock or unlock
type CliprdrCtrlClipboardData struct {
ClipDataId uint32
}
// format data
type CliprdrFormatDataRequest struct {
RequestedFormatId uint32
}
type CliprdrFormatDataResponse struct {
RequestedFormatData []byte
}
// file contents
type CliprdrFileContentsRequest struct {
StreamId uint32 `struc:"little"`
Lindex uint32 `struc:"little"`
DwFlags uint32 `struc:"little"`
NPositionLow uint32 `struc:"little"`
NPositionHigh uint32 `struc:"little"`
CbRequested uint32 `struc:"little"`
ClipDataId uint32 `struc:"little"`
}
func FileContentsSizeRequest(i uint32) *CliprdrFileContentsRequest {
return &CliprdrFileContentsRequest{
StreamId: 1,
Lindex: i,
DwFlags: FILECONTENTS_SIZE,
NPositionLow: 0,
NPositionHigh: 0,
CbRequested: 65535,
ClipDataId: 0,
}
}
type CliprdrFileContentsResponse struct {
StreamId uint32
CbRequested uint32
RequestedData []byte
}
func (resp *CliprdrFileContentsResponse) Unpack(b []byte) {
r := bytes.NewReader(b)
resp.StreamId, _ = core.ReadUInt32LE(r)
resp.CbRequested = uint32(r.Len())
resp.RequestedData, _ = core.ReadBytes(int(resp.CbRequested), r)
}
type CliprdrClient struct {
w core.ChannelSender
useLongFormatNames bool
streamFileClipEnabled bool
fileClipNoFilePaths bool
canLockClipData bool
hasHugeFileSupport bool
formatIdMap map[uint32]uint32
Files []FileDescriptor
reply chan []byte
Control
}
func NewCliprdrClient() *CliprdrClient {
c := &CliprdrClient{
formatIdMap: make(map[uint32]uint32, 20),
Files: make([]FileDescriptor, 0, 20),
reply: make(chan []byte, 100),
}
go ClipWatcher(c)
return c
}
func (c *CliprdrClient) Send(s []byte) (int, error) {
glog.Debug("len:", len(s), "data:", hex.EncodeToString(s))
name, _ := c.GetType()
return c.w.SendToChannel(name, s)
}
func (c *CliprdrClient) Sender(f core.ChannelSender) {
c.w = f
}
func (c *CliprdrClient) GetType() (string, uint32) {
return ChannelName, ChannelOption
}
func (c *CliprdrClient) Process(s []byte) {
glog.Debug("recv:", hex.EncodeToString(s))
r := bytes.NewReader(s)
msgType, _ := core.ReadUint16LE(r)
flag, _ := core.ReadUint16LE(r)
length, _ := core.ReadUInt32LE(r)
glog.Debugf("cliprdr: type=0x%x flag=%d length=%d, all=%d", msgType, flag, length, r.Len())
b, _ := core.ReadBytes(int(length), r)
switch msgType {
case CB_CLIP_CAPS:
glog.Info("CB_CLIP_CAPS")
c.processClipCaps(b)
case CB_MONITOR_READY:
glog.Info("CB_MONITOR_READY")
c.processMonitorReady(b)
case CB_FORMAT_LIST:
glog.Info("CB_FORMAT_LIST")
c.processFormatList(b)
case CB_FORMAT_LIST_RESPONSE:
glog.Info("CB_FORMAT_LIST_RESPONSE")
c.processFormatListResponse(flag, b)
case CB_FORMAT_DATA_REQUEST:
glog.Info("CB_FORMAT_DATA_REQUEST")
c.processFormatDataRequest(b)
case CB_FORMAT_DATA_RESPONSE:
glog.Info("CB_FORMAT_DATA_RESPONSE")
c.processFormatDataResponse(flag, b)
case CB_FILECONTENTS_REQUEST:
glog.Info("CB_FILECONTENTS_REQUEST")
c.processFileContentsRequest(b)
case CB_FILECONTENTS_RESPONSE:
glog.Info("CB_FILECONTENTS_RESPONSE")
c.processFileContentsResponse(flag, b)
case CB_LOCK_CLIPDATA:
glog.Info("CB_LOCK_CLIPDATA")
c.processLockClipData(b)
case CB_UNLOCK_CLIPDATA:
glog.Info("CB_UNLOCK_CLIPDATA")
c.processUnlockClipData(b)
default:
glog.Errorf("type 0x%x not supported", msgType)
}
}
func (c *CliprdrClient) processClipCaps(b []byte) {
r := bytes.NewReader(b)
var cp CliprdrCapabilitiesPDU
err := struc.Unpack(r, &cp)
if err != nil {
glog.Error(err)
return
}
glog.Debugf("Capabilities:%+v", cp)
c.useLongFormatNames = cp.CapabilitySets[0].GeneralFlags&CB_USE_LONG_FORMAT_NAMES != 0
c.streamFileClipEnabled = cp.CapabilitySets[0].GeneralFlags&CB_STREAM_FILECLIP_ENABLED != 0
c.fileClipNoFilePaths = cp.CapabilitySets[0].GeneralFlags&CB_FILECLIP_NO_FILE_PATHS != 0
c.canLockClipData = cp.CapabilitySets[0].GeneralFlags&CB_CAN_LOCK_CLIPDATA != 0
c.hasHugeFileSupport = cp.CapabilitySets[0].GeneralFlags&CB_HUGE_FILE_SUPPORT_ENABLED != 0
glog.Info("UseLongFormatNames:", c.useLongFormatNames)
glog.Info("StreamFileClipEnabled:", c.streamFileClipEnabled)
glog.Info("FileClipNoFilePaths:", c.fileClipNoFilePaths)
glog.Info("CanLockClipData:", c.canLockClipData)
glog.Info("HasHugeFileSupport:", c.hasHugeFileSupport)
}
func (c *CliprdrClient) processMonitorReady(b []byte) {
//Client Clipboard Capabilities PDU
c.sendClientCapabilitiesPDU()
//Temporary Directory PDU
//c.sendTemporaryDirectoryPDU()
//Format List PDU
c.sendFormatListPDU()
}
func (c *CliprdrClient) processFormatList(b []byte) {
c.withOpenClipboard(func() {
if !EmptyClipboard() {
glog.Error("EmptyClipboard failed")
}
})
fl, hasFile := c.readForamtList(b)
glog.Info("numFormats:", fl.NumFormats)
if hasFile {
c.SendCliprdrMessage()
} else {
c.withOpenClipboard(func() {
if !EmptyClipboard() {
glog.Error("EmptyClipboard failed")
}
for i := range c.formatIdMap {
glog.Debug("i:", i)
SetClipboardData(i, 0)
}
})
}
c.sendFormatListResponse(CB_RESPONSE_OK)
}
func (c *CliprdrClient) processFormatListResponse(flag uint16, b []byte) {
if flag != CB_RESPONSE_OK {
glog.Error("Format List Response Failed")
return
}
glog.Error("Format List Response OK")
}
func getFilesDescriptor(name string) (FileDescriptor, error) {
var fd FileDescriptor
fd.Flags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI
f, e := os.Stat(name)
if e != nil {
glog.Error(e.Error())
return fd, e
}
fd.FileAttributes, fd.LastWriteTime,
fd.FileSizeHigh, fd.FileSizeLow = GetFileInfo(f.Sys())
fd.FileName = core.UnicodeEncode(name)
return fd, nil
}
func (c *CliprdrClient) processFormatDataRequest(b []byte) {
r := bytes.NewReader(b)
requestId, _ := core.ReadUInt32LE(r)
buff := &bytes.Buffer{}
if requestId == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW) {
fs := GetFileNames()
core.WriteUInt32LE(uint32(len(fs)), buff)
c.Files = c.Files[:0]
for _, v := range fs {
glog.Info("Name:", v)
f, _ := getFilesDescriptor(v)
buff.Write(f.serialize())
for i := 0; i < 8; i++ {
buff.WriteByte(0)
}
c.Files = append(c.Files, f)
}
} else {
c.withOpenClipboard(func() {
data := GetClipboardData(requestId)
glog.Debug("data:", data)
buff.Write(core.UnicodeEncode(data))
buff.Write([]byte{0, 0})
})
}
c.sendFormatDataResponse(buff.Bytes())
}
func (c *CliprdrClient) processFormatDataResponse(flag uint16, b []byte) {
if flag != CB_RESPONSE_OK {
glog.Error("Format Data Response Failed")
}
c.reply <- b
}
func (c *CliprdrClient) processFileContentsRequest(b []byte) {
r := bytes.NewReader(b)
var req CliprdrFileContentsRequest
struc.Unpack(r, &req)
if len(c.Files) <= int(req.Lindex) {
glog.Error("No found file:", req.Lindex)
c.sendFormatContentsResponse(req.StreamId, []byte{})
return
}
buff := &bytes.Buffer{}
/*o := OleGetClipboard()
var format_etc FORMATETC
var stg_medium STGMEDIUM
format_etc.CFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS)
format_etc.Tymed = TYMED_ISTREAM
format_etc.Aspect = 1
format_etc.Index = req.Lindex
o.GetData(&format_etc, &stg_medium)
s, _ := stg_medium.Stream()*/
f := c.Files[req.Lindex]
if req.DwFlags == FILECONTENTS_SIZE {
core.WriteUInt32LE(f.FileSizeLow, buff)
core.WriteUInt32LE(f.FileSizeHigh, buff)
c.sendFormatContentsResponse(req.StreamId, buff.Bytes())
} else if req.DwFlags == FILECONTENTS_RANGE {
name := core.UnicodeDecode(f.FileName)
fi, err := os.Open(name)
if err != nil {
glog.Error(err.Error())
return
}
defer fi.Close()
data := make([]byte, req.CbRequested)
n, _ := fi.ReadAt(data, int64(f.FileSizeHigh))
c.sendFormatContentsResponse(req.StreamId, data[:n])
}
}
func (c *CliprdrClient) processFileContentsResponse(flag uint16, b []byte) {
if flag != CB_RESPONSE_OK {
glog.Error("File Contents Response Failed")
}
var resp CliprdrFileContentsResponse
resp.Unpack(b)
glog.Debug("Get File Contents Response:", resp.StreamId, resp.CbRequested)
c.reply <- resp.RequestedData
}
func (c *CliprdrClient) processLockClipData(b []byte) {
r := bytes.NewReader(b)
var l CliprdrCtrlClipboardData
l.ClipDataId, _ = core.ReadUInt32LE(r)
}
func (c *CliprdrClient) processUnlockClipData(b []byte) {
r := bytes.NewReader(b)
var l CliprdrCtrlClipboardData
l.ClipDataId, _ = core.ReadUInt32LE(r)
}
func (c *CliprdrClient) sendClientCapabilitiesPDU() {
glog.Info("Send Client Clipboard Capabilities PDU")
var cs CliprdrGeneralCapabilitySet
cs.CapabilitySetLength = 12
cs.CapabilitySetType = CB_CAPSTYPE_GENERAL
cs.Version = CB_CAPS_VERSION_2
cs.GeneralFlags = CB_USE_LONG_FORMAT_NAMES |
CB_STREAM_FILECLIP_ENABLED |
CB_FILECLIP_NO_FILE_PATHS
var cc CliprdrCapabilitiesPDU
cc.CCapabilitiesSets = 1
cc.Pad1 = 0
cc.CapabilitySets = make([]CliprdrGeneralCapabilitySet, 0, 1)
cc.CapabilitySets = append(cc.CapabilitySets, cs)
header := NewCliprdrPDUHeader(CB_CLIP_CAPS, 0, 16)
buff := &bytes.Buffer{}
buff.Write(header.serialize())
core.WriteUInt16LE(cc.CCapabilitiesSets, buff)
core.WriteUInt16LE(cc.Pad1, buff)
for _, v := range cc.CapabilitySets {
struc.Pack(buff, v)
}
c.Send(buff.Bytes())
}
func (c *CliprdrClient) sendTemporaryDirectoryPDU() {
glog.Info("Send Temporary Directory PDU")
var t CliprdrTempDirectory
header := &CliprdrPDUHeader{CB_TEMP_DIRECTORY, 0, 260}
t.SzTempDir = core.UnicodeEncode(os.TempDir())
buff := &bytes.Buffer{}
core.WriteBytes(header.serialize(), buff)
core.WriteBytes(t.SzTempDir, buff)
c.Send(buff.Bytes())
}
func (c *CliprdrClient) sendFormatListPDU() {
glog.Info("Send Format List PDU")
var f CliprdrFormatList
f.Formats = GetFormatList(c.hwnd)
f.NumFormats = uint32(len(f.Formats))
glog.Info("NumFormats:", f.NumFormats)
glog.Debug("Formats:", f.Formats)
b := &bytes.Buffer{}
for _, v := range f.Formats {
core.WriteUInt32LE(v.FormatId, b)
if v.FormatName == "" {
core.WriteUInt16LE(0, b)
} else {
n := core.UnicodeEncode(v.FormatName)
core.WriteBytes(n, b)
b.Write([]byte{0, 0})
}
}
header := NewCliprdrPDUHeader(CB_FORMAT_LIST, 0, uint32(b.Len()))
buff := &bytes.Buffer{}
buff.Write(header.serialize())
core.WriteBytes(b.Bytes(), buff)
c.Send(buff.Bytes())
}
func (c *CliprdrClient) readForamtList(b []byte) (*CliprdrFormatList, bool) {
r := bytes.NewReader(b)
fs := make([]CliprdrFormat, 0, 20)
var numFormats uint32 = 0
hasFile := false
c.formatIdMap = make(map[uint32]uint32, 0)
for r.Len() > 0 {
foramtId, _ := core.ReadUInt32LE(r)
bs := make([]uint16, 0, 20)
ln := r.Len()
for j := 0; j < ln; j++ {
b, _ := core.ReadUint16LE(r)
if b == 0 {
break
}
bs = append(bs, b)
}
name := string(utf16.Decode(bs))
if strings.EqualFold(name, CFSTR_FILEDESCRIPTORW) {
hasFile = true
}
glog.Infof("Foramt:%d Name:<%s>", foramtId, name)
if name != "" {
localId := RegisterClipboardFormat(name)
glog.Info("local:", localId, "remote:", foramtId)
c.formatIdMap[localId] = foramtId
} else {
c.formatIdMap[foramtId] = foramtId
}
numFormats++
fs = append(fs, CliprdrFormat{foramtId, name})
}
return &CliprdrFormatList{numFormats, fs}, hasFile
}
func (c *CliprdrClient) sendFormatListResponse(flags uint16) {
glog.Info("Send Format List Response")
header := NewCliprdrPDUHeader(CB_FORMAT_LIST_RESPONSE, flags, 0)
buff := &bytes.Buffer{}
buff.Write(header.serialize())
c.Send(buff.Bytes())
}
func (c *CliprdrClient) sendFormatDataRequest(id uint32) {
glog.Info("Send Format Data Request")
var r CliprdrFormatDataRequest
r.RequestedFormatId = id
header := NewCliprdrPDUHeader(CB_FORMAT_DATA_REQUEST, 0, 4)
buff := &bytes.Buffer{}
buff.Write(header.serialize())
core.WriteUInt32LE(r.RequestedFormatId, buff)
c.Send(buff.Bytes())
}
func (c *CliprdrClient) sendFormatDataResponse(b []byte) {
glog.Info("Send Format Data Response")
var resp CliprdrFormatDataResponse
resp.RequestedFormatData = b
header := NewCliprdrPDUHeader(CB_FORMAT_DATA_RESPONSE, CB_RESPONSE_OK, uint32(len(resp.RequestedFormatData)))
buff := &bytes.Buffer{}
buff.Write(header.serialize())
buff.Write(resp.RequestedFormatData)
c.Send(buff.Bytes())
}
func (c *CliprdrClient) sendFormatContentsRequest(r CliprdrFileContentsRequest) uint32 {
glog.Info("Send Format Contents Request")
glog.Debugf("Format Contents Request:%+v", r)
header := NewCliprdrPDUHeader(CB_FILECONTENTS_REQUEST, 0, 28)
buff := &bytes.Buffer{}
buff.Write(header.serialize())
core.WriteUInt32LE(r.StreamId, buff)
core.WriteUInt32LE(uint32(r.Lindex), buff)
core.WriteUInt32LE(r.DwFlags, buff)
core.WriteUInt32LE(r.NPositionLow, buff)
core.WriteUInt32LE(r.NPositionHigh, buff)
core.WriteUInt32LE(r.CbRequested, buff)
core.WriteUInt32LE(r.ClipDataId, buff)
c.Send(buff.Bytes())
return uint32(buff.Len())
}
func (c *CliprdrClient) sendFormatContentsResponse(streamId uint32, b []byte) {
glog.Info("Send Format Contents Response")
var r CliprdrFileContentsResponse
r.StreamId = streamId
r.RequestedData = b
r.CbRequested = uint32(len(b))
header := NewCliprdrPDUHeader(CB_FILECONTENTS_RESPONSE, CB_RESPONSE_OK, uint32(4+r.CbRequested))
buff := &bytes.Buffer{}
buff.Write(header.serialize())
core.WriteUInt32LE(r.StreamId, buff)
core.WriteBytes(r.RequestedData, buff)
c.Send(buff.Bytes())
}
func (c *CliprdrClient) sendLockClipData() {
glog.Info("Send Lock Clip Data")
var r CliprdrCtrlClipboardData
header := NewCliprdrPDUHeader(CB_LOCK_CLIPDATA, 0, 4)
buff := &bytes.Buffer{}
buff.Write(header.serialize())
core.WriteUInt32LE(r.ClipDataId, buff)
c.Send(buff.Bytes())
}
func (c *CliprdrClient) sendUnlockClipData() {
glog.Info("Send Unlock Clip Data")
var r CliprdrCtrlClipboardData
header := NewCliprdrPDUHeader(CB_UNLOCK_CLIPDATA, 0, 4)
buff := &bytes.Buffer{}
buff.Write(header.serialize())
core.WriteUInt32LE(r.ClipDataId, buff)
c.Send(buff.Bytes())
}

View File

@@ -1,353 +0,0 @@
// cliprdr_windows.go
package cliprdr
import (
"bytes"
"syscall"
"unicode/utf16"
"unsafe"
"github.com/shirou/w32"
"ShotRDP/grdp/core"
"ShotRDP/grdp/glog"
"ShotRDP/grdp/win"
)
const (
CFSTR_SHELLIDLIST = "Shell IDList Array"
CFSTR_SHELLIDLISTOFFSET = "Shell Object Offsets"
CFSTR_NETRESOURCES = "Net Resource"
CFSTR_FILECONTENTS = "FileContents"
CFSTR_FILENAMEA = "FileName"
CFSTR_FILENAMEMAPA = "FileNameMap"
CFSTR_FILEDESCRIPTORA = "FileGroupDescriptor"
CFSTR_INETURLA = "UniformResourceLocator"
CFSTR_SHELLURL = CFSTR_INETURLA
CFSTR_FILENAMEW = "FileNameW"
CFSTR_FILENAMEMAPW = "FileNameMapW"
CFSTR_FILEDESCRIPTORW = "FileGroupDescriptorW"
CFSTR_INETURLW = "UniformResourceLocatorW"
CFSTR_PRINTERGROUP = "PrinterFriendlyName"
CFSTR_INDRAGLOOP = "InShellDragLoop"
CFSTR_PASTESUCCEEDED = "Paste Succeeded"
CFSTR_PERFORMEDDROPEFFECT = "Performed DropEffect"
CFSTR_PREFERREDDROPEFFECT = "Preferred DropEffect"
)
const DVASPECT_CONTENT = 0x1
const (
CF_TEXT = 1
CF_BITMAP = 2
CF_METAFILEPICT = 3
CF_SYLK = 4
CF_DIF = 5
CF_TIFF = 6
CF_OEMTEXT = 7
CF_DIB = 8
CF_PALETTE = 9
CF_PENDATA = 10
CF_RIFF = 11
CF_WAVE = 12
CF_UNICODETEXT = 13
CF_ENHMETAFILE = 14
CF_HDROP = 15
CF_LOCALE = 16
CF_DIBV5 = 17
CF_MAX = 18
)
const (
WM_CLIPRDR_MESSAGE = (w32.WM_USER + 156)
OLE_SETCLIPBOARD = 1
)
type Control struct {
hwnd uintptr
dataObject *IDataObject
}
func (c *Control) withOpenClipboard(f func()) {
if OpenClipboard(c.hwnd) {
f()
CloseClipboard()
}
}
func ClipWatcher(c *CliprdrClient) {
win.OleInitialize(0)
defer win.OleUninitialize()
className := syscall.StringToUTF16Ptr("ClipboardHiddenMessageProcessor")
windowName := syscall.StringToUTF16Ptr("rdpclip")
wndClassEx := w32.WNDCLASSEX{
ClassName: className,
WndProc: syscall.NewCallback(func(hwnd w32.HWND, msg uint32, wParam, lParam uintptr) uintptr {
switch msg {
case w32.WM_CLIPBOARDUPDATE:
glog.Info("info: WM_CLIPBOARDUPDATE wParam:", wParam)
glog.Debug("IsClipboardOwner:", IsClipboardOwner(win.HWND(c.hwnd)))
glog.Debug("OleIsCurrentClipboard:", OleIsCurrentClipboard(c.dataObject))
if !IsClipboardOwner(win.HWND(c.hwnd)) && int(wParam) != 0 &&
!OleIsCurrentClipboard(c.dataObject) {
c.sendFormatListPDU()
}
case w32.WM_RENDERALLFORMATS:
glog.Info("info: WM_RENDERALLFORMATS")
c.withOpenClipboard(func() {
EmptyClipboard()
})
case w32.WM_RENDERFORMAT:
glog.Info("info: WM_RENDERFORMAT wParam:", wParam)
formatId := uint32(wParam)
c.sendFormatDataRequest(formatId)
b := <-c.reply
hmem := HmemAlloc(b)
SetClipboardData(formatId, hmem)
case WM_CLIPRDR_MESSAGE:
glog.Info("info: WM_CLIPRDR_MESSAGE wParam:", wParam)
if wParam == OLE_SETCLIPBOARD {
if !OleIsCurrentClipboard(c.dataObject) {
o := CreateDataObject(c)
OleSetClipboard(o)
c.dataObject = o
}
}
default:
return w32.DefWindowProc(hwnd, msg, wParam, lParam)
}
return 0
}),
Style: w32.CS_OWNDC,
}
wndClassEx.Size = uint32(unsafe.Sizeof(wndClassEx))
w32.RegisterClassEx(&wndClassEx)
hwnd := w32.CreateWindowEx(w32.WS_EX_LEFT, className, windowName, 0, 0, 0, 0, 0, w32.HWND_MESSAGE, 0, 0, nil)
c.hwnd = uintptr(hwnd)
w32.AddClipboardFormatListener(hwnd)
defer w32.RemoveClipboardFormatListener(hwnd)
msg := w32.MSG{}
for w32.GetMessage(&msg, 0, 0, 0) > 0 {
w32.DispatchMessage(&msg)
}
}
func OpenClipboard(hwnd uintptr) bool {
return win.OpenClipboard(win.HWND(hwnd))
}
func CloseClipboard() bool {
return win.CloseClipboard()
}
func CountClipboardFormats() int32 {
return win.CountClipboardFormats()
}
func IsClipboardFormatAvailable(id uint32) bool {
return win.IsClipboardFormatAvailable(win.UINT(id))
}
func EnumClipboardFormats(formatId uint32) uint32 {
id := win.EnumClipboardFormats(win.UINT(formatId))
return uint32(id)
}
func GetClipboardFormatName(id uint32) string {
buf := make([]uint16, 250)
n := win.GetClipboardFormatName(win.UINT(id), win.LPWSTR(unsafe.Pointer(&buf[0])), int32(len(buf)))
return string(utf16.Decode(buf[:n]))
}
func EmptyClipboard() bool {
return win.EmptyClipboard()
}
func RegisterClipboardFormat(format string) uint32 {
id := win.RegisterClipboardFormat(format)
return uint32(id)
}
func IsClipboardOwner(h win.HWND) bool {
hwnd := win.GetClipboardOwner()
return h == hwnd
}
func HmemAlloc(data []byte) uintptr {
ln := (len(data))
h := win.GlobalAlloc(0x0002, win.SIZE_T(ln))
if h == 0 {
return uintptr(h)
}
if ln == 0 {
return uintptr(h)
}
l := win.GlobalLock(h)
defer win.GlobalUnlock(h)
win.RtlCopyMemory(uintptr(unsafe.Pointer(l)), uintptr(unsafe.Pointer(&data[0])), win.SIZE_T(ln))
return uintptr(h)
}
func SetClipboardData(formatId uint32, hmem uintptr) bool {
r := win.SetClipboardData(win.UINT(formatId), win.HANDLE(hmem))
if r == 0 {
//glog.Error("SetClipboardData failed:", formatId, hmem)
return false
}
return true
}
func GetClipboardData(formatId uint32) string {
r := win.GetClipboardData(win.UINT(formatId))
if r == 0 {
return ""
}
h := win.GlobalHandle(uintptr(r))
size := win.GlobalSize(h)
l := win.GlobalLock(h)
defer win.GlobalUnlock(h)
result := make([]byte, size)
win.RtlCopyMemory(uintptr(unsafe.Pointer(&result[0])), uintptr(unsafe.Pointer(l)), size)
return core.UnicodeDecode(result)
}
func GetFormatList(hwnd uintptr) []CliprdrFormat {
list := make([]CliprdrFormat, 0, 10)
if OpenClipboard(hwnd) {
n := CountClipboardFormats()
if IsClipboardFormatAvailable(CF_HDROP) {
formatId := RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW)
var f CliprdrFormat
f.FormatId = formatId
f.FormatName = CFSTR_FILEDESCRIPTORW
list = append(list, f)
formatId = RegisterClipboardFormat(CFSTR_FILECONTENTS)
var f1 CliprdrFormat
f1.FormatId = formatId
f1.FormatName = CFSTR_FILECONTENTS
list = append(list, f1)
} else {
var id uint32
for i := 0; i < int(n); i++ {
id = EnumClipboardFormats(id)
name := GetClipboardFormatName(id)
var f CliprdrFormat
f.FormatId = id
f.FormatName = name
list = append(list, f)
}
}
CloseClipboard()
}
return list
}
func OleGetClipboard() *IDataObject {
var dataObject *IDataObject
win.OleGetClipboard((**win.IDataObject)(unsafe.Pointer(&dataObject)))
return dataObject
}
func OleSetClipboard(dataObject *IDataObject) bool {
r := win.OleSetClipboard((*win.IDataObject)(unsafe.Pointer(dataObject)))
if r != 0 {
glog.Error("OleSetClipboard failed")
return false
}
return true
}
func OleIsCurrentClipboard(dataObject *IDataObject) bool {
r := win.OleIsCurrentClipboard((*win.IDataObject)(unsafe.Pointer(dataObject)))
if r != 0 {
return false
}
return true
}
func GlobalSize(hMem uintptr) win.SIZE_T {
return win.GlobalSize(win.HGLOBAL(hMem))
}
func GlobalLock(hMem uintptr) uintptr {
r := win.GlobalLock(win.HGLOBAL(hMem))
return uintptr(r)
}
func GlobalUnlock(hMem uintptr) {
win.GlobalUnlock(win.HGLOBAL(hMem))
}
func (c *Control) SendCliprdrMessage() {
win.PostMessage(win.HWND(c.hwnd), WM_CLIPRDR_MESSAGE, OLE_SETCLIPBOARD, 0)
}
func GetFileInfo(sys interface{}) (uint32, []byte, uint32, uint32) {
f := sys.(*syscall.Win32FileAttributeData)
b := &bytes.Buffer{}
core.WriteUInt32LE(f.LastWriteTime.LowDateTime, b)
core.WriteUInt32LE(f.LastWriteTime.HighDateTime, b)
return f.FileAttributes, b.Bytes(), f.FileSizeHigh, f.FileSizeLow
}
func GetFileNames() []string {
o := OleGetClipboard()
var formatEtc FORMATETC
var stgMedium STGMEDIUM
formatEtc.CFormat = CF_HDROP
formatEtc.Tymed = TYMED_HGLOBAL
formatEtc.Aspect = DVASPECT_CONTENT
formatEtc.Index = -1
o.GetData(&formatEtc, &stgMedium)
b, _ := stgMedium.Bytes()
//DROPFILES
r := bytes.NewReader(b[20:])
fs := make([]string, 0, 20)
for r.Len() > 0 {
bs := make([]uint16, 0, 20)
ln := r.Len()
for j := 0; j < ln; j++ {
b, _ := core.ReadUint16LE(r)
if b == 0 {
break
}
bs = append(bs, b)
}
name := string(utf16.Decode(bs))
if name == "" {
continue
}
fs = append(fs, name)
}
return fs
}
const (
/* File attribute flags */
FILE_SHARE_READ = 0x00000001
FILE_SHARE_WRITE = 0x00000002
FILE_SHARE_DELETE = 0x00000004
FILE_ATTRIBUTE_READONLY = 0x00000001
FILE_ATTRIBUTE_HIDDEN = 0x00000002
FILE_ATTRIBUTE_SYSTEM = 0x00000004
FILE_ATTRIBUTE_DIRECTORY = 0x00000010
FILE_ATTRIBUTE_ARCHIVE = 0x00000020
FILE_ATTRIBUTE_DEVICE = 0x00000040
FILE_ATTRIBUTE_NORMAL = 0x00000080
FILE_ATTRIBUTE_TEMPORARY = 0x00000100
FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200
FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400
FILE_ATTRIBUTE_COMPRESSED = 0x00000800
FILE_ATTRIBUTE_OFFLINE = 0x00001000
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000
FILE_ATTRIBUTE_ENCRYPTED = 0x00004000
FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x00008000
FILE_ATTRIBUTE_VIRTUAL = 0x00010000
FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x00020000
FILE_ATTRIBUTE_EA = 0x00040000
)
type DROPFILES struct {
pFiles uintptr
pt uintptr
fNC bool
fWide bool
}

View File

@@ -1,864 +0,0 @@
//go:build windows
// +build windows
// dataobject.go
package cliprdr
import (
"bytes"
"fmt"
"io"
"reflect"
"sync/atomic"
"syscall"
"unsafe"
"ShotRDP/grdp/core"
"ShotRDP/grdp/glog"
"ShotRDP/grdp/win"
)
const (
S_OK = 0x00000000
E_UNEXPECTED = 0x8000FFFF
E_NOTIMPL = 0x80004001
E_OUTOFMEMORY = 0x8007000E
E_INVALIDARG = 0x80070057
E_NOINTERFACE = 0x80004002
E_POINTER = 0x80004003
E_HANDLE = 0x80070006
E_ABORT = 0x80004004
E_FAIL = 0x80004005
E_ACCESSDENIED = 0x80070005
E_PENDING = 0x8000000A
E_ADVISENOTSUPPORTED = 0x80040003
E_FORMATETC = 0x80040064
CO_E_CLASSSTRING = 0x800401F3
)
type HRESULT uintptr
func (hr HRESULT) Error() string {
switch uint32(hr) {
case 0x80040064:
return "DV_E_FORMATETC (0x80040064)"
case 0x800401D3:
return "CLIPBRD_E_BAD_DATA (0x800401D3)"
case 0x80004005:
return "E_FAIL (0x80004005)"
case 0x00000001:
return "S_FALSE (0x00000001)"
}
return fmt.Sprintf("%d", hr)
}
const (
TYMED_NULL = 0x0
TYMED_HGLOBAL = 0x1
TYMED_FILE = 0x2
TYMED_ISTREAM = 0x4
TYMED_ISTORAGE = 0x8
TYMED_GDI = 0x10
TYMED_MFPICT = 0x20
TYMED_ENHMF = 0x40
)
type iUnknownVtbl struct {
QueryInterface uintptr
AddRef uintptr
Release uintptr
}
type iDataObjectVtbl struct {
iUnknownVtbl
GetData uintptr
GetDataHere uintptr
QueryGetData uintptr
GetCanonicalFormatEtc uintptr
SetData uintptr
EnumFormatEtc uintptr
DAdvise uintptr
DUnadvise uintptr
EnumDAdvise uintptr
}
type FORMATETC struct {
CFormat uint32
DvTargetDevice uintptr
Aspect uint32
Index int32
Tymed uint32
}
type STGMEDIUM struct {
Tymed uint32
UnionMember uintptr
PUnkForRelease *IUnknown
}
type ISequentialStreamVtbl struct {
iUnknownVtbl
Read uintptr
Write uintptr
}
type IUnknown struct {
vtbl iUnknownVtbl
}
func (obj *IUnknown) Release() error {
ret, _, _ := syscall.Syscall(
obj.vtbl.Release,
1,
uintptr(unsafe.Pointer(obj)),
0,
0,
)
if ret != 0 {
return HRESULT(ret)
}
return nil
}
func (m STGMEDIUM) Release() {
if m.PUnkForRelease == nil {
win.ReleaseStgMedium((*win.STGMEDIUM)(unsafe.Pointer(&m)))
}
}
func (m STGMEDIUM) Stream() (*IStream, error) {
if m.Tymed != TYMED_ISTREAM {
return nil, fmt.Errorf("invalid Tymed")
}
return (*IStream)(unsafe.Pointer(m.UnionMember)), nil
}
func (m STGMEDIUM) Bytes() ([]byte, error) {
if m.Tymed != TYMED_HGLOBAL {
return nil, fmt.Errorf("invalid Tymed")
}
size := GlobalSize(m.UnionMember)
l := GlobalLock(m.UnionMember)
defer GlobalUnlock(m.UnionMember)
result := make([]byte, size)
win.RtlCopyMemory(uintptr(unsafe.Pointer(&result[0])), uintptr(unsafe.Pointer(l)), size)
return result, nil
}
type IDataObject struct {
vtbl *iDataObjectVtbl
}
func (obj *IDataObject) Release() error {
ret, _, _ := syscall.Syscall(
obj.vtbl.Release,
1,
uintptr(unsafe.Pointer(obj)),
0,
0,
)
if ret != 0 {
return HRESULT(ret)
}
return nil
}
func (obj *IDataObject) GetData(formatEtc *FORMATETC, medium *STGMEDIUM) error {
s2 := unsafe.Sizeof(*medium)
_ = s2
ret, _, _ := syscall.Syscall(
obj.vtbl.GetData,
3,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(formatEtc)),
uintptr(unsafe.Pointer(medium)),
)
if ret != 0 {
return HRESULT(ret)
}
return nil
}
func (obj *IDataObject) GetDataHere(formatEtc *FORMATETC, medium *STGMEDIUM) error {
ret, _, _ := syscall.Syscall(
obj.vtbl.GetDataHere,
3,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(formatEtc)),
uintptr(unsafe.Pointer(medium)),
)
if ret != 0 {
return HRESULT(ret)
}
return nil
}
func (obj *IDataObject) QueryGetData(formatEtc *FORMATETC) error {
ret, _, _ := syscall.Syscall(
obj.vtbl.QueryGetData,
2,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(formatEtc)),
0,
)
if ret != 0 {
return HRESULT(ret)
}
return nil
}
func (obj *IDataObject) EnumFormatEtc(direction uint32, pIEnumFORMATETC **IEnumFORMATETC) error {
ret, _, _ := syscall.Syscall(
obj.vtbl.EnumFormatEtc,
3,
uintptr(unsafe.Pointer(obj)),
uintptr(direction),
uintptr(unsafe.Pointer(pIEnumFORMATETC)),
)
if ret != 0 {
return HRESULT(ret)
}
return nil
}
type iEnumFORMATETCVtbl struct {
iUnknownVtbl
Next uintptr
Skip uintptr
Reset uintptr
Clone uintptr
}
type IEnumFORMATETC struct {
vtbl *iEnumFORMATETCVtbl
}
func (obj *IEnumFORMATETC) Release() error {
ret, _, _ := syscall.Syscall(
obj.vtbl.Release,
1,
uintptr(unsafe.Pointer(obj)),
0,
0,
)
if ret != 0 {
return HRESULT(ret)
}
return nil
}
func (obj *IEnumFORMATETC) Next(formatEtc []FORMATETC, celtFetched *uint32) error {
ret, _, _ := syscall.Syscall6(
obj.vtbl.Next,
4,
uintptr(unsafe.Pointer(obj)),
uintptr(len(formatEtc)),
uintptr(unsafe.Pointer(&formatEtc[0])),
uintptr(unsafe.Pointer(celtFetched)),
0,
0,
)
if ret != 1 {
return HRESULT(ret)
}
return nil
}
type EnumInstance struct {
iEnumFORMATETC IEnumFORMATETC
refCount int32
index int
formatEtc []FORMATETC
}
var (
IID_IDataObject = win.GUID{0x10e, 0, 0, [8]byte{0xc0, 0, 0, 0, 0, 0, 0, 0x46}}
IID_IUnknown = win.GUID{0x000, 0, 0, [8]byte{0xc0, 0, 0, 0, 0, 0, 0, 0x46}}
IID_IEnumFORMATETC = win.GUID{0x103, 0, 0, [8]byte{0xc0, 0, 0, 0, 0, 0, 0, 0x46}}
IID_IStream = win.GUID{0x00c, 0, 0, [8]byte{0xc0, 0, 0, 0, 0, 0, 0, 0x46}}
)
func newEnumInstance(fs []FORMATETC) *EnumInstance {
var instance EnumInstance
ie := &instance.iEnumFORMATETC
ie.vtbl = new(iEnumFORMATETCVtbl)
ie.vtbl.QueryInterface = syscall.NewCallback((*EnumInstance).QueryInterface)
ie.vtbl.AddRef = syscall.NewCallback((*EnumInstance).AddRef)
ie.vtbl.Release = syscall.NewCallback((*EnumInstance).Release)
ie.vtbl.Next = syscall.NewCallback((*EnumInstance).Next)
ie.vtbl.Skip = syscall.NewCallback((*EnumInstance).Skip)
ie.vtbl.Reset = syscall.NewCallback((*EnumInstance).Reset)
ie.vtbl.Clone = syscall.NewCallback((*EnumInstance).Clone)
instance.refCount = 1
if len(fs) > 0 {
instance.formatEtc = make([]FORMATETC, len(fs))
copy(instance.formatEtc, fs)
}
return &instance
}
func (i *EnumInstance) QueryInterface(riid win.REFGUID, ppvObject *uintptr) uintptr {
if win.IsEqualGUID(riid, &IID_IEnumFORMATETC) ||
win.IsEqualGUID(riid, &IID_IUnknown) {
*ppvObject = uintptr(unsafe.Pointer(i))
i.AddRef()
return 0
}
*ppvObject = 0
return E_NOINTERFACE
}
func (i *EnumInstance) AddRef() uintptr {
n := atomic.AddInt32(&i.refCount, 1)
return uintptr(n)
}
func (i *EnumInstance) Release() uintptr {
n := atomic.AddInt32(&i.refCount, -1)
if n == 0 {
i = nil
return 0
}
return uintptr(n)
}
func (i *EnumInstance) Next(celt uint32, rgelt *FORMATETC, pceltFetched *uint32) uintptr {
r := make([]FORMATETC, celt)
var idx uint32
for i.index < len(i.formatEtc) && idx < celt {
r[idx] = i.formatEtc[i.index]
i.index++
idx++
}
*rgelt = r[0]
if pceltFetched != nil {
*pceltFetched = idx
}
if idx != celt {
return E_FAIL
}
return 0
}
func (i *EnumInstance) Skip(celt uint32) uintptr {
if i.index+int(celt) > len(i.formatEtc) {
return E_FAIL
}
i.index += int(celt)
return 0
}
func (i *EnumInstance) Reset() uintptr {
i.index = 0
return 0
}
func (i *EnumInstance) Clone(ppEnum **IEnumFORMATETC) uintptr {
ins := newEnumInstance(i.formatEtc)
ins.index = i.index
(*ppEnum) = (*IEnumFORMATETC)(unsafe.Pointer(ins))
return 0
}
func CreateDataObject(c *CliprdrClient) *IDataObject {
fmtetc := make([]FORMATETC, 2)
stgmeds := make([]STGMEDIUM, 2)
fmtetc[0].CFormat = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW)
fmtetc[0].Aspect = DVASPECT_CONTENT
fmtetc[0].Index = 0
//fmtetc[0].DvTargetDevice = nil
fmtetc[0].Tymed = TYMED_HGLOBAL
stgmeds[0].Tymed = TYMED_HGLOBAL
//stgmeds[0].UnionMember = nil
stgmeds[0].PUnkForRelease = nil
fmtetc[1].CFormat = RegisterClipboardFormat(CFSTR_FILECONTENTS)
fmtetc[1].Aspect = DVASPECT_CONTENT
fmtetc[1].Index = 0
//fmtetc[1].DvTargetDevice = nil
fmtetc[1].Tymed = TYMED_ISTREAM
stgmeds[1].Tymed = TYMED_ISTREAM
//stgmeds[1].UnionMember = nil
stgmeds[1].PUnkForRelease = nil
instance := newDataInstance()
instance.refCount = 1
instance.formatEtc = fmtetc
instance.stgMedium = stgmeds
instance.data = c
return (*IDataObject)(unsafe.Pointer(instance))
}
type DataInstance struct {
iDataObject IDataObject
refCount int32
idx int32
formatEtc []FORMATETC
stgMedium []STGMEDIUM
streams []*StreamInstance
data interface{}
}
func newDataInstance() *DataInstance {
var instance DataInstance
obj := &instance.iDataObject
obj.vtbl = new(iDataObjectVtbl)
obj.vtbl.QueryInterface = syscall.NewCallback((*DataInstance).QueryInterface)
obj.vtbl.AddRef = syscall.NewCallback((*DataInstance).AddRef)
obj.vtbl.Release = syscall.NewCallback((*DataInstance).Release)
obj.vtbl.GetData = syscall.NewCallback((*DataInstance).GetData)
obj.vtbl.GetDataHere = syscall.NewCallback((*DataInstance).GetDataHere)
obj.vtbl.QueryGetData = syscall.NewCallback((*DataInstance).QueryGetData)
obj.vtbl.GetCanonicalFormatEtc = syscall.NewCallback((*DataInstance).GetCanonicalFormatEtc)
obj.vtbl.SetData = syscall.NewCallback((*DataInstance).SetData)
obj.vtbl.EnumFormatEtc = syscall.NewCallback((*DataInstance).EnumFormatEtc)
obj.vtbl.DAdvise = syscall.NewCallback((*DataInstance).DAdvise)
obj.vtbl.DUnadvise = syscall.NewCallback((*DataInstance).DUnadvise)
obj.vtbl.EnumDAdvise = syscall.NewCallback((*DataInstance).EnumDAdvise)
return &instance
}
func (i *DataInstance) QueryInterface(riid win.REFGUID, ppvObject *uintptr) uintptr {
if win.IsEqualGUID(riid, &IID_IDataObject) ||
win.IsEqualGUID(riid, &IID_IUnknown) {
*ppvObject = uintptr(unsafe.Pointer(i))
i.AddRef()
return 0
}
*ppvObject = 0
return E_NOINTERFACE
}
func (i *DataInstance) AddRef() uintptr {
n := atomic.AddInt32(&i.refCount, 1)
return uintptr(n)
}
func (i *DataInstance) Release() uintptr {
n := atomic.AddInt32(&i.refCount, -1)
if n == 0 {
i = nil
return 0
}
return uintptr(n)
}
func (i *DataInstance) GetData(formatEtc *FORMATETC, medium *STGMEDIUM) uintptr {
idx := -1
for j, f := range i.formatEtc {
if formatEtc.Tymed&f.Tymed != 0 &&
formatEtc.CFormat == f.CFormat &&
formatEtc.Aspect&f.Aspect != 0 {
idx = j
}
}
if idx == -1 {
return E_FORMATETC
}
glog.Debugf("GetData:%+v, %s", formatEtc.CFormat, GetClipboardFormatName(formatEtc.CFormat))
medium.Tymed = i.formatEtc[idx].Tymed
if i.formatEtc[idx].CFormat == RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW) {
c := i.data.(*CliprdrClient)
if remoteid, ok := c.formatIdMap[i.formatEtc[idx].CFormat]; ok {
c.sendFormatDataRequest(remoteid)
b := <-c.reply
if len(b) == 0 {
return E_FAIL
}
medium.UnionMember = HmemAlloc(b)
var dsc FileGroupDescriptor
dsc.Unpack(b)
if dsc.CItems > 0 {
glog.Debug("Items:", dsc.CItems)
i.streams = make([]*StreamInstance, dsc.CItems)
var j uint32
for j = 0; j < dsc.CItems; j++ {
glog.Debug("FileName:", core.UnicodeDecode(dsc.Fgd[j].FileName))
s := newStream(j, i.data, &dsc.Fgd[j])
i.streams[j] = s
}
}
}
} else if i.formatEtc[idx].CFormat == RegisterClipboardFormat(CFSTR_FILECONTENTS) {
if formatEtc.Index >= 0 && formatEtc.Index < int32(len(i.streams)) {
medium.UnionMember = uintptr(unsafe.Pointer(i.streams[formatEtc.Index]))
i.AddRef()
} else {
return E_INVALIDARG
}
} else {
return E_UNEXPECTED
}
return 0
}
func (i *DataInstance) GetDataHere(formatEtc *FORMATETC, medium *STGMEDIUM) uintptr {
return E_NOTIMPL
}
func (i *DataInstance) QueryGetData(formatEtc *FORMATETC) uintptr {
for _, f := range i.formatEtc {
if formatEtc.Tymed&f.Tymed != 0 &&
formatEtc.CFormat == f.CFormat &&
formatEtc.Aspect&f.Aspect != 0 {
return 0
}
}
return E_FORMATETC
}
func (i *DataInstance) GetCanonicalFormatEtc(informatEtc, outformatEtc *FORMATETC) uintptr {
return E_NOTIMPL
}
func (i *DataInstance) SetData(formatEtc *FORMATETC, medium *STGMEDIUM, r bool) uintptr {
return E_NOTIMPL
}
func (i *DataInstance) EnumFormatEtc(dwDirection uint32, ppenumFormatEtc **IEnumFORMATETC) uintptr {
if dwDirection == 1 {
ins := newEnumInstance(i.formatEtc)
(*ppenumFormatEtc) = (*IEnumFORMATETC)(unsafe.Pointer(ins))
return 0
}
return E_NOTIMPL
}
func (i *DataInstance) DAdvise(formatEtc *FORMATETC, advf uint32, pAdvSink uintptr, pdwConnection *uint32) uintptr {
return E_ADVISENOTSUPPORTED
}
func (i *DataInstance) DUnadvise(dwDirection uint32) uintptr {
return E_ADVISENOTSUPPORTED
}
func (i *DataInstance) EnumDAdvise(ppenumAdvise uintptr) uintptr {
return E_ADVISENOTSUPPORTED
}
const (
STREAM_SEEK_SET = 0
STREAM_SEEK_CUR = 1
STREAM_SEEK_END = 2
)
const (
STATFLAG_DEFAULT = 0
STATFLAG_NONAME = 1
STATFLAG_NOOPEN = 2
)
const (
STG_E_INSUFFICIENTMEMORY = 0x80030008
STG_E_INVALIDFLAG = 0x800300FF
)
const (
STGTY_STORAGE = 1
STGTY_STREAM = 2
STGTY_LOCKBYTES = 3
STGTY_PROPERTY = 4
)
const (
LOCK_WRITE = 1
LOCK_EXCLUSIVE = 2
LOCK_ONLYONCE = 4
)
const (
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
GENERIC_EXECUTE = 0x20000000
)
type IStreamVtbl struct {
ISequentialStreamVtbl
Seek uintptr
SetSize uintptr
CopyTo uintptr
Commit uintptr
Revert uintptr
LockRegion uintptr
UnlockRegion uintptr
Stat uintptr
Clone uintptr
}
type IStream struct {
vtbl *IStreamVtbl
}
type ULARGE_INTEGER struct {
QuadPart uint64
}
type LARGE_INTEGER struct {
QuadPart int64
}
func (l *ULARGE_INTEGER) LowPart() *uint32 {
return (*uint32)(unsafe.Pointer(&l.QuadPart))
}
func (l *ULARGE_INTEGER) HighPart() *uint32 {
return (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&l.QuadPart)) + uintptr(4)))
}
func (l *LARGE_INTEGER) LowPart() *uint32 {
return (*uint32)(unsafe.Pointer(&l.QuadPart))
}
func (l *LARGE_INTEGER) HighPart() *int32 {
return (*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(&l.QuadPart)) + uintptr(4)))
}
type StreamInstance struct {
iStream IStream
streamId uint32
refCount int32
index uint32
lSize ULARGE_INTEGER
lOffset ULARGE_INTEGER
dsc FileDescriptor
data interface{}
}
type STATSTG struct {
pwcsName []uint16
sType uint32
cbSize ULARGE_INTEGER
mtime win.FILETIME
ctime win.FILETIME
atime win.FILETIME
grfMode uint32
grfLocksSupported uint32
clsid win.CLSID
grfStateBits uint32
reserved uint32
}
func (obj *IStream) Read(buffer []byte) (int, error) {
bufPtr := &buffer[0]
var read uint32
ret, _, _ := syscall.Syscall6(
obj.vtbl.Read,
4,
uintptr(unsafe.Pointer(obj)),
uintptr(unsafe.Pointer(bufPtr)),
uintptr(len(buffer)),
uintptr(unsafe.Pointer(&read)),
0,
0,
)
if ret == 1 {
return int(read), io.EOF
}
if ret != 0 {
return int(read), HRESULT(ret)
}
return int(read), nil
}
func (obj *IStream) Close() error {
return obj.Release()
}
func (obj *IStream) Release() error {
ret, _, _ := syscall.Syscall(
obj.vtbl.Release,
1,
uintptr(unsafe.Pointer(obj)),
0,
0,
)
if ret != 0 {
return HRESULT(ret)
}
return nil
}
func newStream(index uint32, data interface{}, dsc *FileDescriptor) *StreamInstance {
var instance StreamInstance
is := &instance.iStream
is.vtbl = new(IStreamVtbl)
is.vtbl.QueryInterface = syscall.NewCallback((*StreamInstance).QueryInterface)
is.vtbl.AddRef = syscall.NewCallback((*StreamInstance).AddRef)
is.vtbl.Release = syscall.NewCallback((*StreamInstance).Release)
is.vtbl.Read = syscall.NewCallback((*StreamInstance).Read)
is.vtbl.Write = syscall.NewCallback((*StreamInstance).Write)
is.vtbl.Seek = syscall.NewCallback((*StreamInstance).Seek)
is.vtbl.SetSize = syscall.NewCallback((*StreamInstance).SetSize)
is.vtbl.CopyTo = syscall.NewCallback((*StreamInstance).CopyTo)
is.vtbl.Commit = syscall.NewCallback((*StreamInstance).Commit)
is.vtbl.Revert = syscall.NewCallback((*StreamInstance).Revert)
is.vtbl.LockRegion = syscall.NewCallback((*StreamInstance).LockRegion)
is.vtbl.UnlockRegion = syscall.NewCallback((*StreamInstance).UnlockRegion)
is.vtbl.Stat = syscall.NewCallback((*StreamInstance).Stat)
is.vtbl.Clone = syscall.NewCallback((*StreamInstance).Clone)
instance.streamId = (uint32)(reflect.ValueOf(&instance).Pointer())
instance.refCount = 1
instance.dsc = *dsc
instance.data = data
instance.index = index
if !instance.dsc.hasFileSize() && !instance.dsc.isDir() {
c := data.(*CliprdrClient)
var r CliprdrFileContentsRequest
r.StreamId = instance.streamId
r.Lindex = instance.index
r.DwFlags = FILECONTENTS_SIZE
r.CbRequested = 8
c.sendFormatContentsRequest(r)
b := <-c.reply
instance.lSize.QuadPart = core.BytesToUint64(b)
} else {
b := &bytes.Buffer{}
core.WriteUInt32LE(dsc.FileSizeLow, b)
core.WriteUInt32LE(dsc.FileSizeHigh, b)
instance.lSize.QuadPart = core.BytesToUint64(b.Bytes())
}
return &instance
}
func (i *StreamInstance) QueryInterface(riid win.REFGUID, ppvObject *uintptr) uintptr {
if win.IsEqualGUID(riid, &IID_IStream) ||
win.IsEqualGUID(riid, &IID_IUnknown) {
*ppvObject = uintptr(unsafe.Pointer(i))
i.AddRef()
return 0
}
*ppvObject = 0
return E_NOINTERFACE
}
func (i *StreamInstance) AddRef() uintptr {
n := atomic.AddInt32(&i.refCount, 1)
return uintptr(n)
}
func (i *StreamInstance) Release() uintptr {
n := atomic.AddInt32(&i.refCount, -1)
if n == 0 {
i = nil
return 0
}
return uintptr(n)
}
func (i *StreamInstance) Read(pv uintptr, cb uint32, cbRead *uint32) uintptr {
glog.Debug("StreamInstance Read:", i.lOffset.QuadPart, i.lSize.QuadPart)
if i.lOffset.QuadPart >= i.lSize.QuadPart {
return 1
}
c := i.data.(*CliprdrClient)
*cbRead = 0
var r CliprdrFileContentsRequest
r.StreamId = i.streamId
r.Lindex = i.index
r.DwFlags = FILECONTENTS_RANGE
r.NPositionHigh = *(i.lOffset.HighPart())
r.NPositionLow = *(i.lOffset.LowPart())
r.CbRequested = cb
c.sendFormatContentsRequest(r)
b := <-c.reply
if len(b) == 0 {
return E_FAIL
}
win.RtlCopyMemory(pv, uintptr(unsafe.Pointer(&b[0])), win.SIZE_T(len(b)))
*cbRead = uint32(len(b))
i.lOffset.QuadPart += uint64(len(b))
glog.Debug("StreamInstance Read:", *cbRead, cb)
if *cbRead < cb {
return 1
}
return 0
}
func (i *StreamInstance) Write(pv uintptr, cb uint32, cbWritten *uint32) uintptr {
return E_ACCESSDENIED
}
func (i *StreamInstance) Seek(dlibMove LARGE_INTEGER, dwOrigin uint32, plibNewPosition *ULARGE_INTEGER) uintptr {
glog.Debug("StreamInstance Seek:", dwOrigin, dlibMove, plibNewPosition)
var newoffset uint64 = i.lOffset.QuadPart
switch dwOrigin {
case STREAM_SEEK_SET:
newoffset = uint64(dlibMove.QuadPart)
break
case STREAM_SEEK_CUR:
newoffset += uint64(dlibMove.QuadPart)
break
case STREAM_SEEK_END:
newoffset = i.lSize.QuadPart + uint64(dlibMove.QuadPart)
break
default:
return E_INVALIDARG
}
glog.Debug("StreamInstance Seek:", newoffset, i.lSize.QuadPart)
if newoffset < 0 || newoffset >= i.lSize.QuadPart {
return 1
}
i.lOffset.QuadPart = newoffset
if plibNewPosition != nil {
(*plibNewPosition).QuadPart = i.lOffset.QuadPart
}
return 0
}
func (i *StreamInstance) SetSize(libNewSize ULARGE_INTEGER) uintptr {
return E_NOTIMPL
}
func (i *StreamInstance) CopyTo(pstm *IStream, cb ULARGE_INTEGER, cbRead, cbWritten *ULARGE_INTEGER) uintptr {
return E_NOTIMPL
}
func (i *StreamInstance) Commit(grfCommitFlags uint32) uintptr {
return E_NOTIMPL
}
func (i *StreamInstance) Revert() uintptr {
return E_NOTIMPL
}
func (i *StreamInstance) LockRegion(libOffset, cb ULARGE_INTEGER, dwLockType uint32) uintptr {
return E_NOTIMPL
}
func (i *StreamInstance) UnlockRegion(libOffset, cb ULARGE_INTEGER, dwLockType uint32) uintptr {
return E_NOTIMPL
}
func (i *StreamInstance) Stat(pstatstg *STATSTG, grfStatFlag uint32) uintptr {
switch grfStatFlag {
case STATFLAG_DEFAULT:
return STG_E_INSUFFICIENTMEMORY
case STATFLAG_NONAME:
pstatstg.cbSize.QuadPart = i.lSize.QuadPart
pstatstg.grfLocksSupported = LOCK_EXCLUSIVE
pstatstg.grfMode = GENERIC_READ
pstatstg.grfStateBits = 0
pstatstg.sType = STGTY_STREAM
break
case STATFLAG_NOOPEN:
return STG_E_INVALIDFLAG
default:
return STG_E_INVALIDFLAG
}
return 0
}
func (i *StreamInstance) Clone(ppstm **IStream) uintptr {
return E_NOTIMPL
}