xref: /trunk/main/ucb/source/ucp/gvfs/gvfs_content.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_ucb.hxx"
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 
34 #include "osl/time.h"
35 #include <osl/diagnose.h>
36 
37 #include "osl/doublecheckedlocking.h"
38 
39 #include <com/sun/star/beans/PropertyValue.hpp>
40 #include <com/sun/star/beans/PropertyAttribute.hpp>
41 #include <com/sun/star/beans/PropertySetInfoChange.hpp>
42 #include <com/sun/star/beans/PropertySetInfoChangeEvent.hpp>
43 #include <com/sun/star/io/XActiveDataSink.hpp>
44 #include <com/sun/star/io/XOutputStream.hpp>
45 #include <com/sun/star/lang/IllegalAccessException.hpp>
46 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
47 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
48 #include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
49 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
50 #include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
51 #include <com/sun/star/ucb/InteractiveNetworkGeneralException.hpp>
52 #include <com/sun/star/ucb/InteractiveNetworkReadException.hpp>
53 #include <com/sun/star/ucb/InteractiveNetworkResolveNameException.hpp>
54 #include <com/sun/star/ucb/InteractiveNetworkWriteException.hpp>
55 #include <com/sun/star/ucb/NameClash.hpp>
56 #include <com/sun/star/ucb/NameClashException.hpp>
57 #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
58 #include <com/sun/star/ucb/OpenMode.hpp>
59 #include <com/sun/star/ucb/PostCommandArgument2.hpp>
60 #include <com/sun/star/ucb/TransferInfo.hpp>
61 #include <com/sun/star/ucb/XCommandInfo.hpp>
62 #include <com/sun/star/ucb/XPersistentPropertySet.hpp>
63 #include <com/sun/star/ucb/MissingInputStreamException.hpp>
64 #include <com/sun/star/ucb/MissingPropertiesException.hpp>
65 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
66 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
67 #include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
68 #include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
69 #include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
70 #include <com/sun/star/ucb/NameClashException.hpp>
71 #include <ucbhelper/contentidentifier.hxx>
72 #include <ucbhelper/propertyvalueset.hxx>
73 #include <ucbhelper/interactionrequest.hxx>
74 #include <ucbhelper/cancelcommandexecution.hxx>
75 #include <ucbhelper/simpleauthenticationrequest.hxx>
76 
77 const int TRANSFER_BUFFER_SIZE = 65536;
78 
79 /*
80  * NB. Name escaping is done only for URIs
81  * the 'Title' property is unescaped on set/get
82  */
83 #include <libgnomevfs/gnome-vfs-utils.h>
84 #include <libgnomevfs/gnome-vfs-result.h>
85 #include <libgnomevfs/gnome-vfs-standard-callbacks.h>
86 extern "C" { // missing in the header: doh.
87 #  include <libgnomevfs/gnome-vfs-module-callback.h>
88 }
89 
90 #include "gvfs_content.hxx"
91 #include "gvfs_provider.hxx"
92 #include "gvfs_directory.hxx"
93 #include "gvfs_stream.hxx"
94 
95 using namespace gvfs;
96 using namespace com::sun::star;
97 
98 #define CLEAR_INFO(info) memset((info), 0, sizeof ((info)[0]))
99 
100 
101 static char *
102 OUStringToGnome( const rtl::OUString &str )
103 {
104     rtl::OString aTempStr = rtl::OUStringToOString( str, RTL_TEXTENCODING_UTF8 );
105     return g_strdup( (const sal_Char *) aTempStr );
106 }
107 
108 static rtl::OUString
109 GnomeToOUString( const char *utf8_str)
110 {
111     if (!utf8_str)
112         return rtl::OUString();
113     else
114         return rtl::OUString( utf8_str, strlen( utf8_str ), RTL_TEXTENCODING_UTF8 );
115 }
116 
117 
118 Content::Content(
119           const uno::Reference< lang::XMultiServiceFactory >& rxSMgr,
120           ContentProvider* pProvider,
121           const uno::Reference< ucb::XContentIdentifier >& Identifier)
122   throw ( ucb::ContentCreationException )
123     : ContentImplHelper( rxSMgr, pProvider, Identifier ),
124       m_pProvider( pProvider ),
125       m_bTransient( sal_False )
126 {
127     CLEAR_INFO (&m_info);
128 #ifdef DEBUG
129     g_warning ("New Content ('%s')", getURI());
130 #endif
131 }
132 
133 Content::Content(
134     const uno::Reference< lang::XMultiServiceFactory >& rxSMgr,
135     ContentProvider                                   * pProvider,
136     const uno::Reference< ucb::XContentIdentifier >&    Identifier,
137     sal_Bool                                            IsFolder)
138         throw ( ucb::ContentCreationException )
139     : ContentImplHelper( rxSMgr, pProvider, Identifier ),
140       m_pProvider( pProvider ),
141       m_bTransient( sal_True )
142 {
143     CLEAR_INFO (&m_info);
144 
145 #ifdef DEBUG
146     g_warning ("New Transient content ('%s') (%d)", getURI(), IsFolder);
147 #endif
148 //	m_info.name = FIXME: set name ?
149     m_info.valid_fields = GNOME_VFS_FILE_INFO_FIELDS_TYPE;
150     m_info.type = IsFolder ? GNOME_VFS_FILE_TYPE_DIRECTORY :
151                          GNOME_VFS_FILE_TYPE_REGULAR;
152 }
153 
154 // virtual
155 Content::~Content()
156 {
157     gnome_vfs_file_info_clear( &m_info );
158 }
159 
160 //
161 // XInterface methods.
162 //
163 
164 void SAL_CALL Content::acquire()
165     throw( )
166 {
167     ContentImplHelper::acquire();
168 }
169 void SAL_CALL Content::release()
170     throw( )
171 {
172     ContentImplHelper::release();
173 }
174 uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType )
175     throw ( uno::RuntimeException )
176 {
177     // Note: isFolder may require network activities! So call it only
178     //       if it is really necessary!!!
179     uno::Any aRet = cppu::queryInterface( rType,
180                         static_cast< ucb::XContentCreator * >( this ) );
181     if ( aRet.hasValue() )
182             return isFolder( uno::Reference< ucb::XCommandEnvironment >() )
183             ? aRet : uno::Any();
184     else
185         return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface( rType );
186 }
187 
188 //
189 // XTypeProvider methods.
190 //
191 
192 XTYPEPROVIDER_COMMON_IMPL( Content );
193 
194 uno::Sequence< uno::Type > SAL_CALL Content::getTypes()
195     throw( uno::RuntimeException )
196 {
197     static cppu::OTypeCollection *pFolderCollection = NULL;
198     static cppu::OTypeCollection *pFileCollection = NULL;
199 
200     if (!pFolderCollection) {
201         osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
202 
203         if (!pFolderCollection) {
204             static cppu::OTypeCollection aFolderCollection
205                 (CPPU_TYPE_REF( lang::XTypeProvider ),
206                  CPPU_TYPE_REF( lang::XServiceInfo ),
207                  CPPU_TYPE_REF( lang::XComponent ),
208                  CPPU_TYPE_REF( ucb::XContent ),
209                  CPPU_TYPE_REF( ucb::XCommandProcessor ),
210                  CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
211                  CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
212                  CPPU_TYPE_REF( beans::XPropertyContainer ),
213                  CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
214                  CPPU_TYPE_REF( container::XChild ),
215                  CPPU_TYPE_REF( ucb::XContentCreator ) ); // !!
216             static cppu::OTypeCollection aFileCollection
217                 (CPPU_TYPE_REF( lang::XTypeProvider ),
218                  CPPU_TYPE_REF( lang::XServiceInfo ),
219                  CPPU_TYPE_REF( lang::XComponent ),
220                  CPPU_TYPE_REF( ucb::XContent ),
221                  CPPU_TYPE_REF( ucb::XCommandProcessor ),
222                  CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
223                  CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
224                  CPPU_TYPE_REF( beans::XPropertyContainer ),
225                  CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
226                  CPPU_TYPE_REF( container::XChild ) );
227 
228             pFolderCollection = &aFolderCollection;
229             pFileCollection = &aFileCollection;
230             OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
231         }
232     }
233     else {
234         OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
235     }
236 
237     if ( isFolder( uno::Reference< ucb::XCommandEnvironment >() ) )
238         return pFolderCollection->getTypes();
239     else
240         return pFileCollection->getTypes();
241 }
242 
243 //
244 // XServiceInfo methods.
245 //
246 
247 rtl::OUString SAL_CALL Content::getImplementationName()
248     throw( uno::RuntimeException )
249 {
250     return rtl::OUString::createFromAscii("com.sun.star.comp.GnomeVFSContent" );
251 }
252 
253 uno::Sequence< rtl::OUString > SAL_CALL Content::getSupportedServiceNames()
254     throw( uno::RuntimeException )
255 {
256     uno::Sequence< rtl::OUString > aSNS( 1 );
257     aSNS.getArray()[ 0 ] = rtl::OUString::createFromAscii(
258         "com.sun.star.ucb.GnomeVFSContent" );
259     return aSNS;
260 }
261 
262 //
263 // XContent methods.
264 //
265 
266 rtl::OUString SAL_CALL Content::getContentType()
267     throw( uno::RuntimeException )
268 {
269     if ( isFolder( uno::Reference< ucb::XCommandEnvironment >() ) )
270         return rtl::OUString::createFromAscii( GVFS_FOLDER_TYPE );
271     else
272         return rtl::OUString::createFromAscii( GVFS_FILE_TYPE );
273 }
274 
275 //
276 // XCommandProcessor methods.
277 //
278 
279 uno::Any Content::getBadArgExcept()
280 {
281     return uno::makeAny( lang::IllegalArgumentException
282                  ( rtl::OUString::createFromAscii( "Wrong argument type!" ),
283                    static_cast< cppu::OWeakObject * >( this ),
284                    -1 ) );
285 }
286 
287 #include <stdio.h>
288 
289 uno::Any SAL_CALL Content::execute(
290         const ucb::Command& aCommand,
291         sal_Int32 /*CommandId*/,
292         const uno::Reference< ucb::XCommandEnvironment >& xEnv )
293     throw( uno::Exception,
294            ucb::CommandAbortedException,
295            uno::RuntimeException )
296 {
297     uno::Any aRet;
298 
299 #ifdef DEBUG
300     {
301         uno::Reference< task::XInteractionHandler > xIH;
302 
303         if ( xEnv.is() )
304             xIH = xEnv->getInteractionHandler();
305         g_warning( "Execute command: '%s' with %s interaction env",
306                OUStringToGnome( aCommand.Name ),
307                xIH.is() ? "" : "NO" );
308     }
309 #endif
310 
311 #define COMMAND_IS(cmd,name) ( (cmd).Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( name ) ) )
312 
313     if ( COMMAND_IS( aCommand, "getPropertyValues" ) ) {
314         uno::Sequence< beans::Property > Properties;
315 
316         if ( !( aCommand.Argument >>= Properties ) )
317             ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
318 
319         aRet <<= getPropertyValues( Properties, xEnv );
320 
321     } else if ( COMMAND_IS( aCommand, "setPropertyValues" ) ) {
322         uno::Sequence< beans::PropertyValue > aProperties;
323 
324         if ( !( aCommand.Argument >>= aProperties ) ||
325              !aProperties.getLength() )
326             ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
327 
328         aRet <<= setPropertyValues( aProperties, xEnv );
329 
330     } else if ( COMMAND_IS( aCommand, "getPropertySetInfo" ) ) {
331         aRet <<= getPropertySetInfo( xEnv, sal_False );
332 
333     } else if ( COMMAND_IS( aCommand, "getCommandInfo" ) ) {
334         aRet <<= getCommandInfo( xEnv, sal_False );
335 
336     } else if ( COMMAND_IS( aCommand, "open" ) ) {
337         rtl::OUString str = m_xIdentifier->getContentIdentifier();
338         rtl::OString stra(
339             str.getStr(),
340             str.getLength(),
341             RTL_TEXTENCODING_UTF8);
342 
343         ucb::OpenCommandArgument2 aOpenCommand;
344         if ( !( aCommand.Argument >>= aOpenCommand ) )
345             ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
346 
347         sal_Bool bOpenFolder =
348             ( ( aOpenCommand.Mode == ucb::OpenMode::ALL ) ||
349               ( aOpenCommand.Mode == ucb::OpenMode::FOLDERS ) ||
350               ( aOpenCommand.Mode == ucb::OpenMode::DOCUMENTS ) );
351 
352         if ( bOpenFolder && isFolder( xEnv ) ) {
353             uno::Reference< ucb::XDynamicResultSet > xSet
354                 = new DynamicResultSet(m_xSMgr, this, aOpenCommand, xEnv );
355             aRet <<= xSet;
356 
357         } else if ( aOpenCommand.Sink.is() ) {
358 
359             if ( ( aOpenCommand.Mode
360                    == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
361                  ( aOpenCommand.Mode
362                    == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) ) {
363                 ucbhelper::cancelCommandExecution
364                     ( uno::makeAny ( ucb::UnsupportedOpenModeException
365                              ( rtl::OUString(),
366                                static_cast< cppu::OWeakObject * >( this ),
367                                sal_Int16( aOpenCommand.Mode ) ) ),
368                       xEnv );
369             }
370             if ( !feedSink( aOpenCommand.Sink, xEnv ) ) {
371                 // Note: aOpenCommand.Sink may contain an XStream
372                 //       implementation. Support for this type of
373                 //       sink is optional...
374 #ifdef DEBUG
375                 g_warning ("Failed to load data from '%s'", getURI());
376 #endif
377                 ucbhelper::cancelCommandExecution
378                     ( uno::makeAny (ucb::UnsupportedDataSinkException
379                             ( rtl::OUString(),
380                               static_cast< cppu::OWeakObject * >( this ),
381                               aOpenCommand.Sink ) ),
382                       xEnv );
383             }
384         }
385 #ifdef DEBUG
386         else
387             g_warning ("Open falling through ...");
388 #endif
389 
390     } else if ( COMMAND_IS( aCommand, "createNewContent" ) && isFolder( xEnv ) ) {
391         ucb::ContentInfo arg;
392         if ( !( aCommand.Argument >>= arg ) )
393             ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
394 
395         aRet <<= createNewContent( arg );
396 
397     } else if ( COMMAND_IS( aCommand, "insert" ) ) {
398         ucb::InsertCommandArgument arg;
399         if ( !( aCommand.Argument >>= arg ) )
400             ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
401 
402         insert( arg.Data, arg.ReplaceExisting, xEnv );
403 
404     } else if ( COMMAND_IS( aCommand, "delete" ) ) {
405 
406         sal_Bool bDeletePhysical = sal_False;
407         aCommand.Argument >>= bDeletePhysical;
408 
409         ::rtl::OString aURI = getOURI();
410         GnomeVFSResult result = gnome_vfs_unlink ((const sal_Char *) aURI);
411 
412         if (result != GNOME_VFS_OK)
413             cancelCommandExecution( result, xEnv, sal_True );
414 
415         destroy( bDeletePhysical );
416 
417     } else if ( COMMAND_IS( aCommand, "transfer" ) && isFolder( xEnv ) ) {
418         ucb::TransferInfo transferArgs;
419 
420         if ( !( aCommand.Argument >>= transferArgs ) )
421             ucbhelper::cancelCommandExecution( getBadArgExcept(), xEnv );
422 
423         transfer( transferArgs, xEnv );
424 
425     } else { // Unsuported
426 #ifdef DEBUG
427         g_warning( "Unsupported command: '%s'",
428                OUStringToGnome( aCommand.Name ) );
429 #endif
430         ucbhelper::cancelCommandExecution
431             ( uno::makeAny( ucb::UnsupportedCommandException
432                     ( rtl::OUString(),
433                       static_cast< cppu::OWeakObject * >( this ) ) ),
434               xEnv );
435     }
436 #undef COMMAND_IS
437 
438     return aRet;
439 }
440 
441 void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
442     throw( uno::RuntimeException )
443 {
444     // FIXME: we should use the GnomeVFSCancellation APIs here ...
445 }
446 
447 //
448 // XContentCreator methods.
449 //
450 
451 uno::Sequence< ucb::ContentInfo > Content::queryCreatableContentsInfo(
452     const uno::Reference< ucb::XCommandEnvironment >& xEnv)
453             throw( uno::RuntimeException )
454 {
455     if ( isFolder( xEnv ) )
456     {
457         uno::Sequence< ucb::ContentInfo > seq(2);
458 
459         // Minimum set of props we really need
460         uno::Sequence< beans::Property > props( 1 );
461         props[0] = beans::Property(
462             rtl::OUString::createFromAscii( "Title" ),
463             -1,
464             getCppuType( static_cast< rtl::OUString* >( 0 ) ),
465             beans::PropertyAttribute::MAYBEVOID | beans::PropertyAttribute::BOUND );
466 
467         // file
468         seq[0].Type       = rtl::OUString::createFromAscii( GVFS_FILE_TYPE );
469         seq[0].Attributes = ( ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM |
470                               ucb::ContentInfoAttribute::KIND_DOCUMENT );
471         seq[0].Properties = props;
472 
473         // folder
474         seq[1].Type       = rtl::OUString::createFromAscii( GVFS_FOLDER_TYPE );
475         seq[1].Attributes = ucb::ContentInfoAttribute::KIND_FOLDER;
476         seq[1].Properties = props;
477 
478         return seq;
479     }
480     else
481     {
482         return uno::Sequence< ucb::ContentInfo >();
483     }
484 }
485 
486 uno::Sequence< ucb::ContentInfo > SAL_CALL Content::queryCreatableContentsInfo()
487             throw( uno::RuntimeException )
488 {
489     return queryCreatableContentsInfo( uno::Reference< ucb::XCommandEnvironment >() );
490 }
491 
492 uno::Reference< ucb::XContent > SAL_CALL
493 Content::createNewContent( const ucb::ContentInfo& Info )
494     throw( uno::RuntimeException )
495 {
496     bool create_document;
497     const char *name;
498 
499         if ( Info.Type.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( GVFS_FILE_TYPE ) ) )
500         create_document = true;
501     else if ( Info.Type.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( GVFS_FOLDER_TYPE ) ) )
502         create_document = false;
503     else {
504 #ifdef DEBUG
505         g_warning( "Failed to create new content '%s'",
506                OUStringToGnome( Info.Type ) );
507 #endif
508         return uno::Reference< ucb::XContent >();
509     }
510 
511 #ifdef DEBUG
512     g_warning( "createNewContent (%d)", (int) create_document );
513 #endif
514 
515         rtl::OUString aURL = getOUURI();
516 
517     if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
518         aURL += rtl::OUString::createFromAscii( "/" );
519 
520     name = create_document ? "[New_Content]" : "[New_Collection]";
521     // This looks problematic to me cf. webdav
522     aURL += rtl::OUString::createFromAscii( name );
523 
524         uno::Reference< ucb::XContentIdentifier > xId
525         ( new ::ucbhelper::ContentIdentifier( m_xSMgr, aURL ) );
526 
527         try {
528         return new ::gvfs::Content( m_xSMgr, m_pProvider, xId, !create_document );
529     } catch ( ucb::ContentCreationException & ) {
530         return uno::Reference< ucb::XContent >();
531         }
532 }
533 
534 rtl::OUString Content::getParentURL()
535 {
536     rtl::OUString aParentURL;
537     // <scheme>://              -> ""
538     // <scheme>://foo           -> ""
539     // <scheme>://foo/          -> ""
540     // <scheme>://foo/bar       -> <scheme>://foo/
541     // <scheme>://foo/bar/      -> <scheme>://foo/
542     // <scheme>://foo/bar/abc   -> <scheme>://foo/bar/
543 
544     rtl::OUString aURL = getOUURI();
545 
546     sal_Int32 nPos = aURL.lastIndexOf( '/' );
547     if ( nPos == ( aURL.getLength() - 1 ) ) {
548         // Trailing slash found. Skip.
549         nPos = aURL.lastIndexOf( '/', nPos );
550     }
551 
552     sal_Int32 nPos1 = aURL.lastIndexOf( '/', nPos );
553     if ( nPos1 != -1 )
554         nPos1 = aURL.lastIndexOf( '/', nPos1 );
555 
556     if ( nPos1 != -1 )
557         aParentURL = rtl::OUString( aURL.copy( 0, nPos + 1 ) );
558 
559 #ifdef DEBUG
560     g_warning ("getParentURL '%s' -> '%s'",
561            getURI(), (const sal_Char *) rtl::OUStringToOString
562                ( aParentURL, RTL_TEXTENCODING_UTF8 ) );
563 #endif
564 
565     return aParentURL;
566 }
567 
568 static util::DateTime
569 getDateFromUnix (time_t t)
570 {
571     TimeValue tv;
572     tv.Nanosec = 0;
573     tv.Seconds = t;
574     oslDateTime dt;
575 
576     if ( osl_getDateTimeFromTimeValue( &tv, &dt ) )
577         return util::DateTime( 0, dt.Seconds, dt.Minutes, dt.Hours,
578                                dt.Day, dt.Month, dt.Year);
579     else
580         return util::DateTime();
581 }
582 
583 uno::Reference< sdbc::XRow > Content::getPropertyValues(
584                 const uno::Sequence< beans::Property >& rProperties,
585                 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
586 {
587     int nProps;
588     GnomeVFSResult result;
589     uno::Sequence< beans::Property > allProperties;
590 
591     if( ( result = getInfo( xEnv ) ) != GNOME_VFS_OK )
592         cancelCommandExecution( result, xEnv, sal_False );
593 
594     const beans::Property* pProps;
595 
596     if( rProperties.getLength() ) {
597         nProps = rProperties.getLength();
598         pProps = rProperties.getConstArray();
599     } else {
600         allProperties = getPropertySetInfo( xEnv )->getProperties();
601         nProps = allProperties.getLength();
602         pProps = allProperties.getConstArray();
603     }
604 
605     rtl::Reference< ::ucbhelper::PropertyValueSet > xRow
606         = new ::ucbhelper::PropertyValueSet( m_xSMgr );
607 
608         osl::Guard< osl::Mutex > aGuard( m_aMutex );
609     for( sal_Int32 n = 0; n < nProps; ++n ) {
610         const beans::Property& rProp = pProps[ n ];
611 
612         if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Title" ) ) ) {
613             if (m_info.name && m_info.name[0] == '/')
614                 g_warning ("Odd NFS title on item '%s' == '%s'",
615                        getURI(), m_info.name);
616             xRow->appendString( rProp, GnomeToOUString( m_info.name ) );
617         }
618 
619         else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "ContentType" ) ) )
620             xRow->appendString( rProp, getContentType () );
621 
622         else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsDocument" ) ) ) {
623             if (m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE)
624                 xRow->appendBoolean( rProp, ( m_info.type == GNOME_VFS_FILE_TYPE_REGULAR ||
625                                   m_info.type == GNOME_VFS_FILE_TYPE_UNKNOWN ) );
626             else
627                 xRow->appendVoid( rProp );
628         }
629         else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsFolder" ) ) ) {
630             if (m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE)
631                 xRow->appendBoolean( rProp, ( m_info.type == GNOME_VFS_FILE_TYPE_DIRECTORY ) );
632             else
633                 xRow->appendVoid( rProp );
634         }
635         else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsReadOnly" ) ) ) {
636 
637             GnomeVFSFileInfo* fileInfo = gnome_vfs_file_info_new ();
638 
639             ::rtl::OString aURI = getOURI();
640             gnome_vfs_get_file_info
641                 ( (const sal_Char *)aURI, fileInfo,
642                         GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS );
643 
644             if (fileInfo->valid_fields & GNOME_VFS_FILE_INFO_FIELDS_ACCESS) {
645                 bool read_only = true;
646 
647                 if (fileInfo->permissions & GNOME_VFS_PERM_ACCESS_WRITABLE)
648                                         read_only = false;
649 
650                 xRow->appendBoolean( rProp, read_only );
651             } else
652                 xRow->appendVoid( rProp );
653             gnome_vfs_file_info_unref (fileInfo);
654         }
655         else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Size" ) ) ) {
656             if (m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE)
657                 xRow->appendLong( rProp, m_info.size );
658             else
659                 xRow->appendVoid( rProp );
660         }
661         else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsHidden" ) ) )
662             xRow->appendBoolean( rProp, ( m_info.name && m_info.name[0] == '.' ) );
663 
664         else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsVolume" ) ) ||
665              rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsCompactDisk" ) ) )
666             xRow->appendBoolean( rProp, sal_False );
667 
668         else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DateCreated" ) ) ) {
669             if (m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_CTIME)
670                 xRow->appendTimestamp( rProp, getDateFromUnix( m_info.ctime ) );
671             else
672                 xRow->appendVoid( rProp );
673         }
674 
675         else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DateModified" ) ) ) {
676             if (m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_MTIME)
677                 xRow->appendTimestamp( rProp, getDateFromUnix( m_info.mtime ) );
678             else
679                 xRow->appendVoid( rProp );
680         }
681 
682         else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "MediaType" ) ) ) {
683             // We do this by sniffing in gnome-vfs; rather expensively.
684 #ifdef DEBUG
685             g_warning ("FIXME: Requested mime-type - an expensive op. indeed!");
686 #endif
687             xRow->appendVoid( rProp );
688         } else if (rProp.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "CreatableContentsInfo" ) ) )
689             xRow->appendObject( rProp, uno::makeAny( queryCreatableContentsInfo( xEnv ) ) );
690 
691         else {
692             xRow->appendVoid( rProp );
693         }
694     }
695 #ifdef DEBUG
696     g_warning ("getPropertyValues on '%s' %d properties returned (of %d)",
697            getURI(), (int)xRow->getLength(), (int)nProps);
698 #endif
699 
700     return uno::Reference< sdbc::XRow >( xRow.get() );
701 }
702 
703 static lang::IllegalAccessException
704 getReadOnlyException( Content *ctnt )
705 {
706     return lang::IllegalAccessException
707         ( rtl::OUString::createFromAscii( "Property is read-only!" ),
708           static_cast< cppu::OWeakObject * >( ctnt ) );
709 }
710 
711 rtl::OUString
712 Content::makeNewURL( const char */*newName*/ )
713 {
714     rtl::OUString aNewURL = getParentURL();
715     if ( aNewURL.lastIndexOf( '/' ) != ( aNewURL.getLength() - 1 ) )
716         aNewURL += rtl::OUString::createFromAscii( "/" );
717 
718     char *name = gnome_vfs_escape_string( m_info.name );
719     aNewURL += GnomeToOUString( name );
720     g_free( name );
721 
722     return aNewURL;
723 }
724 
725 // This is slightly complicated by needing to support either 'move' or 'setname'
726 GnomeVFSResult
727 Content::doSetFileInfo( const GnomeVFSFileInfo *newInfo,
728             GnomeVFSSetFileInfoMask setMask,
729             const uno::Reference< ucb::XCommandEnvironment >& /*xEnv*/ )
730 {
731     GnomeVFSResult result = GNOME_VFS_OK;
732 
733     g_assert (!m_bTransient);
734 
735     ::rtl::OString aURI = getOURI();
736 
737         osl::Guard< osl::Mutex > aGuard( m_aMutex );
738 
739     // The simple approach:
740     if( setMask != GNOME_VFS_SET_FILE_INFO_NONE )
741         result = gnome_vfs_set_file_info // missed a const in the API there
742             ( (const sal_Char *) aURI, (GnomeVFSFileInfo *)newInfo, setMask );
743 
744     if ( result == GNOME_VFS_ERROR_NOT_SUPPORTED &&
745          ( setMask & GNOME_VFS_SET_FILE_INFO_NAME ) ) {
746         // Try a move instead
747 #ifdef DEBUG
748         g_warning( "SetFileInfo not supported on '%s'", getURI() );
749 #endif
750 
751         char *newURI = OUStringToGnome( makeNewURL( newInfo->name ) );
752 
753         result = gnome_vfs_move ((const sal_Char *)aURI, newURI, FALSE);
754 
755         g_free (newURI);
756     }
757 
758     return result;
759 }
760 
761 
762 uno::Sequence< uno::Any > Content::setPropertyValues(
763                 const uno::Sequence< beans::PropertyValue >& rValues,
764                 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
765 {
766     rtl::OUString    aNewTitle;
767     GnomeVFSFileInfo newInfo;
768     int              setMask = GNOME_VFS_SET_FILE_INFO_NONE;
769 
770     getInfo( xEnv );
771 
772     osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
773 
774     gnome_vfs_file_info_copy( &newInfo, &m_info );
775 
776     Authentication aAuth( xEnv );
777 
778     int nChanged = 0, nTitlePos = 0;
779     uno::Sequence< uno::Any > aRet( rValues.getLength() );
780     uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
781 
782     beans::PropertyChangeEvent aEvent;
783     aEvent.Source         = static_cast< cppu::OWeakObject * >( this );
784     aEvent.Further        = sal_False;
785     aEvent.PropertyHandle = -1;
786     // aEvent.PropertyName = fill in later ...
787     // aEvent.OldValue     =
788     // aEvent.NewValue     =
789 
790     int nCount = rValues.getLength();
791     const beans::PropertyValue* pValues = rValues.getConstArray();
792 
793     for ( sal_Int32 n = 0; n < nCount; ++n ) {
794         const beans::PropertyValue& rValue = pValues[ n ];
795 
796 #ifdef DEBUG
797         g_warning( "Set prop '%s'", OUStringToGnome( rValue.Name ) );
798 #endif
799         if ( rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "ContentType" ) ) ||
800              rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "MediaType" ) ) ||
801              rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsDocument" ) ) ||
802              rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "IsFolder" ) ) ||
803              rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Size" ) ) ||
804              rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "CreatableContentsInfo" ) ) )
805             aRet[ n ] <<= getReadOnlyException( this );
806 
807         else if ( rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Title" ) ) ) {
808             if ( rValue.Value >>= aNewTitle ) {
809                 if ( aNewTitle.getLength() <= 0 )
810                     aRet[ n ] <<= lang::IllegalArgumentException
811                         ( rtl::OUString::createFromAscii( "Empty title not allowed!" ),
812                           static_cast< cppu::OWeakObject * >( this ), -1 );
813                 else {
814                     char *newName = OUStringToGnome( aNewTitle );
815 
816                     if( !newName || !m_info.name || strcmp( newName, m_info.name ) ) {
817 #ifdef DEBUG
818                         g_warning ("Set new name to '%s'", newName);
819 #endif
820 
821                         aEvent.PropertyName = rtl::OUString::createFromAscii( "Title" );
822                         aEvent.OldValue     = uno::makeAny( GnomeToOUString( newInfo.name ) );
823                         aEvent.NewValue     = uno::makeAny( aNewTitle );
824                         aChanges.getArray()[ nChanged ] = aEvent;
825                         nTitlePos = nChanged++;
826 
827                         newInfo.name = newName;
828                         setMask |= GNOME_VFS_SET_FILE_INFO_NAME;
829                     } else // same name
830                         g_free (newName);
831                 }
832             } else
833                 aRet[ n ] <<= beans::IllegalTypeException
834                     ( rtl::OUString::createFromAscii( "Property value has wrong type!" ),
835                       static_cast< cppu::OWeakObject * >( this ) );
836 
837         } else if ( rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DateCreated" ) ) ||
838                 rValue.Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DateModified" ) ) ) {
839             // FIXME: should be able to set the timestamps
840             aRet[ n ] <<= getReadOnlyException( this );
841         } else {
842 #ifdef DEBUG
843             g_warning( "Unhandled property '%s'", OUStringToGnome( rValue.Name ) );
844 #endif
845             aRet[ n ] <<= getReadOnlyException( this );
846         }
847     }
848 
849     GnomeVFSResult result = GNOME_VFS_OK;
850 
851     if ( !m_bTransient &&
852          ( result = doSetFileInfo( &newInfo,
853                        (GnomeVFSSetFileInfoMask) setMask,
854                        xEnv ) ) != GNOME_VFS_OK ) {
855         for (int i = 0; i < nChanged; i++)
856             aRet[ i ] <<= mapVFSException( result, sal_True );
857 
858     }
859 
860     if ( result == GNOME_VFS_OK) {
861         gnome_vfs_file_info_copy( &m_info, &newInfo );
862 
863         if ( setMask & GNOME_VFS_SET_FILE_INFO_NAME ) {
864             uno::Reference< ucb::XContentIdentifier > xNewId
865                 = new ::ucbhelper::ContentIdentifier(
866                     m_xSMgr, makeNewURL( newInfo.name ) );
867 
868             aGuard.clear();
869             if (!exchangeIdentity( xNewId ) )
870                 aRet[ nTitlePos ] <<= uno::Exception
871                     ( rtl::OUString::createFromAscii( "Exchange failed!" ),
872                       static_cast< cppu::OWeakObject * >( this ) );
873         }
874     }
875 
876     gnome_vfs_file_info_clear( &newInfo );
877 
878     if ( nChanged > 0 ) {
879             aGuard.clear();
880         aChanges.realloc( nChanged );
881         notifyPropertiesChange( aChanges );
882     }
883 
884     return aRet;
885 }
886 
887 void Content::queryChildren( ContentRefList& rChildren )
888 {
889     // Obtain a list with a snapshot of all currently instanciated contents
890     // from provider and extract the contents which are direct children
891     // of this content.
892 
893     ::ucbhelper::ContentRefList aAllContents;
894     m_xProvider->queryExistingContents( aAllContents );
895 
896     rtl::OUString aURL = getOUURI();
897     sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
898 
899     if ( nURLPos != ( aURL.getLength() - 1 ) )
900         aURL += rtl::OUString::createFromAscii( "/" );
901 
902     sal_Int32 nLen = aURL.getLength();
903 
904     ::ucbhelper::ContentRefList::const_iterator it  = aAllContents.begin();
905     ::ucbhelper::ContentRefList::const_iterator end = aAllContents.end();
906 
907     while ( it != end ) {
908         ::ucbhelper::ContentImplHelperRef xChild = (*it);
909         rtl::OUString aChildURL
910             = xChild->getIdentifier()->getContentIdentifier();
911 
912         // Is aURL a prefix of aChildURL?
913         if ( ( aChildURL.getLength() > nLen ) &&
914              ( aChildURL.compareTo( aURL, nLen ) == 0 ) ) {
915             sal_Int32 nPos = nLen;
916             nPos = aChildURL.indexOf( '/', nPos );
917 
918             if ( ( nPos == -1 ) ||
919                  ( nPos == ( aChildURL.getLength() - 1 ) ) ) {
920                 // No further slashes / only a final slash. It's a child!
921                 rChildren.push_back( ::gvfs::Content::ContentRef
922                              (static_cast< ::gvfs::Content * >(xChild.get() ) ) );
923             }
924         }
925         ++it;
926     }
927 }
928 
929 void Content::insert(
930         const uno::Reference< io::XInputStream >               &xInputStream,
931         sal_Bool                                                bReplaceExisting,
932         const uno::Reference< ucb::XCommandEnvironment > &xEnv )
933         throw( uno::Exception )
934 {
935     osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
936 
937 #ifdef DEBUG
938     g_warning( "Insert '%s' (%d) (0x%x:%d)", getURI(), bReplaceExisting,
939            m_info.valid_fields, m_info.type );
940 #endif
941 
942     GnomeVFSResult result = getInfo( xEnv );
943     // a racy design indeed.
944     if( !bReplaceExisting && !m_bTransient &&
945         result != GNOME_VFS_ERROR_NOT_FOUND) {
946 #ifdef DEBUG
947         g_warning ("Nasty error inserting to '%s' ('%s')",
948                getURI(), gnome_vfs_result_to_string( result ));
949 #endif
950         cancelCommandExecution( GNOME_VFS_ERROR_FILE_EXISTS, xEnv, sal_True );
951     }
952 
953     if ( m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE &&
954          m_info.type == GNOME_VFS_FILE_TYPE_DIRECTORY ) {
955         ::rtl::OString aURI = getOURI();
956         int perm;
957 
958         perm = ( GNOME_VFS_PERM_USER_ALL |
959              GNOME_VFS_PERM_GROUP_READ |
960              GNOME_VFS_PERM_OTHER_READ );
961 
962 #ifdef DEBUG
963         g_warning ("Make directory");
964 #endif
965         result = gnome_vfs_make_directory( (const sal_Char *) aURI, perm );
966 
967         if( result != GNOME_VFS_OK )
968             cancelCommandExecution( result, xEnv, sal_True );
969 
970         return;
971     }
972 
973     if ( !xInputStream.is() ) {
974         // FIXME: slightly unclear whether to accept this and create an empty file
975         ucbhelper::cancelCommandExecution
976             ( uno::makeAny
977               ( ucb::MissingInputStreamException
978                 ( rtl::OUString(),
979                   static_cast< cppu::OWeakObject * >( this ) ) ),
980               xEnv );
981     }
982 
983     GnomeVFSHandle *handle = NULL;
984     ::rtl::OString aURI = getOURI();
985 
986     result = GNOME_VFS_OK;
987     if ( bReplaceExisting ) {
988         Authentication aAuth( xEnv );
989         result = gnome_vfs_open( &handle, (const sal_Char *)aURI,
990                      GNOME_VFS_OPEN_WRITE );
991     }
992 
993     if ( result != GNOME_VFS_OK ) {
994         int perm;
995         Authentication aAuth( xEnv );
996 
997         perm = ( ( GNOME_VFS_PERM_USER_WRITE | GNOME_VFS_PERM_USER_READ ) |
998              ( GNOME_VFS_PERM_GROUP_WRITE | GNOME_VFS_PERM_GROUP_READ ) );
999 
1000         result = gnome_vfs_create
1001             ( &handle, (const sal_Char *)aURI, GNOME_VFS_OPEN_WRITE, TRUE, perm );
1002     }
1003 
1004     if( result != GNOME_VFS_OK )
1005         cancelCommandExecution( result, xEnv, sal_True );
1006 
1007     if ( !xInputStream.is() ) {
1008         result = gnome_vfs_close( handle );
1009         if (result != GNOME_VFS_OK)
1010             cancelCommandExecution( result, xEnv, sal_True );
1011 
1012     } else { // copy it over
1013         uno::Reference < io::XOutputStream > xOutput =
1014             new gvfs::Stream( handle, &m_info );
1015 
1016         copyData( xInputStream, xOutput );
1017     }
1018 
1019     if (m_bTransient) {
1020         m_bTransient = sal_False;
1021         aGuard.clear();
1022         inserted();
1023     }
1024 }
1025 
1026 void Content::transfer(const ucb::TransferInfo & /*rArgs*/,
1027                const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1028     throw( uno::Exception )
1029 {
1030     // FIXME: see gnome-vfs-xfer.h - but we need to be able to easily
1031     // detect which are gnome-vfs owned URI types ...
1032     ucbhelper::cancelCommandExecution
1033         ( uno::makeAny
1034             ( ucb::InteractiveBadTransferURLException
1035                 ( rtl::OUString::createFromAscii( "Unsupported URL scheme!" ),
1036                   static_cast< cppu::OWeakObject * >( this ) ) ),
1037           xEnv );
1038 }
1039 
1040 void Content::destroy( sal_Bool bDeletePhysical )
1041     throw( uno::Exception )
1042 {
1043     // @@@ take care about bDeletePhysical -> trashcan support
1044     rtl::OUString aURL = getOUURI();
1045 
1046     uno::Reference< ucb::XContent > xThis = this;
1047 
1048     deleted();
1049 
1050     osl::Guard< osl::Mutex > aGuard( m_aMutex );
1051 
1052     // Process instanciated children...
1053     ::gvfs::Content::ContentRefList aChildren;
1054     queryChildren( aChildren );
1055 
1056     ContentRefList::const_iterator it  = aChildren.begin();
1057     ContentRefList::const_iterator end = aChildren.end();
1058 
1059     while ( it != end ) {
1060         (*it)->destroy( bDeletePhysical );
1061         ++it;
1062     }
1063 }
1064 
1065 // Used by the 'setPropertyValues' method for
1066 // propagating the renaming of a Content.
1067 sal_Bool Content::exchangeIdentity(
1068     const uno::Reference< ucb::XContentIdentifier >& xNewId )
1069 {
1070     if ( !xNewId.is() )
1071         return sal_False;
1072 
1073     uno::Reference< ucb::XContent > xThis = this;
1074 
1075 #ifdef DEBUG
1076     g_warning( "exchangeIdentity from '%s' to '%s'",
1077            getURI(), OUStringToGnome( xNewId->getContentIdentifier() ) );
1078 #endif
1079 
1080     if ( m_bTransient ) {
1081         osl::Guard< osl::Mutex > aGuard( m_aMutex );
1082         /* FIXME: can we not screw up an identically named
1083          * Content pointing to ourself here ? */
1084         m_xIdentifier = xNewId;
1085         return sal_False;
1086     }
1087 
1088     rtl::OUString aOldURL = getOUURI();
1089 
1090     // Exchange own identitity.
1091     if ( exchange( xNewId ) ) {
1092 
1093         // Process instanciated children...
1094         ContentRefList aChildren;
1095         queryChildren( aChildren );
1096 
1097         ContentRefList::const_iterator it  = aChildren.begin();
1098         ContentRefList::const_iterator end = aChildren.end();
1099 
1100         while ( it != end ) {
1101             ContentRef xChild = (*it);
1102 
1103             // Create new content identifier for the child...
1104             uno::Reference< ucb::XContentIdentifier >
1105                 xOldChildId = xChild->getIdentifier();
1106             rtl::OUString aOldChildURL
1107                 = xOldChildId->getContentIdentifier();
1108             rtl::OUString aNewChildURL
1109                 = aOldChildURL.replaceAt(
1110                              0,
1111                              aOldURL.getLength(),
1112                              xNewId->getContentIdentifier() );
1113             uno::Reference< ucb::XContentIdentifier >
1114                 xNewChildId
1115                 = new ::ucbhelper::ContentIdentifier( m_xSMgr, aNewChildURL );
1116 
1117             if ( !xChild->exchangeIdentity( xNewChildId ) )
1118                 return sal_False;
1119 
1120             ++it;
1121         }
1122         return sal_True;
1123     }
1124 
1125     return sal_False;
1126 }
1127 
1128 GnomeVFSResult
1129 Content::getInfo( const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1130 {
1131     GnomeVFSResult result;
1132         osl::Guard< osl::Mutex > aGuard( m_aMutex );
1133 
1134     if (m_bTransient)
1135         result = GNOME_VFS_OK;
1136 
1137     else if ( !m_info.valid_fields ) {
1138         ::rtl::OString aURI = getOURI();
1139         Authentication aAuth( xEnv );
1140         result = gnome_vfs_get_file_info
1141             ( (const sal_Char *)aURI, &m_info, GNOME_VFS_FILE_INFO_DEFAULT );
1142         if (result != GNOME_VFS_OK)
1143             gnome_vfs_file_info_clear( &m_info );
1144     } else
1145         result = GNOME_VFS_OK;
1146 #ifdef DEBUG
1147     g_warning( "getInfo on '%s' returns '%s' (%d) (0x%x)",
1148            getURI(), gnome_vfs_result_to_string( result ),
1149            result, m_info.valid_fields );
1150 #endif
1151     return result;
1152 }
1153 
1154 sal_Bool
1155 Content::isFolder(const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1156 {
1157         osl::Guard< osl::Mutex > aGuard( m_aMutex );
1158     getInfo( xEnv );
1159     return (m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_TYPE &&
1160         m_info.type == GNOME_VFS_FILE_TYPE_DIRECTORY);
1161 }
1162 
1163 uno::Any Content::mapVFSException( const GnomeVFSResult result, sal_Bool bWrite )
1164 {
1165     uno::Any aException;
1166     const char *gvfs_message;
1167     rtl::OUString message;
1168     uno::Sequence< uno::Any > aArgs( 1 );
1169 
1170 #ifdef DEBUG
1171     g_warning ("Map VFS exception '%s' (%d)",
1172            gnome_vfs_result_to_string( result ), result );
1173 #endif
1174 
1175     if ((gvfs_message = gnome_vfs_result_to_string (result)))
1176         message = GnomeToOUString( gvfs_message );
1177 
1178     switch (result) {
1179     case GNOME_VFS_OK:
1180         g_warning("VFS_OK mapped to exception.");
1181         break;
1182     case GNOME_VFS_ERROR_EOF:
1183         g_warning ("VFS_EOF not handled somewhere.");
1184         break;
1185     case GNOME_VFS_ERROR_NOT_FOUND:
1186         aArgs[ 0 ] <<= m_xIdentifier->getContentIdentifier();
1187         aException <<=
1188             ucb::InteractiveAugmentedIOException
1189             ( rtl::OUString::createFromAscii( "Not found!" ),
1190               static_cast< cppu::OWeakObject * >( this ),
1191               task::InteractionClassification_ERROR,
1192               ucb::IOErrorCode_NOT_EXISTING,
1193               aArgs );
1194         break;
1195     case GNOME_VFS_ERROR_BAD_PARAMETERS:
1196         aException <<=
1197             lang::IllegalArgumentException
1198                 ( rtl::OUString(),
1199                   static_cast< cppu::OWeakObject * >( this ),
1200                   -1 );
1201         break;
1202     case GNOME_VFS_ERROR_GENERIC:
1203     case GNOME_VFS_ERROR_INTERNAL:
1204     case GNOME_VFS_ERROR_NOT_SUPPORTED:
1205 #ifdef DEBUG
1206         g_warning ("Internal - un-mapped error");
1207 #endif
1208         aException <<= io::IOException();
1209         break;
1210     case GNOME_VFS_ERROR_IO:
1211         if ( bWrite )
1212             aException <<=
1213                 ucb::InteractiveNetworkWriteException
1214                 ( rtl::OUString(),
1215                   static_cast< cppu::OWeakObject * >( this ),
1216                   task::InteractionClassification_ERROR,
1217                   message );
1218         else
1219             aException <<=
1220                 ucb::InteractiveNetworkReadException
1221                 ( rtl::OUString(),
1222                   static_cast< cppu::OWeakObject * >( this ),
1223                   task::InteractionClassification_ERROR,
1224                   message );
1225         break;
1226     case GNOME_VFS_ERROR_HOST_NOT_FOUND:
1227     case GNOME_VFS_ERROR_INVALID_HOST_NAME:
1228         aException <<=
1229             ucb::InteractiveNetworkResolveNameException
1230             ( rtl::OUString(),
1231               static_cast< cppu::OWeakObject * >( this ),
1232               task::InteractionClassification_ERROR,
1233               message );
1234         break;
1235     case GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE:
1236     case GNOME_VFS_ERROR_SERVICE_OBSOLETE:
1237     case GNOME_VFS_ERROR_PROTOCOL_ERROR:
1238     case GNOME_VFS_ERROR_NO_MASTER_BROWSER:
1239         aException <<=
1240             ucb::InteractiveNetworkConnectException
1241                 ( rtl::OUString(),
1242                   static_cast< cppu::OWeakObject * >( this ),
1243                   task::InteractionClassification_ERROR,
1244                   message );
1245         break;
1246 
1247     case GNOME_VFS_ERROR_FILE_EXISTS:
1248         aException <<= ucb::NameClashException
1249                 ( rtl::OUString(),
1250                   static_cast< cppu::OWeakObject * >( this ),
1251                   task::InteractionClassification_ERROR,
1252                   message );
1253         break;
1254 
1255     case GNOME_VFS_ERROR_INVALID_OPEN_MODE:
1256         aException <<= ucb::UnsupportedOpenModeException();
1257         break;
1258 
1259     case GNOME_VFS_ERROR_CORRUPTED_DATA:
1260     case GNOME_VFS_ERROR_WRONG_FORMAT:
1261     case GNOME_VFS_ERROR_BAD_FILE:
1262     case GNOME_VFS_ERROR_TOO_BIG:
1263     case GNOME_VFS_ERROR_NO_SPACE:
1264     case GNOME_VFS_ERROR_READ_ONLY:
1265     case GNOME_VFS_ERROR_INVALID_URI:
1266     case GNOME_VFS_ERROR_NOT_OPEN:
1267     case GNOME_VFS_ERROR_ACCESS_DENIED:
1268     case GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES:
1269     case GNOME_VFS_ERROR_NOT_A_DIRECTORY:
1270     case GNOME_VFS_ERROR_IN_PROGRESS:
1271     case GNOME_VFS_ERROR_INTERRUPTED:
1272     case GNOME_VFS_ERROR_LOOP:
1273     case GNOME_VFS_ERROR_NOT_PERMITTED:
1274     case GNOME_VFS_ERROR_IS_DIRECTORY:
1275     case GNOME_VFS_ERROR_NO_MEMORY:
1276     case GNOME_VFS_ERROR_HOST_HAS_NO_ADDRESS:
1277     case GNOME_VFS_ERROR_LOGIN_FAILED:
1278     case GNOME_VFS_ERROR_CANCELLED:
1279     case GNOME_VFS_ERROR_DIRECTORY_BUSY:
1280     case GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY:
1281     case GNOME_VFS_ERROR_TOO_MANY_LINKS:
1282     case GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM:
1283     case GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM:
1284     case GNOME_VFS_ERROR_NAME_TOO_LONG:
1285 #ifdef DEBUG
1286         g_warning( "FIXME: Un-mapped VFS exception '%s' (%d)",
1287                gnome_vfs_result_to_string( result ), result );
1288 #endif
1289     default:
1290         aException <<= ucb::InteractiveNetworkGeneralException
1291             ( rtl::OUString(),
1292               static_cast< cppu::OWeakObject * >( this ),
1293               task::InteractionClassification_ERROR );
1294         break;
1295     }
1296 
1297     return aException;
1298 }
1299 
1300 void Content::cancelCommandExecution(
1301     GnomeVFSResult result,
1302     const uno::Reference< ucb::XCommandEnvironment > & xEnv,
1303     sal_Bool bWrite /* = sal_False */ )
1304     throw ( uno::Exception )
1305 {
1306     ucbhelper::cancelCommandExecution( mapVFSException( result, bWrite ), xEnv );
1307     // Unreachable
1308 }
1309 
1310 uno::Sequence< beans::Property > Content::getProperties(
1311     const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
1312 {
1313     static const beans::Property aGenericProperties[] = {
1314                 beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ContentType" ) ),
1315                  -1, getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
1316                  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1317                 beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsDocument" ) ),
1318                  -1, getCppuBooleanType(),
1319                  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1320                 beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFolder" ) ),
1321                  -1, getCppuBooleanType(),
1322                  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1323                 beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Title" ) ),
1324                  -1, getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
1325                  beans::PropertyAttribute::BOUND ),
1326         // Optional ...
1327         beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DateCreated" ) ),
1328                  -1, getCppuType( static_cast< const util::DateTime * >( 0 ) ),
1329                  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1330                 beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DateModified" ) ),
1331                  -1, getCppuType( static_cast< const util::DateTime * >( 0 ) ),
1332                  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1333 // FIXME: Too expensive for now (?)
1334 //                beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "MediaType" ) ),
1335 //                 -1, getCppuType( static_cast< const rtl::OUString * >( 0 ) ),
1336 //                 beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1337                 beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Size" ) ),
1338                  -1, getCppuType( static_cast< const sal_Int64 * >( 0 ) ),
1339                  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1340                 beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsReadOnly" ) ),
1341                  -1, getCppuBooleanType(),
1342                  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1343                 beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsVolume" ) ),
1344                  -1, getCppuBooleanType(),
1345                  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1346                 beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsCompactDisk" ) ),
1347                  -1, getCppuBooleanType(),
1348                  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1349                 beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "IsHidden" ) ),
1350                  -1, getCppuBooleanType(),
1351                  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1352                 beans::Property( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CreatableContentsInfo" ) ),
1353                  -1, getCppuType( static_cast< const uno::Sequence< ucb::ContentInfo > * >( 0 ) ),
1354                  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY )
1355     };
1356 
1357     const int nProps = sizeof (aGenericProperties) / sizeof (aGenericProperties[0]);
1358 
1359     return uno::Sequence< beans::Property > ( aGenericProperties, nProps );
1360 
1361 }
1362 
1363 uno::Sequence< ucb::CommandInfo > Content::getCommands(
1364     const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1365 {
1366     static ucb::CommandInfo aCommandInfoTable[] = {
1367         // Required commands
1368         ucb::CommandInfo
1369         ( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "getCommandInfo" ) ),
1370           -1, getCppuVoidType() ),
1371         ucb::CommandInfo
1372         ( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "getPropertySetInfo" ) ),
1373           -1, getCppuVoidType() ),
1374         ucb::CommandInfo
1375         ( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "getPropertyValues" ) ),
1376           -1, getCppuType( static_cast<uno::Sequence< beans::Property > * >( 0 ) ) ),
1377         ucb::CommandInfo
1378         ( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "setPropertyValues" ) ),
1379           -1, getCppuType( static_cast<uno::Sequence< beans::PropertyValue > * >( 0 ) ) ),
1380 
1381         // Optional standard commands
1382         ucb::CommandInfo
1383         ( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "delete" ) ),
1384           -1, getCppuBooleanType() ),
1385         ucb::CommandInfo
1386         ( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "insert" ) ),
1387           -1, getCppuType( static_cast<ucb::InsertCommandArgument * >( 0 ) ) ),
1388         ucb::CommandInfo
1389         ( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "open" ) ),
1390           -1, getCppuType( static_cast<ucb::OpenCommandArgument2 * >( 0 ) ) ),
1391 
1392         // Folder Only, omitted if not a folder
1393         ucb::CommandInfo
1394         ( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "transfer" ) ),
1395           -1, getCppuType( static_cast<ucb::TransferInfo * >( 0 ) ) ),
1396         ucb::CommandInfo
1397         ( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "createNewContent" ) ),
1398           -1, getCppuType( static_cast<ucb::ContentInfo * >( 0 ) ) )
1399     };
1400 
1401     const int nProps
1402         = sizeof( aCommandInfoTable ) / sizeof( aCommandInfoTable[ 0 ] );
1403     return uno::Sequence< ucb::CommandInfo >(
1404         aCommandInfoTable, isFolder( xEnv ) ? nProps : nProps - 2 );
1405 }
1406 
1407 rtl::OUString
1408 Content::getOUURI ()
1409 {
1410         osl::Guard< osl::Mutex > aGuard( m_aMutex );
1411     return m_xIdentifier->getContentIdentifier();
1412 }
1413 
1414 rtl::OString
1415 Content::getOURI ()
1416 {
1417     return rtl::OUStringToOString( getOUURI(), RTL_TEXTENCODING_UTF8 );
1418 }
1419 
1420 char *
1421 Content::getURI ()
1422 {
1423     return OUStringToGnome( getOUURI() );
1424 }
1425 
1426 void
1427 Content::copyData( uno::Reference< io::XInputStream > xIn,
1428                    uno::Reference< io::XOutputStream > xOut )
1429 {
1430     uno::Sequence< sal_Int8 > theData( TRANSFER_BUFFER_SIZE );
1431 
1432     g_return_if_fail( xIn.is() && xOut.is() );
1433 
1434     while ( xIn->readBytes( theData, TRANSFER_BUFFER_SIZE ) > 0 )
1435         xOut->writeBytes( theData );
1436 
1437     xOut->closeOutput();
1438 }
1439 
1440 // Inherits an authentication context
1441 uno::Reference< io::XInputStream >
1442 Content::createTempStream(
1443     const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1444         throw( uno::Exception )
1445 {
1446     GnomeVFSResult result;
1447     GnomeVFSHandle *handle = NULL;
1448     ::rtl::OString aURI = getOURI();
1449 
1450         osl::Guard< osl::Mutex > aGuard( m_aMutex );
1451     // Something badly wrong happened - can't seek => stream to a temporary file
1452     const rtl::OUString sServiceName ( RTL_CONSTASCII_USTRINGPARAM ( "com.sun.star.io.TempFile" ) );
1453     uno::Reference < io::XOutputStream > xTempOut =
1454         uno::Reference < io::XOutputStream >
1455             ( m_xSMgr->createInstance( sServiceName ), uno::UNO_QUERY );
1456 
1457     if ( !xTempOut.is() )
1458         cancelCommandExecution( GNOME_VFS_ERROR_IO, xEnv );
1459 
1460     result = gnome_vfs_open
1461         ( &handle, (const sal_Char *)aURI, GNOME_VFS_OPEN_READ );
1462     if (result != GNOME_VFS_OK)
1463         cancelCommandExecution( result, xEnv );
1464 
1465     uno::Reference < io::XInputStream > pStream = new ::gvfs::Stream( handle, &m_info );
1466     copyData( pStream, xTempOut );
1467 
1468     return uno::Reference < io::XInputStream > ( xTempOut, uno::UNO_QUERY );
1469 }
1470 
1471 uno::Reference< io::XInputStream >
1472 Content::createInputStream(
1473     const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1474         throw( uno::Exception )
1475 {
1476     GnomeVFSHandle *handle = NULL;
1477     GnomeVFSResult  result;
1478     uno::Reference<io::XInputStream > xIn;
1479 
1480     Authentication aAuth( xEnv );
1481         osl::Guard< osl::Mutex > aGuard( m_aMutex );
1482 
1483     getInfo( xEnv );
1484     ::rtl::OString aURI = getOURI();
1485 
1486     if ( !(m_info.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) )
1487         return createTempStream( xEnv );
1488 
1489     result = gnome_vfs_open
1490         ( &handle, (const sal_Char *)aURI,
1491           (GnomeVFSOpenMode) (GNOME_VFS_OPEN_READ | GNOME_VFS_OPEN_RANDOM ) );
1492 
1493     if (result == GNOME_VFS_ERROR_INVALID_OPEN_MODE ||
1494         result == GNOME_VFS_ERROR_NOT_SUPPORTED)
1495         return createTempStream( xEnv );
1496 
1497     if (result != GNOME_VFS_OK)
1498         cancelCommandExecution( result, xEnv );
1499 
1500     // Try a seek just to make sure it's Random access: some lie.
1501     result = gnome_vfs_seek( handle, GNOME_VFS_SEEK_START, 0);
1502     if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) {
1503         gnome_vfs_close( handle );
1504         return createTempStream( xEnv );
1505     }
1506 
1507     if (result != GNOME_VFS_OK)
1508         cancelCommandExecution( result, xEnv );
1509 
1510     if (handle != NULL)
1511         xIn = new ::gvfs::Stream( handle, &m_info );
1512 
1513     return xIn;
1514 }
1515 
1516 sal_Bool
1517 Content::feedSink( uno::Reference< uno::XInterface > aSink,
1518                    const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1519 {
1520     if ( !aSink.is() )
1521         return sal_False;
1522 
1523     uno::Reference< io::XOutputStream > xOut
1524         = uno::Reference< io::XOutputStream >(aSink, uno::UNO_QUERY );
1525     uno::Reference< io::XActiveDataSink > xDataSink
1526         = uno::Reference< io::XActiveDataSink >(aSink, uno::UNO_QUERY );
1527 
1528     if ( !xOut.is() && !xDataSink.is() )
1529         return sal_False;
1530 
1531     uno::Reference< io::XInputStream > xIn = createInputStream( xEnv );
1532     if ( !xIn.is() )
1533         return sal_False;
1534 
1535     if ( xOut.is() )
1536         copyData( xIn, xOut );
1537 
1538     if ( xDataSink.is() )
1539         xDataSink->setInputStream( xIn );
1540 
1541     return sal_True;
1542 }
1543 
1544 extern "C" {
1545 
1546 #ifndef GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION
1547 #  error "We require Gnome VFS 2.6.x to compile (will run fine with < 2.6)"
1548 #endif
1549 
1550     static void
1551     vfs_authentication_callback (gconstpointer in_void,
1552                      gsize         in_size,
1553                      gpointer      out_void,
1554                      gsize         out_size,
1555                      gpointer      callback_data)
1556     {
1557         task::XInteractionHandler *xIH;
1558 
1559 #ifdef DEBUG
1560         g_warning ("Full authentication callback (%p) ...", callback_data);
1561 #endif
1562 
1563         if( !( xIH = (task::XInteractionHandler *) callback_data ) )
1564             return;
1565 
1566         const GnomeVFSModuleCallbackFullAuthenticationIn *in =
1567             (const GnomeVFSModuleCallbackFullAuthenticationIn *) in_void;
1568         GnomeVFSModuleCallbackFullAuthenticationOut *out =
1569             (GnomeVFSModuleCallbackFullAuthenticationOut *) out_void;
1570 
1571         g_return_if_fail (in != NULL && out != NULL);
1572         g_return_if_fail (sizeof (GnomeVFSModuleCallbackFullAuthenticationIn) == in_size &&
1573                   sizeof (GnomeVFSModuleCallbackFullAuthenticationOut) == out_size);
1574 
1575 #ifdef DEBUG
1576 #  define NNIL(x) (x?x:"<Null>")
1577         g_warning (" InComing data 0x%x uri '%s' prot '%s' server '%s' object '%s' "
1578                    "port %d auth_t '%s' user '%s' domain '%s' "
1579                    "def user '%s', def domain '%s'",
1580                    (int) in->flags, NNIL(in->uri), NNIL(in->protocol),
1581                    NNIL(in->server), NNIL(in->object),
1582                    (int) in->port, NNIL(in->authtype), NNIL(in->username), NNIL(in->domain),
1583                    NNIL(in->default_user), NNIL(in->default_domain));
1584 #  undef NNIL
1585 #endif
1586 
1587         ucbhelper::SimpleAuthenticationRequest::EntityType
1588                                    eDomain, eUserName, ePassword;
1589         ::rtl::OUString aHostName, aDomain, aUserName, aPassword;
1590 
1591         aHostName = GnomeToOUString( in->server );
1592 
1593         if (in->flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_DOMAIN)
1594         {
1595             aDomain = GnomeToOUString( in->domain );
1596             eDomain = ucbhelper::SimpleAuthenticationRequest::ENTITY_MODIFY;
1597             if (!aDomain.getLength())
1598                 aDomain = GnomeToOUString( in->default_domain );
1599         }
1600         else // no underlying capability to display realm otherwise
1601             eDomain = ucbhelper::SimpleAuthenticationRequest::ENTITY_NA;
1602 
1603         aUserName = GnomeToOUString( in->username );
1604         if (!aUserName.getLength())
1605             aUserName = GnomeToOUString( in->default_user );
1606         eUserName = (in->flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_USERNAME) ?
1607             ucbhelper::SimpleAuthenticationRequest::ENTITY_MODIFY :
1608                 (aUserName.getLength() ?
1609                     ucbhelper::SimpleAuthenticationRequest::ENTITY_FIXED :
1610                     ucbhelper::SimpleAuthenticationRequest::ENTITY_NA);
1611 
1612         // No suggested password.
1613         ePassword = (in->flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_PASSWORD) ?
1614             ucbhelper::SimpleAuthenticationRequest::ENTITY_MODIFY :
1615             ucbhelper::SimpleAuthenticationRequest::ENTITY_FIXED;
1616 
1617         // Really, really bad things happen if we don't provide
1618         // the same user/password as was entered last time if
1619         // we failed to authenticate - infinite looping / flickering
1620         // madness etc. [ nice infrastructure ! ]
1621         static rtl::OUString aLastUserName, aLastPassword;
1622         if (in->flags & GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_PREVIOUS_ATTEMPT_FAILED)
1623         {
1624             osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
1625             aUserName = aLastUserName;
1626             aPassword = aLastPassword;
1627         }
1628 
1629         rtl::Reference< ucbhelper::SimpleAuthenticationRequest > xRequest
1630             = new ucbhelper::SimpleAuthenticationRequest (GnomeToOUString(in->uri),
1631                                                           aHostName, eDomain, aDomain,
1632                                                           eUserName, aUserName,
1633                                                           ePassword, aPassword);
1634 
1635         xIH->handle( xRequest.get() );
1636 
1637         rtl::Reference< ucbhelper::InteractionContinuation > xSelection
1638             = xRequest->getSelection();
1639 
1640         if ( xSelection.is() ) {
1641             // Handler handled the request.
1642             uno::Reference< task::XInteractionAbort > xAbort(xSelection.get(), uno::UNO_QUERY );
1643             if ( !xAbort.is() ) {
1644                 const rtl::Reference<
1645                     ucbhelper::InteractionSupplyAuthentication > & xSupp
1646                     = xRequest->getAuthenticationSupplier();
1647 
1648                 aUserName = xSupp->getUserName();
1649                 aDomain   = xSupp->getRealm();
1650                 aPassword = xSupp->getPassword();
1651 
1652                 {
1653                     osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
1654                     aLastUserName = aUserName;
1655                     aLastPassword = aPassword;
1656                 }
1657 
1658                 out->username = OUStringToGnome( aUserName );
1659                 out->domain   = OUStringToGnome( aDomain );
1660                 out->password = OUStringToGnome( aPassword );
1661                 out->save_password = xSupp->getRememberPasswordMode();
1662 
1663 #ifdef DEBUG
1664                 g_warning ("Got valid user/domain/password '%s' '%s' '%s', %s password",
1665                            out->username, out->domain, out->password,
1666                            out->save_password ? "save" : "don't save");
1667 #endif
1668             }
1669             else
1670                 out->abort_auth = TRUE;
1671         }
1672         else
1673             out->abort_auth = TRUE;
1674     }
1675 
1676     static void
1677     vfs_authentication_old_callback (gconstpointer in_void,
1678                                      gsize         in_size,
1679                                      gpointer      out_void,
1680                                      gsize         out_size,
1681                                      gpointer      callback_data)
1682     {
1683 #ifdef DEBUG
1684         g_warning ("Old authentication callback (%p) [ UNTESTED ] ...", callback_data);
1685 #endif
1686         const GnomeVFSModuleCallbackAuthenticationIn *in =
1687             (const GnomeVFSModuleCallbackAuthenticationIn *) in_void;
1688         GnomeVFSModuleCallbackAuthenticationOut *out =
1689             (GnomeVFSModuleCallbackAuthenticationOut *) out_void;
1690 
1691         g_return_if_fail (in != NULL && out != NULL);
1692         g_return_if_fail (sizeof (GnomeVFSModuleCallbackAuthenticationIn) == in_size &&
1693                   sizeof (GnomeVFSModuleCallbackAuthenticationOut) == out_size);
1694 
1695         GnomeVFSModuleCallbackFullAuthenticationIn mapped_in = {
1696                 (GnomeVFSModuleCallbackFullAuthenticationFlags)
1697                 (GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_PASSWORD |
1698                  GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_USERNAME |
1699                  GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_NEED_DOMAIN),
1700                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1701         GnomeVFSModuleCallbackFullAuthenticationOut mapped_out = { 0, 0, 0, 0, 0, 0, 0, 0 };
1702 
1703         // Map the old style input auth. data to the new style structure.
1704         if (in->previous_attempt_failed)
1705             mapped_in.flags = (GnomeVFSModuleCallbackFullAuthenticationFlags)
1706                 (mapped_in.flags |
1707                  GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION_PREVIOUS_ATTEMPT_FAILED);
1708 
1709         GnomeVFSURI *pURI = NULL;
1710         // Urk - parse all this from the URL ...
1711         mapped_in.uri = in->uri;
1712         if (in->uri)
1713         {
1714             pURI = gnome_vfs_uri_new( in->uri );
1715             mapped_in.protocol = (char *) gnome_vfs_uri_get_scheme (pURI);
1716             mapped_in.server   = (char *) gnome_vfs_uri_get_host_name (pURI);
1717             mapped_in.port     = gnome_vfs_uri_get_host_port (pURI);
1718             mapped_in.username = (char *) gnome_vfs_uri_get_user_name (pURI);
1719         }
1720         mapped_in.domain = in->realm;
1721         mapped_in.default_user = mapped_in.username;
1722         mapped_in.default_domain = mapped_in.domain;
1723 
1724         vfs_authentication_callback ((gconstpointer) &mapped_in,
1725                                      sizeof (mapped_in),
1726                                      (gpointer) &mapped_out,
1727                                      sizeof (mapped_out),
1728                                      callback_data);
1729 
1730         if (pURI)
1731             gnome_vfs_uri_unref (pURI);
1732 
1733         // Map the new style auth. out data to the old style out structure.
1734         out->username = mapped_out.username;
1735         out->password = mapped_out.password;
1736         g_free (mapped_out.domain);
1737         g_free (mapped_out.keyring);
1738     }
1739 
1740 
1741     static void
1742     auth_destroy (gpointer data)
1743     {
1744         task::XInteractionHandler *xIH;
1745         if( ( xIH = ( task::XInteractionHandler * )data ) )
1746             xIH->release();
1747     }
1748 
1749     // This sucks, but gnome-vfs doesn't much like
1750     // repeated set / unsets - so we have to compensate.
1751     GPrivate *auth_queue = NULL;
1752 
1753     void auth_queue_destroy( gpointer data )
1754     {
1755         GList  *l;
1756         GQueue *vq = (GQueue *) data;
1757 
1758         for (l = vq->head; l; l = l->next)
1759             auth_destroy (l->data);
1760         g_queue_free (vq);
1761     }
1762 }
1763 
1764 static void
1765 refresh_auth( GQueue *vq )
1766 {
1767     GList *l;
1768 
1769     gnome_vfs_module_callback_pop( GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION );
1770     gnome_vfs_module_callback_pop( GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION );
1771 
1772     for (l = vq->head; l; l = l->next) {
1773         if (l->data) {
1774             gnome_vfs_module_callback_push
1775                 ( GNOME_VFS_MODULE_CALLBACK_AUTHENTICATION,
1776                   vfs_authentication_old_callback, l->data, NULL );
1777             gnome_vfs_module_callback_push
1778                 ( GNOME_VFS_MODULE_CALLBACK_FULL_AUTHENTICATION,
1779                   vfs_authentication_callback, l->data, NULL );
1780             break;
1781         }
1782     }
1783 }
1784 
1785 gvfs::Authentication::Authentication(
1786     const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1787 {
1788     GQueue *vq;
1789     uno::Reference< task::XInteractionHandler > xIH;
1790 
1791     if ( xEnv.is() )
1792         xIH = xEnv->getInteractionHandler();
1793 
1794     if ( xIH.is() )
1795         xIH->acquire();
1796 
1797     if( !(vq = (GQueue *)g_private_get( auth_queue ) ) ) {
1798         vq = g_queue_new();
1799         g_private_set( auth_queue, vq );
1800     }
1801 
1802     g_queue_push_head( vq, (gpointer) xIH.get() );
1803     refresh_auth( vq );
1804 }
1805 
1806 gvfs::Authentication::~Authentication()
1807 {
1808     GQueue *vq;
1809     gpointer data;
1810 
1811     vq = (GQueue *)g_private_get( auth_queue );
1812 
1813     data = g_queue_pop_head( vq );
1814     auth_destroy (data);
1815 
1816     refresh_auth( vq );
1817 }
1818