init: 自 zomaintain/backend/rdplib 平移独立成库; module path 从上游 github.com/nakagami/grdp 改为 git.zeroonesoft.cn/golib/rdplib
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
package cliprdr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"unicode/utf16"
|
||||
|
||||
"github.com/lunixbochs/struc"
|
||||
|
||||
"git.zeroonesoft.cn/golib/rdplib/core"
|
||||
)
|
||||
|
||||
type CliprdrClient struct {
|
||||
w core.ChannelSender
|
||||
useLongFormatNames bool
|
||||
streamFileClipEnabled bool
|
||||
fileClipNoFilePaths bool
|
||||
canLockClipData bool
|
||||
hasHugeFileSupport bool
|
||||
formatIdMap map[uint32]uint32
|
||||
reply chan []byte
|
||||
}
|
||||
|
||||
func NewCliprdrClient() *CliprdrClient {
|
||||
c := &CliprdrClient{
|
||||
formatIdMap: make(map[uint32]uint32, 20),
|
||||
reply: make(chan []byte, 100),
|
||||
}
|
||||
|
||||
go ClipWatcher(c)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
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) {
|
||||
r := bytes.NewReader(s)
|
||||
|
||||
msgType, _ := core.ReadUint16LE(r)
|
||||
flag, _ := core.ReadUint16LE(r)
|
||||
length, _ := core.ReadUInt32LE(r)
|
||||
slog.Debug(fmt.Sprintf("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:
|
||||
slog.Debug("CB_CLIP_CAPS")
|
||||
c.processClipCaps(b)
|
||||
|
||||
case CB_MONITOR_READY:
|
||||
slog.Debug("CB_MONITOR_READY")
|
||||
c.processMonitorReady(b)
|
||||
|
||||
case CB_FORMAT_LIST:
|
||||
slog.Debug("CB_FORMAT_LIST")
|
||||
c.processFormatList(b)
|
||||
|
||||
case CB_FORMAT_LIST_RESPONSE:
|
||||
slog.Debug("CB_FORMAT_LIST_RESPONSE")
|
||||
c.processFormatListResponse(flag, b)
|
||||
|
||||
case CB_FORMAT_DATA_REQUEST:
|
||||
slog.Debug("CB_FORMAT_DATA_REQUEST")
|
||||
c.processFormatDataRequest(b)
|
||||
|
||||
case CB_FORMAT_DATA_RESPONSE:
|
||||
slog.Debug("CB_FORMAT_DATA_RESPONSE")
|
||||
c.processFormatDataResponse(flag, b)
|
||||
|
||||
case CB_FILECONTENTS_REQUEST:
|
||||
slog.Debug("CB_FILECONTENTS_REQUEST")
|
||||
c.processFileContentsRequest(b)
|
||||
|
||||
case CB_FILECONTENTS_RESPONSE:
|
||||
slog.Debug("CB_FILECONTENTS_RESPONSE")
|
||||
c.processFileContentsResponse(flag, b)
|
||||
|
||||
case CB_LOCK_CLIPDATA:
|
||||
slog.Debug("CB_LOCK_CLIPDATA")
|
||||
c.processLockClipData(b)
|
||||
|
||||
case CB_UNLOCK_CLIPDATA:
|
||||
slog.Debug("CB_UNLOCK_CLIPDATA")
|
||||
c.processUnlockClipData(b)
|
||||
|
||||
default:
|
||||
slog.Error(fmt.Sprintf("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 {
|
||||
slog.Error("Failed to unpack", "error", err)
|
||||
return
|
||||
}
|
||||
slog.Debug(fmt.Sprintf("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
|
||||
slog.Debug("UseLongFormatNames", "value", c.useLongFormatNames)
|
||||
slog.Debug("StreamFileClipEnabled", "value", c.streamFileClipEnabled)
|
||||
slog.Debug("FileClipNoFilePaths", "value", c.fileClipNoFilePaths)
|
||||
slog.Debug("CanLockClipData", "value", c.canLockClipData)
|
||||
slog.Debug("HasHugeFileSupport", "value", c.hasHugeFileSupport)
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) processMonitorReady(b []byte) {
|
||||
c.sendClientCapabilitiesPDU()
|
||||
c.sendFormatListPDU()
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) processFormatList(b []byte) {
|
||||
EmptyClipboard()
|
||||
fl, _ := c.readFormatList(b)
|
||||
slog.Debug("numFormats", "count", fl.NumFormats)
|
||||
c.sendFormatListResponse(CB_RESPONSE_OK)
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) processFormatListResponse(flag uint16, b []byte) {
|
||||
if flag != CB_RESPONSE_OK {
|
||||
slog.Error("Format List Response Failed")
|
||||
return
|
||||
}
|
||||
slog.Debug("Format List Response OK")
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) processFormatDataRequest(b []byte) {
|
||||
r := bytes.NewReader(b)
|
||||
_, _ = core.ReadUInt32LE(r) // requestId
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
// Text-only: directly get clipboard data for any text format
|
||||
data := GetClipboardText()
|
||||
slog.Debug("clipboard data", "content", 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 {
|
||||
slog.Error("Format Data Response Failed")
|
||||
}
|
||||
c.reply <- b
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) processFileContentsRequest(b []byte) {
|
||||
// Text-only mode doesn't support file transfer
|
||||
slog.Debug("File transfer not supported in text-only mode")
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) processFileContentsResponse(flag uint16, b []byte) {
|
||||
// Text-only mode doesn't support file transfer
|
||||
}
|
||||
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() {
|
||||
slog.Debug("Send Client Clipboard Capabilities PDU (text-only mode)")
|
||||
var cs CliprdrGeneralCapabilitySet
|
||||
cs.CapabilitySetLength = 12
|
||||
cs.CapabilitySetType = CB_CAPSTYPE_GENERAL
|
||||
cs.Version = CB_CAPS_VERSION_2
|
||||
// Text-only mode: only use long format names
|
||||
cs.GeneralFlags = CB_USE_LONG_FORMAT_NAMES
|
||||
body := &bytes.Buffer{}
|
||||
core.WriteUInt16LE(1, body) // cCapabilitiesSets
|
||||
core.WriteUInt16LE(0, body) // pad
|
||||
struc.Pack(body, cs)
|
||||
sendClipPDU(c.w, CB_CLIP_CAPS, 0, body.Bytes())
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) sendTemporaryDirectoryPDU() {
|
||||
slog.Debug("Send Temporary Directory PDU (ignored in text-only mode)")
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) sendFormatListPDU() {
|
||||
slog.Debug("Send Format List PDU (text formats only)")
|
||||
formats := GetFormatList()
|
||||
slog.Debug("available formats", "count", len(formats), "formats", formats)
|
||||
|
||||
body := &bytes.Buffer{}
|
||||
for _, v := range formats {
|
||||
core.WriteUInt32LE(v.FormatId, body)
|
||||
if v.FormatName == "" {
|
||||
core.WriteUInt16LE(0, body)
|
||||
} else {
|
||||
n := core.UnicodeEncode(v.FormatName)
|
||||
core.WriteBytes(n, body)
|
||||
body.Write([]byte{0, 0})
|
||||
}
|
||||
}
|
||||
sendClipPDU(c.w, CB_FORMAT_LIST, 0, body.Bytes())
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) readFormatList(b []byte) (*CliprdrFormatList, bool) {
|
||||
r := bytes.NewReader(b)
|
||||
fs := make([]CliprdrFormat, 0, 20)
|
||||
var numFormats uint32 = 0
|
||||
c.formatIdMap = make(map[uint32]uint32, 0)
|
||||
for r.Len() > 0 {
|
||||
formatId, _ := core.ReadUInt32LE(r)
|
||||
bs := make([]uint16, 0, 20)
|
||||
ln := r.Len()
|
||||
for range ln {
|
||||
b, _ := core.ReadUint16LE(r)
|
||||
if b == 0 {
|
||||
break
|
||||
}
|
||||
bs = append(bs, b)
|
||||
}
|
||||
name := string(utf16.Decode(bs))
|
||||
slog.Debug(fmt.Sprintf("Format:%d Name:<%s>", formatId, name))
|
||||
if name != "" {
|
||||
localId := RegisterClipboardFormat(name)
|
||||
slog.Debug("format mapping", "local", localId, "remote", formatId)
|
||||
c.formatIdMap[localId] = formatId
|
||||
} else {
|
||||
c.formatIdMap[formatId] = formatId
|
||||
}
|
||||
|
||||
numFormats++
|
||||
fs = append(fs, CliprdrFormat{formatId, name})
|
||||
}
|
||||
|
||||
return &CliprdrFormatList{numFormats, fs}, false
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) sendFormatListResponse(flags uint16) {
|
||||
slog.Debug("Send Format List Response")
|
||||
sendClipPDU(c.w, CB_FORMAT_LIST_RESPONSE, flags, nil)
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) sendFormatDataRequest(id uint32) {
|
||||
slog.Debug("Send Format Data Request")
|
||||
body := &bytes.Buffer{}
|
||||
core.WriteUInt32LE(id, body)
|
||||
sendClipPDU(c.w, CB_FORMAT_DATA_REQUEST, 0, body.Bytes())
|
||||
}
|
||||
|
||||
func (c *CliprdrClient) sendFormatDataResponse(b []byte) {
|
||||
slog.Debug("Send Format Data Response")
|
||||
sendClipPDU(c.w, CB_FORMAT_DATA_RESPONSE, CB_RESPONSE_OK, b)
|
||||
}
|
||||
Reference in New Issue
Block a user