Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / lib / DtSvc / DtUtil1 / DndFile.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: DndFile.c /main/5 1998/04/09 17:48:19 mgreess $ */
24 /*********************************************************************
25  *
26  *      File:           DndFile.c
27  *
28  *      Description:    Implementation of the File Transfer routines
29  *                      for the DND Convenience API.
30  *
31  *********************************************************************
32  *
33  *
34  *      RESTRICTED CONFIDENTIAL INFORMATION:
35  *      
36  *      The information in this document is subject to special
37  *      restrictions in a confidential disclosure agreement between
38  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
39  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
40  *      Sun's specific written approval.  This documment and all copies
41  *      and derivative works thereof must be returned or destroyed at
42  *      Sun's request.
43  *
44  *      Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
45  *
46  * (c) Copyright 1993, 1994 Hewlett-Packard Company
47  * (c) Copyright 1993, 1994 International Business Machines Corp.
48  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.
49  * (c) Copyright 1993, 1994 Novell, Inc.
50  */
51
52 #include <stdlib.h>
53 #include <unistd.h>
54 #include <fcntl.h>
55 #include <sys/param.h>
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 #include <X11/Intrinsic.h>
59 #include <Xm/AtomMgr.h>
60 #include <Xm/DragC.h>
61 #include <Xm/DropSMgr.h>
62 #include <Xm/DropTrans.h>
63 #include <Tt/tt_c.h>
64 #include "Dnd.h"
65 #include "DndP.h"
66 #include "DtSvcLock.h"
67
68 /* 
69  * File Transfer Protocol Function Prototypes
70  */
71
72 static void     dndFileGetTargets(Boolean, Boolean, Atom**, Cardinal*);
73 static void     dndFileGetAvailTargets(DtDragInfo*, Atom**, Cardinal*);
74 static void     dndFileGetExportTargets(DtDragInfo*, Atom**, Cardinal*);
75 static void     dndFileGetImportTargets(DtDropInfo*, Atom**, Cardinal*);
76 static void     dndFileConvertInit(DtDragInfo*);
77 static Boolean  dndFileConvert(Widget, DtDragInfo*, Atom*, Atom*,
78                         Atom*, XtPointer*, unsigned long*, int*, 
79                         XSelectionRequestEvent*);
80 static void     dndFileConvertFinish(DtDragInfo*);
81 static void     dndFileTransferTargets(DtDropInfo*,
82                         Atom*, Cardinal, Atom**, Cardinal*);
83 static void     dndFileTransfer(Widget, DtDropInfo*, Atom*, Atom*, Atom*,
84                         XtPointer, unsigned long*, int*);
85 static void     dndFileTransferFinish(DtDropInfo*);
86
87 /* 
88  * File Transfer Support Functions
89  */
90
91 static Boolean  dndFileContentsToSelectionValue(String,
92                         Atom*, XtPointer*, unsigned long*, int*);
93 static Boolean  dndFileListToSelectionValue(String*, Cardinal, Boolean,
94                         Atom*, XtPointer*, unsigned long*, int*);
95 static Boolean  dndSelectionValueToFileList(Atom, XtPointer, unsigned long, int,
96                         Boolean, String**, Cardinal*);
97 static String   dndFileEncode(String, Boolean);
98 static String   dndFileDecode(String, Boolean);
99
100 /* 
101  * File Transfer Selection Targets
102  */
103
104 static Atom     XA_FILE_NAME;
105 static Atom     XA_DT_NETFILE;
106
107 /* 
108  * File Transfer Protocol Methods
109  */
110
111 static DtDndMethods dndFileTransferProtocol = {
112         "DtDndFileNameTransfer",                /* name */
113         (DtDndProtocol)DtDND_FILENAME_TRANSFER, /* protocol */
114         DtDND_DRAG_SOURCE_DATA,                 /* sourceType */
115         dndFileGetAvailTargets,                 /* getAvailTargets */
116         dndFileGetExportTargets,                /* getExportTargets */
117         dndFileGetImportTargets,                /* getImportTargets */
118         dndFileConvertInit,                     /* convertInit */
119         dndFileConvert,                         /* convert */
120         dndFileConvertFinish,                   /* convertFinish */
121         dndFileTransferTargets,                 /* transferTargets */
122         dndFileTransfer,                        /* transfer */
123         dndFileTransferFinish,                  /* transferFinish */
124 };
125
126 typedef struct _ConvertState {
127         Boolean         owCompat;
128 } ConvertState;
129
130 /*
131  * File transfer protocol initialization
132  */
133 DtDndMethods *
134 _DtDndFileTransferProtocolInitialize(
135         Display *       dpy)
136 {
137         _DtSvcProcessLock();
138         if (XA_FILE_NAME == 0) {
139                 XA_FILE_NAME         = DtGetAtom(dpy,"FILE_NAME");
140                 XA_DT_NETFILE        = DtGetAtom(dpy,"_DT_NETFILE");
141         }
142
143         _DtSvcProcessUnlock();
144         return &dndFileTransferProtocol;
145 }
146
147 /*
148  * Returns generic export/import targets for filename transfers
149  */
150 static void
151 dndFileGetTargets(
152         Boolean         doHost,
153         Boolean         owCompat,
154         Atom **         targets,
155         Cardinal *      numTargets)
156 {
157         int             ii = 0;
158
159         *numTargets = 2 + (doHost ? 1 : 0) + (owCompat ? 4 : 0);
160
161         *targets = (Atom *)XtMalloc(*numTargets * sizeof(Atom));
162
163         (*targets)[ii++] = XA_DT_NETFILE;
164         (*targets)[ii++] = XA_FILE_NAME;
165
166         if (doHost) {
167                 (*targets)[ii++] = XA_HOST_NAME;
168         }
169
170         if (owCompat) {
171                 (*targets)[ii++] = XA_TEXT;
172                 (*targets)[ii++] = XA_STRING;
173                 (*targets)[ii++] = XA_SUN_DATA_LABEL;
174                 (*targets)[ii++] = XA_SUN_ATM_METHODS;
175         }
176 }
177
178 /*
179  * Returns available targets for filename transfers
180  */
181 static void
182 dndFileGetAvailTargets(
183         DtDragInfo *    dtDragInfo,
184         Atom **         availTargets,
185         Cardinal *      numAvailTargets)
186 {
187         dndFileGetTargets(True, True, availTargets, numAvailTargets);
188 }
189
190 /*
191  * Returns export targets for filename transfers
192  */
193 static void
194 dndFileGetExportTargets(
195         DtDragInfo *    dtDragInfo,
196         Atom **         exportTargets,
197         Cardinal *      numExportTargets)
198 {
199         dndFileGetTargets(True, False, exportTargets, numExportTargets);
200 }
201
202 /*
203  * Returns import targets for filename transfers
204  */
205 static void
206 dndFileGetImportTargets(
207         DtDropInfo *    dtDropInfo,
208         Atom **         importTargets,
209         Cardinal *      numImportTargets)
210 {
211         dndFileGetTargets(False, False, importTargets, numImportTargets);
212 }
213
214 /*
215  * Initialize protocol specific part of drag data
216  */
217 static void
218 dndFileConvertInit(
219         DtDragInfo *    dtDragInfo)
220 {
221         DtDndContext *  dragData = dtDragInfo->dragData;
222         ConvertState *  cvtState;
223
224         dragData->data.files = (String *)
225                 XtMalloc(dragData->numItems * sizeof(String));
226
227         cvtState = (ConvertState *)XtCalloc(1,sizeof(ConvertState));
228         dtDragInfo->clientData = (XtPointer)cvtState;
229 }
230
231 /*
232  * Convert the file names into selection data
233  */
234 static Boolean
235 dndFileConvert(
236         Widget          dragContext,
237         DtDragInfo *    dtDragInfo,
238         Atom *          selection,
239         Atom *          target,  
240         Atom *          returnType,
241         XtPointer *     returnValue,
242         unsigned long * returnLength,
243         int *           returnFormat,
244         XSelectionRequestEvent * selectionRequestEvent)
245 {
246         DtDndContext *  dragData = dtDragInfo->dragData;
247         ConvertState *  cvtState = (ConvertState *)dtDragInfo->clientData;
248
249         *returnType     = XA_NULL;
250         *returnValue    = (XtPointer)NULL;
251         *returnLength   = 0;
252         *returnFormat   = 8;
253
254         /*
255          * Determine file encoding style.
256          * Handle Sun ATM file name.
257          * Reject unknown targets.
258          */
259
260         if (*target == XA_FILE_NAME || *target == XA_DT_NETFILE) {
261
262                 if (!dndFileListToSelectionValue(
263                                 dragData->data.files, dragData->numItems,
264                                 (*target == XA_DT_NETFILE), 
265                                 returnType, returnValue, 
266                                 returnLength, returnFormat)) {
267                         return False;
268                 }
269
270         } else if (*target == XA_TEXT || *target == XA_STRING) {
271
272                 if (dragData->numItems > 1 || !cvtState->owCompat)
273                         return False;
274
275                 if (!dndFileContentsToSelectionValue(dragData->data.files[0],
276                                 returnType, returnValue,
277                                 returnLength, returnFormat)) {
278                         return False;
279                 }
280                 
281         } else if (*target == XA_SUN_SELN_READONLY) {
282
283                 cvtState->owCompat = True;
284
285         } else if (*target == XA_SUN_DATA_LABEL) {
286                 String          name;
287
288                 if (dragData->numItems > 1)
289                         return False;
290
291                 if ((name = strrchr(dragData->data.files[0],'/')) != NULL)
292                         name++;
293                 else
294                         name = dragData->data.files[0];
295
296                 *returnType     = XA_STRING;
297                 *returnValue    = (XtPointer)XtNewString(name);
298                 *returnLength   = strlen(*returnValue);
299                 *returnFormat   = 8;
300
301                 cvtState->owCompat = True;
302
303         } else if (*target == XA_SUN_ATM_METHODS) {
304
305                 Atom *atmAtom   = XtNew(Atom);
306
307                 atmAtom[0]      = XA_SUN_ATM_FILE_NAME;
308         
309                 *returnType     = XA_ATOM;
310                 *returnValue    = (XtPointer)atmAtom;
311                 *returnLength   = 1;
312                 *returnFormat   = 32;
313
314                 cvtState->owCompat = True;
315
316         } else {
317                 return False;
318         }
319
320         return True;
321 }
322
323 /*
324  * Clean up from the convert init proc
325  */
326 static void
327 dndFileConvertFinish(
328         DtDragInfo *    dtDragInfo)
329 {
330         DtDndContext *  dragData = dtDragInfo->dragData;
331
332         if (dragData->data.files) {
333                 XtFree((char *)dragData->data.files);
334                 dragData->data.files = NULL;
335         }
336         if (dtDragInfo->clientData)
337                 XtFree((char *)dtDragInfo->clientData);
338 }
339
340 /*
341  * Returns the transfer targets selected from the export targets
342  * Prefer _DT_NETFILE over FILE_NAME; prefer local host file names
343  */
344 static void
345 dndFileTransferTargets(
346         DtDropInfo *    dtDropInfo,
347         Atom *          exportTargets,
348         Cardinal        numExportTargets,
349         Atom **         transferTargets,
350         Cardinal *      numTransferTargets)
351 {
352         Boolean         foundNetFile, foundFileName, foundHostName;
353         Atom            target;
354         int             ii;
355
356         foundNetFile = foundFileName = foundHostName = False;
357
358         for (ii = 0; ii < numExportTargets; ii++) {
359                 if (exportTargets[ii] == XA_DT_NETFILE) {
360                         foundNetFile = True;
361                 } else if (exportTargets[ii] == XA_FILE_NAME) {
362                         foundFileName = True;
363                 } else if (exportTargets[ii] == XA_HOST_NAME) {
364                         foundHostName = True;
365                 }
366         }
367
368         if (foundNetFile && foundFileName && foundHostName) {
369                 target = XA_HOST_NAME;
370         } else if (foundNetFile) {
371                 target = XA_DT_NETFILE;
372         } else if (foundFileName) {
373                 target = XA_FILE_NAME;
374         } else {
375                 *numTransferTargets     = 0;
376                 *transferTargets        = NULL;
377                 return;
378         }
379  
380         *numTransferTargets = 1;
381
382         *transferTargets = (Atom *)XtMalloc(*numTransferTargets * sizeof(Atom));
383
384         (*transferTargets)[0] = target;
385 }
386
387 /*
388  * Transfer the selection data into file names
389  */
390 static void
391 dndFileTransfer(
392         Widget          dropTransfer,
393         DtDropInfo *    dtDropInfo,
394         Atom *          selection,
395         Atom *          target,
396         Atom *          type,
397         XtPointer       value,
398         unsigned long * length,
399         int *           format)
400 {
401         DtDndContext *  dropData  = dtDropInfo->dropData;
402
403         /*
404          * Ignore if we've already transferred
405          */
406
407         if (dropData->data.files) {
408                 XtFree(value);
409                 return;
410         }
411
412         /*
413          * If hosts are the same then request FILE_NAME else request NETFILE
414          */
415         if (*target == XA_HOST_NAME) {
416                 Atom            target;
417
418                 if (strcmp(_DtDndGetHostName(),(char *)value) == 0)
419                         target = XA_FILE_NAME;
420                 else
421                         target = XA_DT_NETFILE;
422
423                 _DtDndTransferAdd(dropTransfer, dtDropInfo, &target, 1);
424
425         /*
426          * Convert NETFILE or FILE_NAME selection to file list
427          */
428         } else if (*target == XA_DT_NETFILE || *target == XA_FILE_NAME) {
429
430                 if (!dndSelectionValueToFileList(*type, value, 
431                         *length, *format, (*target == XA_DT_NETFILE),
432                         &(dropData->data.files), &(dropData->numItems))) {
433
434                         dtDropInfo->status = DtDND_FAILURE;
435                 }
436         }
437
438         if (value != NULL)
439                 XtFree(value);
440 }
441
442 /*
443  * Clean up from the transfer proc
444  */
445 static void
446 dndFileTransferFinish(
447         DtDropInfo *    dtDropInfo)
448 {
449         DtDndContext *  dropData = dtDropInfo->dropData;
450         int             ii;
451
452         for (ii = 0; ii < dropData->numItems; ii++) {
453                 XtFree((char *)dropData->data.files[ii]);
454         }
455         XtFree((char *)dropData->data.files);
456 }
457
458 /*
459  * Convert the file contents into a STRING selection
460  */
461 static Boolean
462 dndFileContentsToSelectionValue(
463         String          fileName,
464         Atom *          returnType,
465         XtPointer *     returnValue,
466         unsigned long * returnLength,
467         int *           returnFormat)
468 {
469         String          fullPath;
470         struct stat     stBuf;
471         int             fd;
472         unsigned long   bufLen, bytesRead;
473         char *          buf;
474
475         fullPath = dndFileEncode(fileName, False);
476
477         if (stat(fullPath,&stBuf) == -1)
478                 return False;
479
480         if ((fd = open(fullPath, O_RDONLY)) == -1)
481                 return False;
482
483         bufLen = stBuf.st_size;
484         buf = (void *)XtMalloc(bufLen + 1);
485
486         bytesRead = read(fd, buf, bufLen);
487
488         if (bytesRead == -1 || bytesRead != bufLen) {
489                 XtFree((char *)buf);
490                 return False;
491         }
492
493         buf[bufLen] = '\0';
494
495         *returnType     = XA_STRING;
496         *returnValue    = (XtPointer)buf;
497         *returnLength   = bufLen;
498         *returnFormat   = 8;
499
500         XtFree(fullPath);
501
502         return True;
503 }
504
505 /*
506  * Convert the filename list into a STRING selection
507  */
508 static Boolean
509 dndFileListToSelectionValue(
510         String *        fileList,
511         Cardinal        numFiles,
512         Boolean         doNetFile,
513         Atom *          returnType,
514         XtPointer *     returnValue,
515         unsigned long * returnLength,
516         int *           returnFormat)
517 {
518         XTextProperty   textProp;
519         Status          status;
520         String *        tmpList;
521         Cardinal        ii;
522
523         /*
524          * Encode the file list
525          */
526
527         tmpList = (String *)XtMalloc(numFiles * sizeof(String));
528
529         for (ii = 0; ii < numFiles; ii++) {
530                 tmpList[ii] = dndFileEncode(fileList[ii], doNetFile);
531         }
532
533         /*
534          * Convert the encoded file list into a string property
535          */
536
537         status = XStringListToTextProperty(tmpList, numFiles, &textProp);
538
539         for (ii = 0; ii < numFiles; ii++) {
540                 XtFree(tmpList[ii]);
541         }
542
543         XtFree((char *)tmpList);
544
545         if (status == 0) 
546                 return False;
547
548         *returnType     = textProp.encoding;
549         *returnValue    = (XtPointer)textProp.value;
550         *returnLength   = textProp.nitems;
551         *returnFormat   = textProp.format;
552
553         return True;
554 }
555
556 /*
557  * Convert the STRING selection into a filename list
558  */
559 static Boolean
560 dndSelectionValueToFileList(
561         Atom            type,
562         XtPointer       value,
563         unsigned long   length,
564         int             format,
565         Boolean         doNetFile,
566         String **       returnFileList,
567         Cardinal *      returnNumFiles)
568 {
569         XTextProperty   textProp;
570         Status          status;
571         String *        tmpList;
572         String *        fileList;
573         int             ii, numFiles;
574
575         /*
576          * Convert text prop to file list
577          */
578
579         textProp.encoding       = type;
580         textProp.value          = (unsigned char *)value;
581         textProp.nitems         = length;
582         textProp.format         = format;
583
584         status = XTextPropertyToStringList(&textProp, &tmpList, &numFiles);
585
586         if (status == 0)
587                 return False;
588
589         /*
590          * Decode the file list
591          */
592
593         fileList = (String *)XtMalloc(numFiles * sizeof(String));
594
595         for (ii = 0; ii < numFiles; ii++) {
596                 fileList[ii] = dndFileDecode(tmpList[ii], doNetFile);
597         }
598
599         XFreeStringList(tmpList);
600
601         *returnFileList = fileList;
602         *returnNumFiles = numFiles;
603
604         return True;
605 }
606
607 /*
608  * Encodes a file name
609  * Either into ToolTalk NetFile or into a full path if needed
610  */
611 static String
612 dndFileEncode(
613         String          fileName,
614         Boolean         doNetFile)
615 {
616         String          netFile;
617         String          retFile;
618
619         if (doNetFile) {
620                 netFile = tt_file_netfile(fileName);
621                 if (tt_ptr_error(netFile) == TT_OK) {
622                         retFile = XtNewString(netFile);
623                 } else {
624                         retFile = XtNewString(fileName);
625                 }
626                 tt_free(netFile);
627         } else if (fileName[0] != '/') {
628                 char    cwd[MAXPATHLEN];
629                 char    *realPath;
630
631                 if (getcwd(cwd,MAXPATHLEN) == NULL) {
632                         strcpy(cwd,"/");
633                 }
634                 realPath = XtMalloc(strlen(cwd) + strlen(fileName) + 2);
635                 sprintf(realPath,"%s/%s",cwd,fileName);
636                 retFile = realPath;
637         } else {
638                 retFile = XtNewString(fileName);
639         }
640         return retFile;
641 }
642
643 /*
644  * Decodes a file name; possibly from a ToolTalk NetFile
645  */
646 static String
647 dndFileDecode(
648         String          fileName,
649         Boolean         doNetFile)
650 {
651         String          file;
652         String          retFile;
653
654         if (doNetFile) {
655                 file = tt_netfile_file(fileName);
656                 if (tt_ptr_error(file) == TT_OK) {
657                         retFile = XtNewString(file);
658                 } else {
659                         retFile = XtNewString(fileName);
660                 }
661                 tt_free(file);
662         } else {
663                 retFile = XtNewString(fileName);
664         }
665         return retFile;
666 }