667 lines
15 KiB
C
667 lines
15 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they will be useful, but WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU Lesser General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with these librararies and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
/* $TOG: DndFile.c /main/5 1998/04/09 17:48:19 mgreess $ */
|
|
/*********************************************************************
|
|
*
|
|
* File: DndFile.c
|
|
*
|
|
* Description: Implementation of the File Transfer routines
|
|
* for the DND Convenience API.
|
|
*
|
|
*********************************************************************
|
|
*
|
|
*
|
|
* RESTRICTED CONFIDENTIAL INFORMATION:
|
|
*
|
|
* The information in this document is subject to special
|
|
* restrictions in a confidential disclosure agreement between
|
|
* HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
|
|
* document outside HP, IBM, Sun, USL, SCO, or Univel without
|
|
* Sun's specific written approval. This documment and all copies
|
|
* and derivative works thereof must be returned or destroyed at
|
|
* Sun's request.
|
|
*
|
|
* Copyright 1993 Sun Microsystems, Inc. All rights reserved.
|
|
*
|
|
* (c) Copyright 1993, 1994 Hewlett-Packard Company
|
|
* (c) Copyright 1993, 1994 International Business Machines Corp.
|
|
* (c) Copyright 1993, 1994 Sun Microsystems, Inc.
|
|
* (c) Copyright 1993, 1994 Novell, Inc.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <X11/Intrinsic.h>
|
|
#include <Xm/AtomMgr.h>
|
|
#include <Xm/DragC.h>
|
|
#include <Xm/DropSMgr.h>
|
|
#include <Xm/DropTrans.h>
|
|
#include <Tt/tt_c.h>
|
|
#include "Dnd.h"
|
|
#include "DndP.h"
|
|
#include "DtSvcLock.h"
|
|
|
|
/*
|
|
* File Transfer Protocol Function Prototypes
|
|
*/
|
|
|
|
static void dndFileGetTargets(Boolean, Boolean, Atom**, Cardinal*);
|
|
static void dndFileGetAvailTargets(DtDragInfo*, Atom**, Cardinal*);
|
|
static void dndFileGetExportTargets(DtDragInfo*, Atom**, Cardinal*);
|
|
static void dndFileGetImportTargets(DtDropInfo*, Atom**, Cardinal*);
|
|
static void dndFileConvertInit(DtDragInfo*);
|
|
static Boolean dndFileConvert(Widget, DtDragInfo*, Atom*, Atom*,
|
|
Atom*, XtPointer*, unsigned long*, int*,
|
|
XSelectionRequestEvent*);
|
|
static void dndFileConvertFinish(DtDragInfo*);
|
|
static void dndFileTransferTargets(DtDropInfo*,
|
|
Atom*, Cardinal, Atom**, Cardinal*);
|
|
static void dndFileTransfer(Widget, DtDropInfo*, Atom*, Atom*, Atom*,
|
|
XtPointer, unsigned long*, int*);
|
|
static void dndFileTransferFinish(DtDropInfo*);
|
|
|
|
/*
|
|
* File Transfer Support Functions
|
|
*/
|
|
|
|
static Boolean dndFileContentsToSelectionValue(String,
|
|
Atom*, XtPointer*, unsigned long*, int*);
|
|
static Boolean dndFileListToSelectionValue(String*, Cardinal, Boolean,
|
|
Atom*, XtPointer*, unsigned long*, int*);
|
|
static Boolean dndSelectionValueToFileList(Atom, XtPointer, unsigned long, int,
|
|
Boolean, String**, Cardinal*);
|
|
static String dndFileEncode(String, Boolean);
|
|
static String dndFileDecode(String, Boolean);
|
|
|
|
/*
|
|
* File Transfer Selection Targets
|
|
*/
|
|
|
|
static Atom XA_FILE_NAME;
|
|
static Atom XA_DT_NETFILE;
|
|
|
|
/*
|
|
* File Transfer Protocol Methods
|
|
*/
|
|
|
|
static DtDndMethods dndFileTransferProtocol = {
|
|
"DtDndFileNameTransfer", /* name */
|
|
(DtDndProtocol)DtDND_FILENAME_TRANSFER, /* protocol */
|
|
DtDND_DRAG_SOURCE_DATA, /* sourceType */
|
|
dndFileGetAvailTargets, /* getAvailTargets */
|
|
dndFileGetExportTargets, /* getExportTargets */
|
|
dndFileGetImportTargets, /* getImportTargets */
|
|
dndFileConvertInit, /* convertInit */
|
|
dndFileConvert, /* convert */
|
|
dndFileConvertFinish, /* convertFinish */
|
|
dndFileTransferTargets, /* transferTargets */
|
|
dndFileTransfer, /* transfer */
|
|
dndFileTransferFinish, /* transferFinish */
|
|
};
|
|
|
|
typedef struct _ConvertState {
|
|
Boolean owCompat;
|
|
} ConvertState;
|
|
|
|
/*
|
|
* File transfer protocol initialization
|
|
*/
|
|
DtDndMethods *
|
|
_DtDndFileTransferProtocolInitialize(
|
|
Display * dpy)
|
|
{
|
|
_DtSvcProcessLock();
|
|
if (XA_FILE_NAME == 0) {
|
|
XA_FILE_NAME = DtGetAtom(dpy,"FILE_NAME");
|
|
XA_DT_NETFILE = DtGetAtom(dpy,"_DT_NETFILE");
|
|
}
|
|
|
|
_DtSvcProcessUnlock();
|
|
return &dndFileTransferProtocol;
|
|
}
|
|
|
|
/*
|
|
* Returns generic export/import targets for filename transfers
|
|
*/
|
|
static void
|
|
dndFileGetTargets(
|
|
Boolean doHost,
|
|
Boolean owCompat,
|
|
Atom ** targets,
|
|
Cardinal * numTargets)
|
|
{
|
|
int ii = 0;
|
|
|
|
*numTargets = 2 + (doHost ? 1 : 0) + (owCompat ? 4 : 0);
|
|
|
|
*targets = (Atom *)XtMalloc(*numTargets * sizeof(Atom));
|
|
|
|
(*targets)[ii++] = XA_DT_NETFILE;
|
|
(*targets)[ii++] = XA_FILE_NAME;
|
|
|
|
if (doHost) {
|
|
(*targets)[ii++] = XA_HOST_NAME;
|
|
}
|
|
|
|
if (owCompat) {
|
|
(*targets)[ii++] = XA_TEXT;
|
|
(*targets)[ii++] = XA_STRING;
|
|
(*targets)[ii++] = XA_SUN_DATA_LABEL;
|
|
(*targets)[ii++] = XA_SUN_ATM_METHODS;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns available targets for filename transfers
|
|
*/
|
|
static void
|
|
dndFileGetAvailTargets(
|
|
DtDragInfo * dtDragInfo,
|
|
Atom ** availTargets,
|
|
Cardinal * numAvailTargets)
|
|
{
|
|
dndFileGetTargets(True, True, availTargets, numAvailTargets);
|
|
}
|
|
|
|
/*
|
|
* Returns export targets for filename transfers
|
|
*/
|
|
static void
|
|
dndFileGetExportTargets(
|
|
DtDragInfo * dtDragInfo,
|
|
Atom ** exportTargets,
|
|
Cardinal * numExportTargets)
|
|
{
|
|
dndFileGetTargets(True, False, exportTargets, numExportTargets);
|
|
}
|
|
|
|
/*
|
|
* Returns import targets for filename transfers
|
|
*/
|
|
static void
|
|
dndFileGetImportTargets(
|
|
DtDropInfo * dtDropInfo,
|
|
Atom ** importTargets,
|
|
Cardinal * numImportTargets)
|
|
{
|
|
dndFileGetTargets(False, False, importTargets, numImportTargets);
|
|
}
|
|
|
|
/*
|
|
* Initialize protocol specific part of drag data
|
|
*/
|
|
static void
|
|
dndFileConvertInit(
|
|
DtDragInfo * dtDragInfo)
|
|
{
|
|
DtDndContext * dragData = dtDragInfo->dragData;
|
|
ConvertState * cvtState;
|
|
|
|
dragData->data.files = (String *)
|
|
XtMalloc(dragData->numItems * sizeof(String));
|
|
|
|
cvtState = (ConvertState *)XtCalloc(1,sizeof(ConvertState));
|
|
dtDragInfo->clientData = (XtPointer)cvtState;
|
|
}
|
|
|
|
/*
|
|
* Convert the file names into selection data
|
|
*/
|
|
static Boolean
|
|
dndFileConvert(
|
|
Widget dragContext,
|
|
DtDragInfo * dtDragInfo,
|
|
Atom * selection,
|
|
Atom * target,
|
|
Atom * returnType,
|
|
XtPointer * returnValue,
|
|
unsigned long * returnLength,
|
|
int * returnFormat,
|
|
XSelectionRequestEvent * selectionRequestEvent)
|
|
{
|
|
DtDndContext * dragData = dtDragInfo->dragData;
|
|
ConvertState * cvtState = (ConvertState *)dtDragInfo->clientData;
|
|
|
|
*returnType = XA_NULL;
|
|
*returnValue = (XtPointer)NULL;
|
|
*returnLength = 0;
|
|
*returnFormat = 8;
|
|
|
|
/*
|
|
* Determine file encoding style.
|
|
* Handle Sun ATM file name.
|
|
* Reject unknown targets.
|
|
*/
|
|
|
|
if (*target == XA_FILE_NAME || *target == XA_DT_NETFILE) {
|
|
|
|
if (!dndFileListToSelectionValue(
|
|
dragData->data.files, dragData->numItems,
|
|
(*target == XA_DT_NETFILE),
|
|
returnType, returnValue,
|
|
returnLength, returnFormat)) {
|
|
return False;
|
|
}
|
|
|
|
} else if (*target == XA_TEXT || *target == XA_STRING) {
|
|
|
|
if (dragData->numItems > 1 || !cvtState->owCompat)
|
|
return False;
|
|
|
|
if (!dndFileContentsToSelectionValue(dragData->data.files[0],
|
|
returnType, returnValue,
|
|
returnLength, returnFormat)) {
|
|
return False;
|
|
}
|
|
|
|
} else if (*target == XA_SUN_SELN_READONLY) {
|
|
|
|
cvtState->owCompat = True;
|
|
|
|
} else if (*target == XA_SUN_DATA_LABEL) {
|
|
String name;
|
|
|
|
if (dragData->numItems > 1)
|
|
return False;
|
|
|
|
if ((name = strrchr(dragData->data.files[0],'/')) != NULL)
|
|
name++;
|
|
else
|
|
name = dragData->data.files[0];
|
|
|
|
*returnType = XA_STRING;
|
|
*returnValue = (XtPointer)XtNewString(name);
|
|
*returnLength = strlen(*returnValue);
|
|
*returnFormat = 8;
|
|
|
|
cvtState->owCompat = True;
|
|
|
|
} else if (*target == XA_SUN_ATM_METHODS) {
|
|
|
|
Atom *atmAtom = XtNew(Atom);
|
|
|
|
atmAtom[0] = XA_SUN_ATM_FILE_NAME;
|
|
|
|
*returnType = XA_ATOM;
|
|
*returnValue = (XtPointer)atmAtom;
|
|
*returnLength = 1;
|
|
*returnFormat = 32;
|
|
|
|
cvtState->owCompat = True;
|
|
|
|
} else {
|
|
return False;
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
/*
|
|
* Clean up from the convert init proc
|
|
*/
|
|
static void
|
|
dndFileConvertFinish(
|
|
DtDragInfo * dtDragInfo)
|
|
{
|
|
DtDndContext * dragData = dtDragInfo->dragData;
|
|
|
|
if (dragData->data.files) {
|
|
XtFree((char *)dragData->data.files);
|
|
dragData->data.files = NULL;
|
|
}
|
|
if (dtDragInfo->clientData)
|
|
XtFree((char *)dtDragInfo->clientData);
|
|
}
|
|
|
|
/*
|
|
* Returns the transfer targets selected from the export targets
|
|
* Prefer _DT_NETFILE over FILE_NAME; prefer local host file names
|
|
*/
|
|
static void
|
|
dndFileTransferTargets(
|
|
DtDropInfo * dtDropInfo,
|
|
Atom * exportTargets,
|
|
Cardinal numExportTargets,
|
|
Atom ** transferTargets,
|
|
Cardinal * numTransferTargets)
|
|
{
|
|
Boolean foundNetFile, foundFileName, foundHostName;
|
|
Atom target;
|
|
int ii;
|
|
|
|
foundNetFile = foundFileName = foundHostName = False;
|
|
|
|
for (ii = 0; ii < numExportTargets; ii++) {
|
|
if (exportTargets[ii] == XA_DT_NETFILE) {
|
|
foundNetFile = True;
|
|
} else if (exportTargets[ii] == XA_FILE_NAME) {
|
|
foundFileName = True;
|
|
} else if (exportTargets[ii] == XA_HOST_NAME) {
|
|
foundHostName = True;
|
|
}
|
|
}
|
|
|
|
if (foundNetFile && foundFileName && foundHostName) {
|
|
target = XA_HOST_NAME;
|
|
} else if (foundNetFile) {
|
|
target = XA_DT_NETFILE;
|
|
} else if (foundFileName) {
|
|
target = XA_FILE_NAME;
|
|
} else {
|
|
*numTransferTargets = 0;
|
|
*transferTargets = NULL;
|
|
return;
|
|
}
|
|
|
|
*numTransferTargets = 1;
|
|
|
|
*transferTargets = (Atom *)XtMalloc(*numTransferTargets * sizeof(Atom));
|
|
|
|
(*transferTargets)[0] = target;
|
|
}
|
|
|
|
/*
|
|
* Transfer the selection data into file names
|
|
*/
|
|
static void
|
|
dndFileTransfer(
|
|
Widget dropTransfer,
|
|
DtDropInfo * dtDropInfo,
|
|
Atom * selection,
|
|
Atom * target,
|
|
Atom * type,
|
|
XtPointer value,
|
|
unsigned long * length,
|
|
int * format)
|
|
{
|
|
DtDndContext * dropData = dtDropInfo->dropData;
|
|
|
|
/*
|
|
* Ignore if we've already transferred
|
|
*/
|
|
|
|
if (dropData->data.files) {
|
|
XtFree(value);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* If hosts are the same then request FILE_NAME else request NETFILE
|
|
*/
|
|
if (*target == XA_HOST_NAME) {
|
|
Atom target;
|
|
|
|
if (strcmp(_DtDndGetHostName(),(char *)value) == 0)
|
|
target = XA_FILE_NAME;
|
|
else
|
|
target = XA_DT_NETFILE;
|
|
|
|
_DtDndTransferAdd(dropTransfer, dtDropInfo, &target, 1);
|
|
|
|
/*
|
|
* Convert NETFILE or FILE_NAME selection to file list
|
|
*/
|
|
} else if (*target == XA_DT_NETFILE || *target == XA_FILE_NAME) {
|
|
|
|
if (!dndSelectionValueToFileList(*type, value,
|
|
*length, *format, (*target == XA_DT_NETFILE),
|
|
&(dropData->data.files), &(dropData->numItems))) {
|
|
|
|
dtDropInfo->status = DtDND_FAILURE;
|
|
}
|
|
}
|
|
|
|
if (value != NULL)
|
|
XtFree(value);
|
|
}
|
|
|
|
/*
|
|
* Clean up from the transfer proc
|
|
*/
|
|
static void
|
|
dndFileTransferFinish(
|
|
DtDropInfo * dtDropInfo)
|
|
{
|
|
DtDndContext * dropData = dtDropInfo->dropData;
|
|
int ii;
|
|
|
|
for (ii = 0; ii < dropData->numItems; ii++) {
|
|
XtFree((char *)dropData->data.files[ii]);
|
|
}
|
|
XtFree((char *)dropData->data.files);
|
|
}
|
|
|
|
/*
|
|
* Convert the file contents into a STRING selection
|
|
*/
|
|
static Boolean
|
|
dndFileContentsToSelectionValue(
|
|
String fileName,
|
|
Atom * returnType,
|
|
XtPointer * returnValue,
|
|
unsigned long * returnLength,
|
|
int * returnFormat)
|
|
{
|
|
String fullPath;
|
|
struct stat stBuf;
|
|
int fd;
|
|
unsigned long bufLen, bytesRead;
|
|
char * buf;
|
|
|
|
fullPath = dndFileEncode(fileName, False);
|
|
|
|
if (stat(fullPath,&stBuf) == -1)
|
|
return False;
|
|
|
|
if ((fd = open(fullPath, O_RDONLY)) == -1)
|
|
return False;
|
|
|
|
bufLen = stBuf.st_size;
|
|
buf = (void *)XtMalloc(bufLen + 1);
|
|
|
|
bytesRead = read(fd, buf, bufLen);
|
|
|
|
if (bytesRead == -1 || bytesRead != bufLen) {
|
|
XtFree((char *)buf);
|
|
return False;
|
|
}
|
|
|
|
buf[bufLen] = '\0';
|
|
|
|
*returnType = XA_STRING;
|
|
*returnValue = (XtPointer)buf;
|
|
*returnLength = bufLen;
|
|
*returnFormat = 8;
|
|
|
|
XtFree(fullPath);
|
|
|
|
return True;
|
|
}
|
|
|
|
/*
|
|
* Convert the filename list into a STRING selection
|
|
*/
|
|
static Boolean
|
|
dndFileListToSelectionValue(
|
|
String * fileList,
|
|
Cardinal numFiles,
|
|
Boolean doNetFile,
|
|
Atom * returnType,
|
|
XtPointer * returnValue,
|
|
unsigned long * returnLength,
|
|
int * returnFormat)
|
|
{
|
|
XTextProperty textProp;
|
|
Status status;
|
|
String * tmpList;
|
|
Cardinal ii;
|
|
|
|
/*
|
|
* Encode the file list
|
|
*/
|
|
|
|
tmpList = (String *)XtMalloc(numFiles * sizeof(String));
|
|
|
|
for (ii = 0; ii < numFiles; ii++) {
|
|
tmpList[ii] = dndFileEncode(fileList[ii], doNetFile);
|
|
}
|
|
|
|
/*
|
|
* Convert the encoded file list into a string property
|
|
*/
|
|
|
|
status = XStringListToTextProperty(tmpList, numFiles, &textProp);
|
|
|
|
for (ii = 0; ii < numFiles; ii++) {
|
|
XtFree(tmpList[ii]);
|
|
}
|
|
|
|
XtFree((char *)tmpList);
|
|
|
|
if (status == 0)
|
|
return False;
|
|
|
|
*returnType = textProp.encoding;
|
|
*returnValue = (XtPointer)textProp.value;
|
|
*returnLength = textProp.nitems;
|
|
*returnFormat = textProp.format;
|
|
|
|
return True;
|
|
}
|
|
|
|
/*
|
|
* Convert the STRING selection into a filename list
|
|
*/
|
|
static Boolean
|
|
dndSelectionValueToFileList(
|
|
Atom type,
|
|
XtPointer value,
|
|
unsigned long length,
|
|
int format,
|
|
Boolean doNetFile,
|
|
String ** returnFileList,
|
|
Cardinal * returnNumFiles)
|
|
{
|
|
XTextProperty textProp;
|
|
Status status;
|
|
String * tmpList;
|
|
String * fileList;
|
|
int ii, numFiles;
|
|
|
|
/*
|
|
* Convert text prop to file list
|
|
*/
|
|
|
|
textProp.encoding = type;
|
|
textProp.value = (unsigned char *)value;
|
|
textProp.nitems = length;
|
|
textProp.format = format;
|
|
|
|
status = XTextPropertyToStringList(&textProp, &tmpList, &numFiles);
|
|
|
|
if (status == 0)
|
|
return False;
|
|
|
|
/*
|
|
* Decode the file list
|
|
*/
|
|
|
|
fileList = (String *)XtMalloc(numFiles * sizeof(String));
|
|
|
|
for (ii = 0; ii < numFiles; ii++) {
|
|
fileList[ii] = dndFileDecode(tmpList[ii], doNetFile);
|
|
}
|
|
|
|
XFreeStringList(tmpList);
|
|
|
|
*returnFileList = fileList;
|
|
*returnNumFiles = numFiles;
|
|
|
|
return True;
|
|
}
|
|
|
|
/*
|
|
* Encodes a file name
|
|
* Either into ToolTalk NetFile or into a full path if needed
|
|
*/
|
|
static String
|
|
dndFileEncode(
|
|
String fileName,
|
|
Boolean doNetFile)
|
|
{
|
|
String netFile;
|
|
String retFile;
|
|
|
|
if (doNetFile) {
|
|
netFile = tt_file_netfile(fileName);
|
|
if (tt_ptr_error(netFile) == TT_OK) {
|
|
retFile = XtNewString(netFile);
|
|
} else {
|
|
retFile = XtNewString(fileName);
|
|
}
|
|
tt_free(netFile);
|
|
} else if (fileName[0] != '/') {
|
|
char cwd[MAXPATHLEN];
|
|
char *realPath;
|
|
|
|
if (getcwd(cwd,MAXPATHLEN) == NULL) {
|
|
strcpy(cwd,"/");
|
|
}
|
|
realPath = XtMalloc(strlen(cwd) + strlen(fileName) + 2);
|
|
sprintf(realPath,"%s/%s",cwd,fileName);
|
|
retFile = realPath;
|
|
} else {
|
|
retFile = XtNewString(fileName);
|
|
}
|
|
return retFile;
|
|
}
|
|
|
|
/*
|
|
* Decodes a file name; possibly from a ToolTalk NetFile
|
|
*/
|
|
static String
|
|
dndFileDecode(
|
|
String fileName,
|
|
Boolean doNetFile)
|
|
{
|
|
String file;
|
|
String retFile;
|
|
|
|
if (doNetFile) {
|
|
file = tt_netfile_file(fileName);
|
|
if (tt_ptr_error(file) == TT_OK) {
|
|
retFile = XtNewString(file);
|
|
} else {
|
|
retFile = XtNewString(fileName);
|
|
}
|
|
tt_free(file);
|
|
} else {
|
|
retFile = XtNewString(fileName);
|
|
}
|
|
return retFile;
|
|
}
|