1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <utility>
23 
24 #include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
25 #include <com/sun/star/lang/IllegalArgumentException.hpp>
26 #include <sal/types.h>
27 #include <osl/diagnose.h>
28 
29 #include "OSXTransferable.hxx"
30 
31 #include "DataFlavorMapping.hxx"
32 
33 using namespace std;
34 using namespace osl;
35 using namespace cppu;
36 using namespace com::sun::star::uno;
37 using namespace com::sun::star::datatransfer;
38 using namespace com::sun::star::lang;
39 
40 namespace
41 {
isValidFlavor(const DataFlavor & aFlavor)42     bool isValidFlavor( const DataFlavor& aFlavor )
43     {
44       size_t len = aFlavor.MimeType.getLength();
45       Type dtype = aFlavor.DataType;
46       return ((len > 0) && ((dtype == cppu::UnoType<Sequence<sal_Int8>>::get()) || (dtype == cppu::UnoType<OUString>::get())));
47     }
48 
cmpAllContentTypeParameter(const Reference<XMimeContentType> & xLhs,const Reference<XMimeContentType> & xRhs)49 bool cmpAllContentTypeParameter(const Reference<XMimeContentType> & xLhs,
50                                                const Reference<XMimeContentType> & xRhs)
51 {
52   Sequence<OUString> xLhsFlavors = xLhs->getParameters();
53   Sequence<OUString> xRhsFlavors = xRhs->getParameters();
54 
55   // Stop here if the number of parameters is different already
56   if (xLhsFlavors.getLength() != xRhsFlavors.getLength())
57     return false;
58 
59   try
60     {
61       OUString pLhs;
62       OUString pRhs;
63 
64       for (sal_Int32 i = 0; i < xLhsFlavors.getLength(); i++)
65         {
66           pLhs = xLhs->getParameterValue(xLhsFlavors[i]);
67           pRhs = xRhs->getParameterValue(xLhsFlavors[i]);
68 
69           if (!pLhs.equalsIgnoreAsciiCase(pRhs))
70             {
71               return false;
72             }
73         }
74     }
75   catch(IllegalArgumentException&)
76     {
77       return false;
78     }
79 
80   return true;
81 }
82 
83 } // unnamed namespace
84 
OSXTransferable(const Reference<XMimeContentTypeFactory> & rXMimeCntFactory,DataFlavorMapperPtr_t pDataFlavorMapper,NSPasteboard * pasteboard)85 OSXTransferable::OSXTransferable(const Reference<XMimeContentTypeFactory> & rXMimeCntFactory,
86                                  DataFlavorMapperPtr_t pDataFlavorMapper,
87                                  NSPasteboard* pasteboard) :
88   mrXMimeCntFactory(rXMimeCntFactory),
89   mDataFlavorMapper(pDataFlavorMapper),
90   mPasteboard(pasteboard)
91 {
92   [mPasteboard retain];
93 
94   initClipboardItemList();
95 }
96 
~OSXTransferable()97 OSXTransferable::~OSXTransferable()
98 {
99   [mPasteboard release];
100 }
101 
getTransferData(const DataFlavor & aFlavor)102 Any SAL_CALL OSXTransferable::getTransferData( const DataFlavor& aFlavor )
103 {
104   if (!isValidFlavor(aFlavor) || !isDataFlavorSupported(aFlavor))
105     {
106       throw UnsupportedFlavorException("AquaClipboard: Unsupported data flavor",
107                                        static_cast<XTransferable*>(this));
108     }
109 
110   bool bInternal(false);
111   NSString const * sysFormat =
112       (aFlavor.MimeType.startsWith("image/png"))
113       ? DataFlavorMapper::openOfficeImageToSystemFlavor( mPasteboard )
114       : mDataFlavorMapper->openOfficeToSystemFlavor(aFlavor, bInternal);
115   DataProviderPtr_t dp;
116 
117 SAL_WNODEPRECATED_DECLARATIONS_PUSH
118       // "'NSFilenamesPboardType' is deprecated: first deprecated in macOS 10.14 - Create multiple
119       // pasteboard items with NSPasteboardTypeFileURL or kUTTypeFileURL instead"
120   if ([sysFormat caseInsensitiveCompare: NSFilenamesPboardType] == NSOrderedSame)
121 SAL_WNODEPRECATED_DECLARATIONS_POP
122     {
123       NSArray* sysData = [mPasteboard propertyListForType: const_cast<NSString *>(sysFormat)];
124       dp = DataFlavorMapper::getDataProvider(sysFormat, sysData);
125     }
126   else
127     {
128       NSData* sysData = [mPasteboard dataForType: const_cast<NSString *>(sysFormat)];
129       dp = DataFlavorMapper::getDataProvider(sysFormat, sysData);
130     }
131 
132   if (!dp)
133     {
134       throw UnsupportedFlavorException("AquaClipboard: Unsupported data flavor",
135                                        static_cast<XTransferable*>(this));
136     }
137 
138   return dp->getOOoData();
139 }
140 
getTransferDataFlavors()141 Sequence< DataFlavor > SAL_CALL OSXTransferable::getTransferDataFlavors(  )
142 {
143   return mFlavorList;
144 }
145 
isDataFlavorSupported(const DataFlavor & aFlavor)146 sal_Bool SAL_CALL OSXTransferable::isDataFlavorSupported(const DataFlavor& aFlavor)
147 {
148     for (const DataFlavor& rFlavor : std::as_const(mFlavorList))
149       if (compareDataFlavors(aFlavor, rFlavor))
150         return true;
151 
152     return false;
153 }
154 
initClipboardItemList()155 void OSXTransferable::initClipboardItemList()
156 {
157   NSArray* pboardFormats = [mPasteboard types];
158 
159   if (pboardFormats == nullptr)
160     {
161       throw RuntimeException("AquaClipboard: Cannot get clipboard data",
162                              static_cast<XTransferable*>(this));
163     }
164 
165   mFlavorList = mDataFlavorMapper->typesArrayToFlavorSequence(pboardFormats);
166 }
167 
168 /* Compares two DataFlavors. Returns true if both DataFlavor have the same media type
169    and the number of parameter and all parameter values do match otherwise false
170    is returned.
171  */
compareDataFlavors(const DataFlavor & lhs,const DataFlavor & rhs)172 bool OSXTransferable::compareDataFlavors(const DataFlavor& lhs, const DataFlavor& rhs )
173 {
174     try
175     {
176         Reference<XMimeContentType> xLhs(mrXMimeCntFactory->createMimeContentType(lhs.MimeType));
177         Reference<XMimeContentType> xRhs(mrXMimeCntFactory->createMimeContentType(rhs.MimeType));
178 
179         if (!xLhs->getFullMediaType().equalsIgnoreAsciiCase(xRhs->getFullMediaType()) ||
180             !cmpAllContentTypeParameter(xLhs, xRhs))
181           {
182             return false;
183           }
184     }
185     catch( IllegalArgumentException& )
186     {
187         OSL_FAIL( "Invalid content type detected" );
188         return false;
189     }
190 
191     return true;
192 }
193 
194 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
195