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