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 <string.h>
25 #include <sys/types.h>
26 #include <sal/macros.h>
27 #include <osl/time.h>
28 #include <sal/log.hxx>
29 
30 #include <com/sun/star/beans/IllegalTypeException.hpp>
31 #include <com/sun/star/beans/PropertyValue.hpp>
32 #include <com/sun/star/beans/PropertyAttribute.hpp>
33 #include <com/sun/star/beans/XPropertySetInfo.hpp>
34 #include <com/sun/star/io/IOException.hpp>
35 #include <com/sun/star/io/XActiveDataSink.hpp>
36 #include <com/sun/star/io/XOutputStream.hpp>
37 #include <com/sun/star/lang/IllegalAccessException.hpp>
38 #include <com/sun/star/lang/IllegalArgumentException.hpp>
39 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
40 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
41 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
42 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
43 #include <com/sun/star/ucb/InteractiveNetworkGeneralException.hpp>
44 #include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
45 #include <com/sun/star/ucb/NameClashException.hpp>
46 #include <com/sun/star/ucb/OpenMode.hpp>
47 #include <com/sun/star/ucb/XCommandInfo.hpp>
48 #include <com/sun/star/ucb/MissingInputStreamException.hpp>
49 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
50 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
51 #include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
52 #include <com/sun/star/ucb/XDynamicResultSet.hpp>
53 #include <com/sun/star/ucb/XContentCreator.hpp>
54 
55 #include <comphelper/seekableinput.hxx>
56 #include <cppuhelper/exc_hlp.hxx>
57 #include <cppuhelper/queryinterface.hxx>
58 #include <ucbhelper/contentidentifier.hxx>
59 #include <ucbhelper/propertyvalueset.hxx>
60 #include <ucbhelper/cancelcommandexecution.hxx>
61 #include <ucbhelper/macros.hxx>
62 #include <vcl/svapp.hxx>
63 
64 #include "gio_content.hxx"
65 #include "gio_provider.hxx"
66 #include "gio_resultset.hxx"
67 #include "gio_inputstream.hxx"
68 #include "gio_outputstream.hxx"
69 #include "gio_mount.hxx"
70 
71 namespace gio
72 {
73 
Content(const css::uno::Reference<css::uno::XComponentContext> & rxContext,ContentProvider * pProvider,const css::uno::Reference<css::ucb::XContentIdentifier> & Identifier)74 Content::Content(
75     const css::uno::Reference< css::uno::XComponentContext >& rxContext,
76     ContentProvider* pProvider,
77     const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier)
78     : ContentImplHelper( rxContext, pProvider, Identifier ),
79       m_pProvider( pProvider ), mpFile (nullptr), mpInfo( nullptr ), mbTransient(false)
80 {
81     SAL_INFO("ucb.ucp.gio", "New Content ('" << m_xIdentifier->getContentIdentifier() << "')");
82 }
83 
Content(const css::uno::Reference<css::uno::XComponentContext> & rxContext,ContentProvider * pProvider,const css::uno::Reference<css::ucb::XContentIdentifier> & Identifier,bool bIsFolder)84 Content::Content(
85     const css::uno::Reference< css::uno::XComponentContext >& rxContext,
86     ContentProvider* pProvider,
87     const css::uno::Reference< css::ucb::XContentIdentifier >& Identifier,
88     bool bIsFolder)
89     : ContentImplHelper( rxContext, pProvider, Identifier ),
90       m_pProvider( pProvider ), mpFile (nullptr), mpInfo( nullptr ), mbTransient(true)
91 {
92     SAL_INFO("ucb.ucp.gio", "Create Content ('" << m_xIdentifier->getContentIdentifier() << "')");
93     mpInfo = g_file_info_new();
94     g_file_info_set_file_type(mpInfo, bIsFolder ? G_FILE_TYPE_DIRECTORY : G_FILE_TYPE_REGULAR);
95 }
96 
~Content()97 Content::~Content()
98 {
99     if (mpInfo) g_object_unref(mpInfo);
100     if (mpFile) g_object_unref(mpFile);
101 }
102 
getParentURL()103 OUString Content::getParentURL()
104 {
105     OUString sURL;
106     if (GFile* pFile = g_file_get_parent(getGFile()))
107     {
108         char* pPath = g_file_get_uri(pFile);
109         g_object_unref(pFile);
110         sURL = OUString::createFromAscii(pPath);
111         g_free(pPath);
112     }
113     return sURL;
114 }
115 
abort(sal_Int32)116 void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
117 {
118     //TODO
119     //stick a map from each CommandId to a new GCancellable and propagate
120     //it throughout the g_file_* calls
121 }
122 
getContentType()123 OUString SAL_CALL Content::getContentType()
124 {
125     return isFolder(css::uno::Reference< css::ucb::XCommandEnvironment >())
126         ? OUString( GIO_FOLDER_TYPE )
127         : OUString( GIO_FILE_TYPE );
128 }
129 
130 #define EXCEPT(aExcept) \
131 do { \
132     if (bThrow) throw aExcept;\
133     aRet <<= aExcept;\
134 } while(false)
135 
convertToException(GError * pError,const css::uno::Reference<css::uno::XInterface> & rContext,bool bThrow)136 css::uno::Any convertToException(GError *pError, const css::uno::Reference< css::uno::XInterface >& rContext, bool bThrow)
137 {
138     css::uno::Any aRet;
139 
140     gint eCode = pError->code;
141     OUString sMessage(pError->message, strlen(pError->message), RTL_TEXTENCODING_UTF8);
142     g_error_free(pError);
143 
144     OUString sName;
145 
146     css::uno::Sequence< css::uno::Any > aArgs( 1 );
147     aArgs[ 0 ] <<= sName;
148 
149     switch (eCode)
150     {
151         case G_IO_ERROR_FAILED:
152             { css::io::IOException aExcept(sMessage, rContext);
153               EXCEPT(aExcept); }
154             break;
155         case G_IO_ERROR_NOT_MOUNTED:
156             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
157                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NOT_EXISTING_PATH, aArgs);
158               EXCEPT(aExcept); }
159             break;
160         case G_IO_ERROR_NOT_FOUND:
161             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
162                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NOT_EXISTING, aArgs);
163               EXCEPT(aExcept); }
164             break;
165         case G_IO_ERROR_EXISTS:
166             { css::ucb::NameClashException aExcept(sMessage, rContext,
167                 css::task::InteractionClassification_ERROR, sName);
168               EXCEPT(aExcept); }
169             break;
170         case G_IO_ERROR_INVALID_ARGUMENT:
171             { css::lang::IllegalArgumentException aExcept(sMessage, rContext, -1 );
172               EXCEPT(aExcept); }
173             break;
174         case G_IO_ERROR_PERMISSION_DENIED:
175             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
176                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_ACCESS_DENIED, aArgs);
177               EXCEPT(aExcept); }
178             break;
179         case G_IO_ERROR_IS_DIRECTORY:
180             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
181                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NO_FILE, aArgs);
182               EXCEPT(aExcept); }
183             break;
184         case G_IO_ERROR_NOT_REGULAR_FILE:
185             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
186                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NO_FILE, aArgs);
187               EXCEPT(aExcept); }
188             break;
189         case G_IO_ERROR_NOT_DIRECTORY:
190             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
191                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NO_DIRECTORY, aArgs);
192               EXCEPT(aExcept); }
193             break;
194         case G_IO_ERROR_FILENAME_TOO_LONG:
195             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
196                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NAME_TOO_LONG, aArgs);
197               EXCEPT(aExcept); }
198             break;
199         case G_IO_ERROR_FAILED_HANDLED: /* Operation failed and a helper program
200                                            has already interacted with the user. Do not display any error
201                                            dialog */
202         case G_IO_ERROR_PENDING:
203             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
204                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_PENDING, aArgs);
205               EXCEPT(aExcept); }
206             break;
207         case G_IO_ERROR_CLOSED:
208         case G_IO_ERROR_CANCELLED:
209         case G_IO_ERROR_TOO_MANY_LINKS:
210         case G_IO_ERROR_WRONG_ETAG:
211             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
212                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_GENERAL, aArgs);
213               EXCEPT(aExcept); }
214             break;
215         case G_IO_ERROR_NOT_SUPPORTED:
216         case G_IO_ERROR_CANT_CREATE_BACKUP:
217         case G_IO_ERROR_WOULD_MERGE:
218             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
219                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_NOT_SUPPORTED, aArgs);
220               EXCEPT(aExcept); }
221             break;
222         case G_IO_ERROR_NO_SPACE:
223             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
224                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_OUT_OF_DISK_SPACE, aArgs);
225               EXCEPT(aExcept); }
226             break;
227         case G_IO_ERROR_INVALID_FILENAME:
228             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
229                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_INVALID_CHARACTER, aArgs);
230               EXCEPT(aExcept); }
231             break;
232         case G_IO_ERROR_READ_ONLY:
233             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
234                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_WRITE_PROTECTED, aArgs);
235               EXCEPT(aExcept); }
236             break;
237         case G_IO_ERROR_TIMED_OUT:
238             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
239                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_DEVICE_NOT_READY, aArgs);
240               EXCEPT(aExcept); }
241             break;
242         case G_IO_ERROR_WOULD_RECURSE:
243             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
244                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_RECURSIVE, aArgs);
245               EXCEPT(aExcept); }
246             break;
247         case G_IO_ERROR_BUSY:
248         case G_IO_ERROR_WOULD_BLOCK:
249             { css::ucb::InteractiveAugmentedIOException aExcept(sMessage, rContext,
250                 css::task::InteractionClassification_ERROR, css::ucb::IOErrorCode_LOCKING_VIOLATION, aArgs);
251               EXCEPT(aExcept); }
252             break;
253         case G_IO_ERROR_HOST_NOT_FOUND:
254             { css::ucb::InteractiveNetworkResolveNameException aExcept(sMessage, rContext,
255                 css::task::InteractionClassification_ERROR, OUString());
256               EXCEPT(aExcept);}
257             break;
258         default:
259         case G_IO_ERROR_ALREADY_MOUNTED:
260         case G_IO_ERROR_NOT_EMPTY:
261         case G_IO_ERROR_NOT_SYMBOLIC_LINK:
262         case G_IO_ERROR_NOT_MOUNTABLE_FILE:
263             { css::ucb::InteractiveNetworkGeneralException aExcept(sMessage, rContext,
264                 css::task::InteractionClassification_ERROR);
265               EXCEPT(aExcept);}
266             break;
267     }
268     return aRet;
269 }
270 
convertToIOException(GError * pError,const css::uno::Reference<css::uno::XInterface> & rContext)271 void convertToIOException(GError *pError, const css::uno::Reference< css::uno::XInterface >& rContext)
272 {
273     try
274     {
275         convertToException(pError, rContext);
276     }
277     catch (const css::io::IOException&)
278     {
279         throw;
280     }
281     catch (const css::uno::RuntimeException&)
282     {
283         throw;
284     }
285     catch (const css::uno::Exception& e)
286     {
287         css::uno::Any a(cppu::getCaughtException());
288         throw css::lang::WrappedTargetRuntimeException(
289             "wrapped Exception " + e.Message,
290             css::uno::Reference<css::uno::XInterface>(), a);
291     }
292 }
293 
mapGIOError(GError * pError)294 css::uno::Any Content::mapGIOError( GError *pError )
295 {
296     if (!pError)
297         return getBadArgExcept();
298 
299     return convertToException(pError, static_cast< cppu::OWeakObject * >(this), false);
300 }
301 
getBadArgExcept()302 css::uno::Any Content::getBadArgExcept()
303 {
304     return css::uno::makeAny( css::lang::IllegalArgumentException(
305         "Wrong argument type!",
306         static_cast< cppu::OWeakObject * >( this ), -1) );
307 }
308 
309 class MountOperation
310 {
311     ucb::ucp::gio::glib::MainContextRef mContext;
312     GMainLoop *mpLoop;
313     GMountOperation *mpAuthentication;
314     GError *mpError;
315     static void Completed(GObject *source, GAsyncResult *res, gpointer user_data);
316 public:
317     explicit MountOperation(const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv);
318     ~MountOperation();
319     GError *Mount(GFile *pFile);
320 };
321 
MountOperation(const css::uno::Reference<css::ucb::XCommandEnvironment> & xEnv)322 MountOperation::MountOperation(const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv) : mpError(nullptr)
323 {
324     ucb::ucp::gio::glib::MainContextRef oldContext(g_main_context_ref_thread_default());
325     mContext.reset(g_main_context_new());
326     mpLoop = g_main_loop_new(mContext.get(), FALSE);
327     g_main_context_push_thread_default(mContext.get());
328     mpAuthentication = ooo_mount_operation_new(std::move(oldContext), xEnv);
329 }
330 
Completed(GObject * source,GAsyncResult * res,gpointer user_data)331 void MountOperation::Completed(GObject *source, GAsyncResult *res, gpointer user_data)
332 {
333     MountOperation *pThis = static_cast<MountOperation*>(user_data);
334     g_file_mount_enclosing_volume_finish(G_FILE(source), res, &(pThis->mpError));
335     g_main_loop_quit(pThis->mpLoop);
336 }
337 
Mount(GFile * pFile)338 GError *MountOperation::Mount(GFile *pFile)
339 {
340     g_file_mount_enclosing_volume(pFile, G_MOUNT_MOUNT_NONE, mpAuthentication, nullptr, MountOperation::Completed, this);
341     {
342         //HACK: At least the gdk_threads_set_lock_functions(GdkThreadsEnter,
343         // GdkThreadsLeave) call in vcl/unx/gtk/app/gtkinst.cxx will lead to
344         // GdkThreadsLeave unlock the SolarMutex down to zero at the end of
345         // g_main_loop_run, so we need ~SolarMutexReleaser to raise it back to
346         // the original value again:
347         if (comphelper::SolarMutex::get()->IsCurrentThread())
348         {
349             SolarMutexReleaser rel;
350             g_main_loop_run(mpLoop);
351         }
352         else
353         {
354             g_main_loop_run(mpLoop);
355         }
356     }
357     return mpError;
358 }
359 
~MountOperation()360 MountOperation::~MountOperation()
361 {
362     g_object_unref(mpAuthentication);
363     g_main_context_pop_thread_default(mContext.get());
364     g_main_loop_unref(mpLoop);
365 }
366 
getGFileInfo(const css::uno::Reference<css::ucb::XCommandEnvironment> & xEnv,GError ** ppError)367 GFileInfo* Content::getGFileInfo(const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv, GError **ppError)
368 {
369     GError * err = nullptr;
370     if (mpInfo == nullptr && !mbTransient) {
371         for (bool retried = false;; retried = true) {
372             mpInfo = g_file_query_info(
373                 getGFile(), "*", G_FILE_QUERY_INFO_NONE, nullptr, &err);
374             if (mpInfo != nullptr) {
375                 break;
376             }
377             assert(err != nullptr);
378             if (err->code != G_IO_ERROR_NOT_MOUNTED || retried) {
379                 break;
380             }
381             SAL_INFO(
382                 "ucb.ucp.gio",
383                 "G_IO_ERROR_NOT_MOUNTED \"" << err->message
384                     << "\", trying to mount");
385             g_error_free(err);
386             err = MountOperation(xEnv).Mount(getGFile());
387             if (err != nullptr) {
388                 break;
389             }
390         }
391     }
392     if (ppError != nullptr) {
393         *ppError = err;
394     } else if (err != nullptr) {
395         SAL_WARN(
396             "ucb.ucp.gio",
397             "ignoring GError \"" << err->message << "\" for <"
398                 << m_xIdentifier->getContentIdentifier() << ">");
399         g_error_free(err);
400     }
401     return mpInfo;
402 }
403 
getGFile()404 GFile* Content::getGFile()
405 {
406     if (!mpFile)
407         mpFile = g_file_new_for_uri(OUStringToOString(m_xIdentifier->getContentIdentifier(), RTL_TEXTENCODING_UTF8).getStr());
408     return mpFile;
409 }
410 
isFolder(const css::uno::Reference<css::ucb::XCommandEnvironment> & xEnv)411 bool Content::isFolder(const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv)
412 {
413     GFileInfo *pInfo = getGFileInfo(xEnv);
414     return pInfo && (g_file_info_get_file_type(pInfo) == G_FILE_TYPE_DIRECTORY);
415 }
416 
getDateFromUnix(time_t t)417 static css::util::DateTime getDateFromUnix (time_t t)
418 {
419     TimeValue tv;
420     tv.Nanosec = 0;
421     tv.Seconds = t;
422     oslDateTime dt;
423 
424     if ( osl_getDateTimeFromTimeValue( &tv, &dt ) )
425         return css::util::DateTime( 0, dt.Seconds, dt.Minutes, dt.Hours,
426                    dt.Day, dt.Month, dt.Year, false);
427     else
428         return css::util::DateTime();
429 }
430 
getPropertyValues(const css::uno::Sequence<css::beans::Property> & rProperties,const css::uno::Reference<css::ucb::XCommandEnvironment> & xEnv)431 css::uno::Reference< css::sdbc::XRow > Content::getPropertyValues(
432                 const css::uno::Sequence< css::beans::Property >& rProperties,
433                 const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv )
434 {
435     rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( m_xContext );
436 
437     GFileInfo *pInfo = nullptr;
438     for( const css::beans::Property& rProp : rProperties )
439     {
440         if ( rProp.Name == "IsDocument" )
441         {
442             getFileInfo(xEnv, &pInfo, true);
443             if (pInfo != nullptr && g_file_info_has_attribute(pInfo, G_FILE_ATTRIBUTE_STANDARD_TYPE))
444                 xRow->appendBoolean( rProp, ( g_file_info_get_file_type( pInfo ) == G_FILE_TYPE_REGULAR ||
445                                                g_file_info_get_file_type( pInfo ) == G_FILE_TYPE_UNKNOWN ) );
446             else
447                 xRow->appendVoid( rProp );
448         }
449         else if ( rProp.Name == "IsFolder" )
450         {
451             getFileInfo(xEnv, &pInfo, true);
452             if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_STANDARD_TYPE) )
453                 xRow->appendBoolean( rProp, ( g_file_info_get_file_type( pInfo ) == G_FILE_TYPE_DIRECTORY ));
454             else
455                 xRow->appendVoid( rProp );
456         }
457         else if ( rProp.Name == "Title" )
458         {
459             getFileInfo(xEnv, &pInfo, false);
460             if (pInfo != nullptr && g_file_info_has_attribute(pInfo, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))
461             {
462                 const char *pName = g_file_info_get_display_name(pInfo);
463                 xRow->appendString( rProp, OUString(pName, strlen(pName), RTL_TEXTENCODING_UTF8) );
464             }
465             else
466                 xRow->appendVoid(rProp);
467         }
468         else if ( rProp.Name == "IsReadOnly" )
469         {
470             getFileInfo(xEnv, &pInfo, true);
471             if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE ) )
472                 xRow->appendBoolean( rProp, !g_file_info_get_attribute_boolean( pInfo, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE) );
473             else
474                 xRow->appendVoid( rProp );
475         }
476         else if ( rProp.Name == "DateCreated" )
477         {
478             getFileInfo(xEnv, &pInfo, true);
479             if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_TIME_CREATED ) )
480                 xRow->appendTimestamp( rProp, getDateFromUnix(g_file_info_get_attribute_uint64(pInfo, G_FILE_ATTRIBUTE_TIME_CREATED)) );
481             else
482                 xRow->appendVoid( rProp );
483         }
484         else if ( rProp.Name == "DateModified" )
485         {
486             getFileInfo(xEnv, &pInfo, true);
487             if (pInfo != nullptr && g_file_info_has_attribute( pInfo,  G_FILE_ATTRIBUTE_TIME_CHANGED ) )
488                 xRow->appendTimestamp( rProp, getDateFromUnix(g_file_info_get_attribute_uint64(pInfo, G_FILE_ATTRIBUTE_TIME_CHANGED)) );
489             else
490                 xRow->appendVoid( rProp );
491         }
492         else if ( rProp.Name == "Size" )
493         {
494             getFileInfo(xEnv, &pInfo, true);
495             if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_STANDARD_SIZE) )
496                 xRow->appendLong( rProp, ( g_file_info_get_size( pInfo ) ));
497             else
498                 xRow->appendVoid( rProp );
499         }
500         else if ( rProp.Name == "IsVolume" )
501         {
502             //What do we use this for ?
503             xRow->appendBoolean( rProp, false );
504         }
505         else if ( rProp.Name == "IsCompactDisc" )
506         {
507             getFileInfo(xEnv, &pInfo, true);
508             if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT ) )
509                 xRow->appendBoolean( rProp, g_file_info_get_attribute_boolean(pInfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT) );
510             else
511                 xRow->appendVoid( rProp );
512         }
513         else if ( rProp.Name == "IsRemoveable" )
514         {
515             getFileInfo(xEnv, &pInfo, true);
516             if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT ) )
517                 xRow->appendBoolean( rProp, g_file_info_get_attribute_boolean(pInfo, G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT ) );
518             else
519                 xRow->appendVoid( rProp );
520         }
521         else if ( rProp.Name == "IsFloppy" )
522         {
523             xRow->appendBoolean( rProp, false );
524         }
525         else if ( rProp.Name == "IsHidden" )
526         {
527             getFileInfo(xEnv, &pInfo, true);
528             if (pInfo != nullptr && g_file_info_has_attribute( pInfo, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN) )
529                 xRow->appendBoolean( rProp, ( g_file_info_get_is_hidden ( pInfo ) ) );
530             else
531                 xRow->appendVoid( rProp );
532         }
533         else if ( rProp.Name == "CreatableContentsInfo" )
534         {
535             xRow->appendObject( rProp, css::uno::makeAny( queryCreatableContentsInfo( xEnv ) ) );
536         }
537         else
538         {
539             SAL_WARN(
540                 "ucb.ucp.gio",
541                 "Looking for unsupported property " << rProp.Name);
542         }
543     }
544 
545     return css::uno::Reference< css::sdbc::XRow >( xRow.get() );
546 }
547 
548 static css::lang::IllegalAccessException
getReadOnlyException(const css::uno::Reference<css::uno::XInterface> & rContext)549 getReadOnlyException( const css::uno::Reference< css::uno::XInterface >& rContext )
550 {
551     return css::lang::IllegalAccessException ("Property is read-only!", rContext );
552 }
553 
queryChildren(ContentRefList & rChildren)554 void Content::queryChildren( ContentRefList& rChildren )
555 {
556     // Obtain a list with a snapshot of all currently instantiated contents
557     // from provider and extract the contents which are direct children
558     // of this content.
559 
560     ucbhelper::ContentRefList aAllContents;
561     m_xProvider->queryExistingContents( aAllContents );
562 
563     OUString aURL = m_xIdentifier->getContentIdentifier();
564     sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
565 
566     if ( nURLPos != ( aURL.getLength() - 1 ) )
567         aURL += "/";
568 
569     sal_Int32 nLen = aURL.getLength();
570 
571     for ( const auto& rContent : aAllContents )
572     {
573         ucbhelper::ContentImplHelperRef xChild = rContent;
574         OUString aChildURL = xChild->getIdentifier()->getContentIdentifier();
575 
576         // Is aURL a prefix of aChildURL?
577         if ( ( aChildURL.getLength() > nLen ) && aChildURL.startsWith( aURL ) )
578         {
579             sal_Int32 nPos = aChildURL.indexOf( '/', nLen );
580 
581             if ( ( nPos == -1 ) || ( nPos == ( aChildURL.getLength() - 1 ) ) )
582             {
583                 // No further slashes / only a final slash. It's a child!
584                 rChildren.emplace_back(static_cast< ::gio::Content * >(xChild.get() ) );
585             }
586         }
587     }
588 }
589 
exchangeIdentity(const css::uno::Reference<css::ucb::XContentIdentifier> & xNewId)590 bool Content::exchangeIdentity( const css::uno::Reference< css::ucb::XContentIdentifier >& xNewId )
591 {
592     if ( !xNewId.is() )
593         return false;
594 
595     css::uno::Reference< css::ucb::XContent > xThis = this;
596 
597     if ( mbTransient )
598     {
599         m_xIdentifier = xNewId;
600         return false;
601     }
602 
603     OUString aOldURL = m_xIdentifier->getContentIdentifier();
604 
605     // Exchange own identitity.
606     if ( exchange( xNewId ) )
607     {
608         // Process instantiated children...
609         ContentRefList aChildren;
610         queryChildren( aChildren );
611 
612         for ( const auto& rChild : aChildren )
613         {
614             ContentRef xChild = rChild;
615 
616             // Create new content identifier for the child...
617             css::uno::Reference< css::ucb::XContentIdentifier > xOldChildId = xChild->getIdentifier();
618             OUString aOldChildURL = xOldChildId->getContentIdentifier();
619             OUString aNewChildURL = aOldChildURL.replaceAt(
620                 0, aOldURL.getLength(), xNewId->getContentIdentifier() );
621 
622             css::uno::Reference< css::ucb::XContentIdentifier > xNewChildId
623                 = new ::ucbhelper::ContentIdentifier( aNewChildURL );
624 
625             if ( !xChild->exchangeIdentity( xNewChildId ) )
626                 return false;
627         }
628         return true;
629     }
630 
631     return false;
632 }
633 
getFileInfo(css::uno::Reference<css::ucb::XCommandEnvironment> const & env,GFileInfo ** info,bool fail)634 void Content::getFileInfo(
635     css::uno::Reference<css::ucb::XCommandEnvironment> const & env, GFileInfo ** info, bool fail)
636 {
637     assert(info != nullptr);
638     if (*info == nullptr)
639     {
640         GError * err = nullptr;
641         *info = getGFileInfo(env, &err);
642         if (*info == nullptr && !mbTransient && fail)
643         {
644             ucbhelper::cancelCommandExecution(mapGIOError(err), env);
645         }
646         else if (err != nullptr)
647         {
648             g_error_free(err);
649         }
650     }
651 }
652 
setPropertyValues(const css::uno::Sequence<css::beans::PropertyValue> & rValues,const css::uno::Reference<css::ucb::XCommandEnvironment> & xEnv)653 css::uno::Sequence< css::uno::Any > Content::setPropertyValues(
654     const css::uno::Sequence< css::beans::PropertyValue >& rValues,
655     const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv )
656 {
657     GError *pError=nullptr;
658     GFileInfo *pNewInfo=nullptr;
659     GFileInfo *pInfo = getGFileInfo(xEnv, &pError);
660     if (pInfo)
661         pNewInfo = g_file_info_dup(pInfo);
662     else
663     {
664         if (!mbTransient)
665             ucbhelper::cancelCommandExecution(mapGIOError(pError), xEnv);
666         else
667         {
668             if (pError)
669                 g_error_free(pError);
670             pNewInfo = g_file_info_new();
671         }
672     }
673 
674     sal_Int32 nCount = rValues.getLength();
675 
676     css::beans::PropertyChangeEvent aEvent;
677     aEvent.Source = static_cast< cppu::OWeakObject * >( this );
678     aEvent.Further = false;
679     aEvent.PropertyHandle = -1;
680 
681     sal_Int32 nChanged = 0, nTitlePos = -1;
682     const char *newName = nullptr;
683     css::uno::Sequence< css::beans::PropertyChangeEvent > aChanges(nCount);
684 
685     css::uno::Sequence< css::uno::Any > aRet( nCount );
686     const css::beans::PropertyValue* pValues = rValues.getConstArray();
687     for ( sal_Int32 n = 0; n < nCount; ++n )
688     {
689         const css::beans::PropertyValue& rValue = pValues[ n ];
690         SAL_INFO("ucb.ucp.gio", "Set prop '" << rValue.Name << "'");
691         if ( rValue.Name == "ContentType" ||
692              rValue.Name == "MediaType" ||
693              rValue.Name == "IsDocument" ||
694              rValue.Name == "IsFolder" ||
695              rValue.Name == "Size" ||
696              rValue.Name == "CreatableContentsInfo" )
697         {
698             aRet[ n ] <<= getReadOnlyException( static_cast< cppu::OWeakObject * >(this) );
699         }
700         else if ( rValue.Name == "Title" )
701         {
702             OUString aNewTitle;
703             if (!( rValue.Value >>= aNewTitle ))
704             {
705                 aRet[ n ] <<= css::beans::IllegalTypeException
706                     ( "Property value has wrong type!",
707                       static_cast< cppu::OWeakObject * >( this ) );
708                 continue;
709             }
710 
711             if ( aNewTitle.getLength() <= 0 )
712             {
713                 aRet[ n ] <<= css::lang::IllegalArgumentException
714                     ( "Empty title not allowed!",
715                       static_cast< cppu::OWeakObject * >( this ), -1 );
716                 continue;
717 
718             }
719 
720             OString sNewTitle = OUStringToOString(aNewTitle, RTL_TEXTENCODING_UTF8);
721             newName = sNewTitle.getStr();
722             const char *oldName = g_file_info_get_name( pInfo);
723 
724             if (!newName || !oldName || strcmp(newName, oldName))
725             {
726                 SAL_INFO("ucb.ucp.gio", "Set new name to '" << newName << "'");
727 
728                 aEvent.PropertyName = "Title";
729                 if (oldName)
730                     aEvent.OldValue <<= OUString(oldName, strlen(oldName), RTL_TEXTENCODING_UTF8);
731                 aEvent.NewValue <<= aNewTitle;
732                 aChanges.getArray()[ nChanged ] = aEvent;
733                 nTitlePos = nChanged++;
734 
735                 g_file_info_set_name(pNewInfo, newName);
736             }
737         }
738         else
739         {
740             SAL_WARN("ucb.ucp.gio", "Unknown property " << rValue.Name);
741             aRet[ n ] <<= getReadOnlyException( static_cast< cppu::OWeakObject * >(this) );
742             //TODO
743         }
744     }
745 
746     if (nChanged)
747     {
748         bool bOk = true;
749         if (!mbTransient)
750         {
751             if ((bOk = doSetFileInfo(pNewInfo)))
752             {
753                 for (sal_Int32 i = 0; i < nChanged; ++i)
754                     aRet[ i ] = getBadArgExcept();
755             }
756         }
757 
758         if (bOk)
759         {
760             if (nTitlePos > -1)
761             {
762                 OUString aNewURL = getParentURL() +
763                     OUString( newName, strlen(newName), RTL_TEXTENCODING_UTF8 );
764                 css::uno::Reference< css::ucb::XContentIdentifier > xNewId
765                     = new ::ucbhelper::ContentIdentifier( aNewURL );
766 
767                 if (!exchangeIdentity( xNewId ) )
768                 {
769                     aRet[ nTitlePos ] <<= css::uno::Exception
770                         ( "Exchange failed!",
771                           static_cast< cppu::OWeakObject * >( this ) );
772                 }
773             }
774 
775             if (!mbTransient) //Discard and refetch
776             {
777                 g_object_unref(mpInfo);
778                 mpInfo = nullptr;
779             }
780 
781             if (mpInfo)
782             {
783                 g_file_info_copy_into(pNewInfo, mpInfo);
784                 g_object_unref(pNewInfo);
785             }
786             else
787                 mpInfo = pNewInfo;
788 
789             if (mpFile) //Discard and refetch
790             {
791                 g_object_unref(mpFile);
792                 mpFile = nullptr;
793             }
794         }
795 
796         aChanges.realloc( nChanged );
797         notifyPropertiesChange( aChanges );
798     }
799 
800     return aRet;
801 }
802 
doSetFileInfo(GFileInfo * pNewInfo)803 bool Content::doSetFileInfo(GFileInfo *pNewInfo)
804 {
805     g_assert (!mbTransient);
806 
807     bool bOk = true;
808     GFile *pFile = getGFile();
809     if(!g_file_set_attributes_from_info(pFile, pNewInfo, G_FILE_QUERY_INFO_NONE, nullptr, nullptr))
810         bOk = false;
811     return bOk;
812 }
813 
814 const int TRANSFER_BUFFER_SIZE = 65536;
815 
copyData(const css::uno::Reference<css::io::XInputStream> & xIn,const css::uno::Reference<css::io::XOutputStream> & xOut)816 void Content::copyData( const css::uno::Reference< css::io::XInputStream >& xIn,
817     const css::uno::Reference< css::io::XOutputStream >& xOut )
818 {
819     css::uno::Sequence< sal_Int8 > theData( TRANSFER_BUFFER_SIZE );
820 
821     g_return_if_fail( xIn.is() && xOut.is() );
822 
823     while ( xIn->readBytes( theData, TRANSFER_BUFFER_SIZE ) > 0 )
824         xOut->writeBytes( theData );
825 
826     xOut->closeOutput();
827 }
828 
feedSink(const css::uno::Reference<css::uno::XInterface> & xSink)829 bool Content::feedSink( const css::uno::Reference< css::uno::XInterface >& xSink )
830 {
831     if ( !xSink.is() )
832         return false;
833 
834     css::uno::Reference< css::io::XOutputStream > xOut(xSink, css::uno::UNO_QUERY );
835     css::uno::Reference< css::io::XActiveDataSink > xDataSink(xSink, css::uno::UNO_QUERY );
836 
837     if ( !xOut.is() && !xDataSink.is() )
838         return false;
839 
840     GError *pError=nullptr;
841     GFileInputStream *pStream = g_file_read(getGFile(), nullptr, &pError);
842     if (!pStream)
843        convertToException(pError, static_cast< cppu::OWeakObject * >(this));
844 
845     css::uno::Reference< css::io::XInputStream > xIn(
846         new comphelper::OSeekableInputWrapper(
847             new ::gio::InputStream(pStream), m_xContext));
848 
849     if ( xOut.is() )
850         copyData( xIn, xOut );
851 
852     if ( xDataSink.is() )
853         xDataSink->setInputStream( xIn );
854 
855     return true;
856 }
857 
open(const css::ucb::OpenCommandArgument2 & rOpenCommand,const css::uno::Reference<css::ucb::XCommandEnvironment> & xEnv)858 css::uno::Any Content::open(const css::ucb::OpenCommandArgument2 & rOpenCommand,
859     const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv )
860 {
861     bool bIsFolder = isFolder(xEnv);
862 
863     if (!g_file_query_exists(getGFile(), nullptr))
864     {
865         css::uno::Sequence< css::uno::Any > aArgs( 1 );
866         aArgs[ 0 ] <<= m_xIdentifier->getContentIdentifier();
867         css::uno::Any aErr = css::uno::makeAny(
868             css::ucb::InteractiveAugmentedIOException(OUString(), static_cast< cppu::OWeakObject * >( this ),
869                 css::task::InteractionClassification_ERROR,
870                 bIsFolder ? css::ucb::IOErrorCode_NOT_EXISTING_PATH : css::ucb::IOErrorCode_NOT_EXISTING, aArgs)
871         );
872 
873         ucbhelper::cancelCommandExecution(aErr, xEnv);
874     }
875 
876     css::uno::Any aRet;
877 
878     bool bOpenFolder = (
879         ( rOpenCommand.Mode == css::ucb::OpenMode::ALL ) ||
880         ( rOpenCommand.Mode == css::ucb::OpenMode::FOLDERS ) ||
881         ( rOpenCommand.Mode == css::ucb::OpenMode::DOCUMENTS )
882      );
883 
884     if ( bOpenFolder && bIsFolder )
885     {
886         css::uno::Reference< css::ucb::XDynamicResultSet > xSet
887             = new DynamicResultSet( m_xContext, this, rOpenCommand, xEnv );
888         aRet <<= xSet;
889     }
890     else if ( rOpenCommand.Sink.is() )
891     {
892         if (
893             ( rOpenCommand.Mode == css::ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
894             ( rOpenCommand.Mode == css::ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE )
895            )
896         {
897             ucbhelper::cancelCommandExecution(
898                 css::uno::makeAny ( css::ucb::UnsupportedOpenModeException
899                     ( OUString(), static_cast< cppu::OWeakObject * >( this ),
900                       sal_Int16( rOpenCommand.Mode ) ) ),
901                     xEnv );
902         }
903 
904         if ( !feedSink( rOpenCommand.Sink ) )
905         {
906             // Note: rOpenCommand.Sink may contain an XStream
907             //       implementation. Support for this type of
908             //       sink is optional...
909             SAL_WARN("ucb.ucp.gio", "Failed to load data from '" << m_xIdentifier->getContentIdentifier() << "'");
910 
911             ucbhelper::cancelCommandExecution(
912                 css::uno::makeAny (css::ucb::UnsupportedDataSinkException
913                     ( OUString(), static_cast< cppu::OWeakObject * >( this ),
914                       rOpenCommand.Sink ) ),
915                     xEnv );
916         }
917     }
918     else
919         SAL_INFO("ucb.ucp.gio", "Open falling through ...");
920     return aRet;
921 }
922 
execute(const css::ucb::Command & aCommand,sal_Int32,const css::uno::Reference<css::ucb::XCommandEnvironment> & xEnv)923 css::uno::Any SAL_CALL Content::execute(
924         const css::ucb::Command& aCommand,
925         sal_Int32 /*CommandId*/,
926         const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv )
927 {
928     SAL_INFO("ucb.ucp.gio", "Content::execute " << aCommand.Name);
929     css::uno::Any aRet;
930 
931     if ( aCommand.Name == "getPropertyValues" )
932     {
933         css::uno::Sequence< css::beans::Property > Properties;
934         if ( !( aCommand.Argument >>= Properties ) )
935             ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
936         aRet <<= getPropertyValues( Properties, xEnv );
937     }
938     else if ( aCommand.Name == "getPropertySetInfo" )
939         aRet <<= getPropertySetInfo( xEnv, false );
940     else if ( aCommand.Name == "getCommandInfo" )
941         aRet <<= getCommandInfo( xEnv, false );
942     else if ( aCommand.Name == "open" )
943     {
944         css::ucb::OpenCommandArgument2 aOpenCommand;
945         if ( !( aCommand.Argument >>= aOpenCommand ) )
946             ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
947         aRet = open( aOpenCommand, xEnv );
948     }
949     else if ( aCommand.Name == "transfer" )
950     {
951         css::ucb::TransferInfo transferArgs;
952         if ( !( aCommand.Argument >>= transferArgs ) )
953             ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
954         transfer( transferArgs, xEnv );
955     }
956     else if ( aCommand.Name == "setPropertyValues" )
957     {
958         css::uno::Sequence< css::beans::PropertyValue > aProperties;
959         if ( !( aCommand.Argument >>= aProperties ) || !aProperties.hasElements() )
960             ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
961         aRet <<= setPropertyValues( aProperties, xEnv );
962     }
963     else if (aCommand.Name == "createNewContent"
964              && isFolder( xEnv ) )
965     {
966         css::ucb::ContentInfo arg;
967         if ( !( aCommand.Argument >>= arg ) )
968                 ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
969         aRet <<= createNewContent( arg );
970     }
971     else if ( aCommand.Name == "insert" )
972     {
973         css::ucb::InsertCommandArgument arg;
974         if ( !( aCommand.Argument >>= arg ) )
975                 ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
976         insert( arg.Data, arg.ReplaceExisting, xEnv );
977     }
978     else if ( aCommand.Name == "delete" )
979     {
980         bool bDeletePhysical = false;
981         aCommand.Argument >>= bDeletePhysical;
982 
983         //If no delete physical, try and trashcan it, if that doesn't work go
984         //ahead and try and delete it anyway
985         if (!bDeletePhysical && !g_file_trash(getGFile(), nullptr, nullptr))
986                 bDeletePhysical = true;
987 
988         if (bDeletePhysical)
989         {
990             GError *pError = nullptr;
991             if (!g_file_delete( getGFile(), nullptr, &pError))
992                 ucbhelper::cancelCommandExecution(mapGIOError(pError), xEnv);
993         }
994 
995         destroy( bDeletePhysical );
996     }
997     else
998     {
999         SAL_WARN("ucb.ucp.gio", "Unknown command " << aCommand.Name);
1000 
1001         ucbhelper::cancelCommandExecution
1002             ( css::uno::makeAny( css::ucb::UnsupportedCommandException
1003               ( OUString(),
1004                 static_cast< cppu::OWeakObject * >( this ) ) ),
1005               xEnv );
1006     }
1007 
1008     return aRet;
1009 }
1010 
destroy(bool bDeletePhysical)1011 void Content::destroy( bool bDeletePhysical )
1012 {
1013     css::uno::Reference< css::ucb::XContent > xThis = this;
1014 
1015     deleted();
1016 
1017     ::gio::Content::ContentRefList aChildren;
1018     queryChildren( aChildren );
1019 
1020     for ( auto& rChild : aChildren )
1021     {
1022         rChild->destroy( bDeletePhysical );
1023     }
1024 }
1025 
insert(const css::uno::Reference<css::io::XInputStream> & xInputStream,bool bReplaceExisting,const css::uno::Reference<css::ucb::XCommandEnvironment> & xEnv)1026 void Content::insert(const css::uno::Reference< css::io::XInputStream > &xInputStream,
1027     bool bReplaceExisting, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv )
1028 {
1029     GError *pError = nullptr;
1030     GFileInfo *pInfo = getGFileInfo(xEnv);
1031 
1032     if ( pInfo &&
1033          g_file_info_has_attribute(pInfo, G_FILE_ATTRIBUTE_STANDARD_TYPE) &&
1034          g_file_info_get_file_type(pInfo) == G_FILE_TYPE_DIRECTORY )
1035     {
1036         SAL_INFO("ucb.ucp.gio", "Make directory");
1037         if( !g_file_make_directory( getGFile(), nullptr, &pError))
1038             ucbhelper::cancelCommandExecution(mapGIOError(pError), xEnv);
1039         return;
1040     }
1041 
1042     if ( !xInputStream.is() )
1043     {
1044         ucbhelper::cancelCommandExecution( css::uno::makeAny
1045             ( css::ucb::MissingInputStreamException
1046               ( OUString(), static_cast< cppu::OWeakObject * >( this ) ) ),
1047             xEnv );
1048     }
1049 
1050     GFileOutputStream* pOutStream = nullptr;
1051     if ( bReplaceExisting )
1052     {
1053         if (!(pOutStream = g_file_replace(getGFile(), nullptr, false, G_FILE_CREATE_PRIVATE, nullptr, &pError)))
1054             ucbhelper::cancelCommandExecution(mapGIOError(pError), xEnv);
1055     }
1056     else
1057     {
1058         if (!(pOutStream = g_file_create (getGFile(), G_FILE_CREATE_PRIVATE, nullptr, &pError)))
1059             ucbhelper::cancelCommandExecution(mapGIOError(pError), xEnv);
1060     }
1061 
1062     css::uno::Reference < css::io::XOutputStream > xOutput = new ::gio::OutputStream(pOutStream);
1063     copyData( xInputStream, xOutput );
1064 
1065     if (mbTransient)
1066     {
1067         mbTransient = false;
1068         inserted();
1069     }
1070 }
1071 
1072 const GFileCopyFlags DEFAULT_COPYDATA_FLAGS =
1073     static_cast<GFileCopyFlags>(G_FILE_COPY_OVERWRITE|G_FILE_COPY_TARGET_DEFAULT_PERMS);
1074 
transfer(const css::ucb::TransferInfo & aTransferInfo,const css::uno::Reference<css::ucb::XCommandEnvironment> & xEnv)1075 void Content::transfer( const css::ucb::TransferInfo& aTransferInfo, const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv )
1076 {
1077     OUString sDest = m_xIdentifier->getContentIdentifier();
1078     if (!sDest.endsWith("/")) {
1079         sDest += "/";
1080     }
1081     if (aTransferInfo.NewTitle.getLength())
1082         sDest += aTransferInfo.NewTitle;
1083     else
1084         sDest += OUString::createFromAscii(g_file_get_basename(getGFile()));
1085 
1086     GFile *pDest = g_file_new_for_uri(OUStringToOString(sDest, RTL_TEXTENCODING_UTF8).getStr());
1087     GFile *pSource = g_file_new_for_uri(OUStringToOString(aTransferInfo.SourceURL, RTL_TEXTENCODING_UTF8).getStr());
1088 
1089     bool bSuccess = false;
1090     GError *pError = nullptr;
1091     if (aTransferInfo.MoveData)
1092         bSuccess = g_file_move(pSource, pDest, G_FILE_COPY_OVERWRITE, nullptr, nullptr, nullptr, &pError);
1093     else
1094         bSuccess = g_file_copy(pSource, pDest, DEFAULT_COPYDATA_FLAGS, nullptr, nullptr, nullptr, &pError);
1095     g_object_unref(pSource);
1096     g_object_unref(pDest);
1097     if (!bSuccess)
1098         ucbhelper::cancelCommandExecution(mapGIOError(pError), xEnv);
1099 }
1100 
queryCreatableContentsInfo(const css::uno::Reference<css::ucb::XCommandEnvironment> & xEnv)1101 css::uno::Sequence< css::ucb::ContentInfo > Content::queryCreatableContentsInfo(
1102     const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv)
1103 {
1104     if ( isFolder( xEnv ) )
1105     {
1106         css::uno::Sequence< css::ucb::ContentInfo > seq(2);
1107 
1108         // Minimum set of props we really need
1109         css::uno::Sequence< css::beans::Property > props( 1 );
1110         props[0] = css::beans::Property(
1111             "Title",
1112             -1,
1113             cppu::UnoType<OUString>::get(),
1114             css::beans::PropertyAttribute::MAYBEVOID | css::beans::PropertyAttribute::BOUND );
1115 
1116         // file
1117         seq[0].Type       = GIO_FILE_TYPE;
1118         seq[0].Attributes = ( css::ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM |
1119                               css::ucb::ContentInfoAttribute::KIND_DOCUMENT );
1120         seq[0].Properties = props;
1121 
1122         // folder
1123         seq[1].Type       = GIO_FOLDER_TYPE;
1124         seq[1].Attributes = css::ucb::ContentInfoAttribute::KIND_FOLDER;
1125         seq[1].Properties = props;
1126 
1127         return seq;
1128     }
1129     else
1130     {
1131         return css::uno::Sequence< css::ucb::ContentInfo >();
1132     }
1133 }
1134 
queryCreatableContentsInfo()1135 css::uno::Sequence< css::ucb::ContentInfo > SAL_CALL Content::queryCreatableContentsInfo()
1136 {
1137     return queryCreatableContentsInfo( css::uno::Reference< css::ucb::XCommandEnvironment >() );
1138 }
1139 
1140 css::uno::Reference< css::ucb::XContent >
createNewContent(const css::ucb::ContentInfo & Info)1141     SAL_CALL Content::createNewContent( const css::ucb::ContentInfo& Info )
1142 {
1143     bool create_document;
1144     const char *name;
1145 
1146     if ( Info.Type == GIO_FILE_TYPE )
1147         create_document = true;
1148     else if ( Info.Type == GIO_FOLDER_TYPE )
1149         create_document = false;
1150     else
1151     {
1152         SAL_WARN("ucb.ucp.gio", "Failed to create new content '" << Info.Type << "'");
1153         return css::uno::Reference< css::ucb::XContent >();
1154     }
1155 
1156     SAL_INFO("ucb.ucp.gio", "createNewContent (" << create_document << ")");
1157     OUString aURL = m_xIdentifier->getContentIdentifier();
1158 
1159     if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
1160             aURL += "/";
1161 
1162     name = create_document ? "[New_Content]" : "[New_Collection]";
1163     aURL += OUString::createFromAscii( name );
1164 
1165     css::uno::Reference< css::ucb::XContentIdentifier > xId(new ::ucbhelper::ContentIdentifier(aURL));
1166 
1167     try
1168     {
1169         return new ::gio::Content( m_xContext, m_pProvider, xId, !create_document );
1170     } catch ( css::ucb::ContentCreationException & )
1171     {
1172             return css::uno::Reference< css::ucb::XContent >();
1173     }
1174 }
1175 
getTypes()1176 css::uno::Sequence< css::uno::Type > SAL_CALL Content::getTypes()
1177 {
1178     if ( isFolder( css::uno::Reference< css::ucb::XCommandEnvironment >() ) )
1179     {
1180         static cppu::OTypeCollection s_aFolderCollection
1181             (CPPU_TYPE_REF( css::lang::XTypeProvider ),
1182              CPPU_TYPE_REF( css::lang::XServiceInfo ),
1183              CPPU_TYPE_REF( css::lang::XComponent ),
1184              CPPU_TYPE_REF( css::ucb::XContent ),
1185              CPPU_TYPE_REF( css::ucb::XCommandProcessor ),
1186              CPPU_TYPE_REF( css::beans::XPropertiesChangeNotifier ),
1187              CPPU_TYPE_REF( css::ucb::XCommandInfoChangeNotifier ),
1188              CPPU_TYPE_REF( css::beans::XPropertyContainer ),
1189              CPPU_TYPE_REF( css::beans::XPropertySetInfoChangeNotifier ),
1190              CPPU_TYPE_REF( css::container::XChild ),
1191              CPPU_TYPE_REF( css::ucb::XContentCreator ) );
1192         return s_aFolderCollection.getTypes();
1193     }
1194     else
1195     {
1196         static cppu::OTypeCollection s_aFileCollection
1197             (CPPU_TYPE_REF( css::lang::XTypeProvider ),
1198              CPPU_TYPE_REF( css::lang::XServiceInfo ),
1199              CPPU_TYPE_REF( css::lang::XComponent ),
1200              CPPU_TYPE_REF( css::ucb::XContent ),
1201              CPPU_TYPE_REF( css::ucb::XCommandProcessor ),
1202              CPPU_TYPE_REF( css::beans::XPropertiesChangeNotifier ),
1203              CPPU_TYPE_REF( css::ucb::XCommandInfoChangeNotifier ),
1204              CPPU_TYPE_REF( css::beans::XPropertyContainer ),
1205              CPPU_TYPE_REF( css::beans::XPropertySetInfoChangeNotifier ),
1206              CPPU_TYPE_REF( css::container::XChild ) );
1207 
1208         return s_aFileCollection.getTypes();
1209     }
1210 }
1211 
getProperties(const css::uno::Reference<css::ucb::XCommandEnvironment> &)1212 css::uno::Sequence< css::beans::Property > Content::getProperties(
1213     const css::uno::Reference< css::ucb::XCommandEnvironment > & /*xEnv*/ )
1214 {
1215     static const css::beans::Property aGenericProperties[] =
1216     {
1217         css::beans::Property( "IsDocument",
1218             -1, cppu::UnoType<bool>::get(),
1219             css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
1220         css::beans::Property( "IsFolder",
1221             -1, cppu::UnoType<bool>::get(),
1222             css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
1223         css::beans::Property( "Title",
1224             -1, cppu::UnoType<OUString>::get(),
1225             css::beans::PropertyAttribute::BOUND ),
1226         css::beans::Property( "IsReadOnly",
1227             -1, cppu::UnoType<bool>::get(),
1228             css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
1229         css::beans::Property( "DateCreated",
1230             -1, cppu::UnoType<css::util::DateTime>::get(),
1231             css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
1232         css::beans::Property( "DateModified",
1233             -1, cppu::UnoType<css::util::DateTime>::get(),
1234             css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
1235         css::beans::Property( "Size",
1236             -1, cppu::UnoType<sal_Int64>::get(),
1237             css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
1238         css::beans::Property( "IsVolume",
1239             1, cppu::UnoType<bool>::get(),
1240             css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
1241         css::beans::Property( "IsCompactDisc",
1242             -1, cppu::UnoType<bool>::get(),
1243             css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
1244         css::beans::Property( "IsRemoveable",
1245             -1, cppu::UnoType<bool>::get(),
1246             css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
1247         css::beans::Property( "IsHidden",
1248             -1, cppu::UnoType<bool>::get(),
1249             css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY ),
1250         css::beans::Property( "CreatableContentsInfo",
1251             -1, cppu::UnoType<css::uno::Sequence< css::ucb::ContentInfo >>::get(),
1252             css::beans::PropertyAttribute::BOUND | css::beans::PropertyAttribute::READONLY )
1253     };
1254 
1255     const int nProps = SAL_N_ELEMENTS(aGenericProperties);
1256     return css::uno::Sequence< css::beans::Property > ( aGenericProperties, nProps );
1257 }
1258 
getCommands(const css::uno::Reference<css::ucb::XCommandEnvironment> & xEnv)1259 css::uno::Sequence< css::ucb::CommandInfo > Content::getCommands( const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv)
1260 {
1261     static const css::ucb::CommandInfo aCommandInfoTable[] =
1262     {
1263         // Required commands
1264         css::ucb::CommandInfo
1265         ( "getCommandInfo",
1266           -1, cppu::UnoType<void>::get() ),
1267         css::ucb::CommandInfo
1268         ( "getPropertySetInfo",
1269           -1, cppu::UnoType<void>::get() ),
1270         css::ucb::CommandInfo
1271         ( "getPropertyValues",
1272           -1, cppu::UnoType<css::uno::Sequence< css::beans::Property >>::get() ),
1273         css::ucb::CommandInfo
1274         ( "setPropertyValues",
1275           -1, cppu::UnoType<css::uno::Sequence< css::beans::PropertyValue >>::get() ),
1276 
1277         // Optional standard commands
1278         css::ucb::CommandInfo
1279         ( "delete",
1280           -1, cppu::UnoType<bool>::get() ),
1281         css::ucb::CommandInfo
1282         ( "insert",
1283           -1, cppu::UnoType<css::ucb::InsertCommandArgument>::get() ),
1284         css::ucb::CommandInfo
1285         ( "open",
1286           -1, cppu::UnoType<css::ucb::OpenCommandArgument2>::get() ),
1287 
1288         // Folder Only, omitted if not a folder
1289         css::ucb::CommandInfo
1290         ( "transfer",
1291           -1, cppu::UnoType<css::ucb::TransferInfo>::get() ),
1292         css::ucb::CommandInfo
1293         ( "createNewContent",
1294           -1, cppu::UnoType<css::ucb::ContentInfo>::get() )
1295     };
1296 
1297     const int nProps = SAL_N_ELEMENTS(aCommandInfoTable);
1298     return css::uno::Sequence< css::ucb::CommandInfo >(aCommandInfoTable, isFolder(xEnv) ? nProps : nProps - 2);
1299 }
1300 
1301 XTYPEPROVIDER_COMMON_IMPL( Content );
1302 
acquire()1303 void SAL_CALL Content::acquire() throw()
1304 {
1305     ContentImplHelper::acquire();
1306 }
1307 
release()1308 void SAL_CALL Content::release() throw()
1309 {
1310     ContentImplHelper::release();
1311 }
1312 
queryInterface(const css::uno::Type & rType)1313 css::uno::Any SAL_CALL Content::queryInterface( const css::uno::Type & rType )
1314 {
1315     css::uno::Any aRet = cppu::queryInterface( rType, static_cast< css::ucb::XContentCreator * >( this ) );
1316     return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface(rType);
1317 }
1318 
getImplementationName()1319 OUString SAL_CALL Content::getImplementationName()
1320 {
1321        return "com.sun.star.comp.GIOContent";
1322 }
1323 
getSupportedServiceNames()1324 css::uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
1325 {
1326        css::uno::Sequence<OUString> aSNS { "com.sun.star.ucb.GIOContent" };
1327        return aSNS;
1328 }
1329 
1330 }
1331 
1332 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1333