Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*************************************************************************
3 : *
4 : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : *
6 : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : *
8 : * OpenOffice.org - a multi-platform office productivity suite
9 : *
10 : * This file is part of OpenOffice.org.
11 : *
12 : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : * it under the terms of the GNU Lesser General Public License version 3
14 : * only, as published by the Free Software Foundation.
15 : *
16 : * OpenOffice.org is distributed in the hope that it will be useful,
17 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : * GNU Lesser General Public License version 3 for more details
20 : * (a copy is included in the LICENSE file that accompanied this code).
21 : *
22 : * You should have received a copy of the GNU Lesser General Public License
23 : * version 3 along with OpenOffice.org. If not, see
24 : * <http://www.openoffice.org/license.html>
25 : * for a copy of the LGPLv3 License.
26 : *
27 : ************************************************************************/
28 :
29 : /**************************************************************************
30 : TODO
31 : **************************************************************************
32 :
33 : *************************************************************************/
34 :
35 : #include <set>
36 : #include <com/sun/star/beans/Property.hpp>
37 : #include <com/sun/star/beans/PropertyAttribute.hpp>
38 : #include <com/sun/star/beans/PropertyValue.hpp>
39 : #include <com/sun/star/ucb/CommandInfo.hpp>
40 : #include <com/sun/star/ucb/ContentInfo.hpp>
41 : #include <com/sun/star/ucb/OpenCommandArgument2.hpp>
42 : #include <com/sun/star/ucb/InsertCommandArgument.hpp>
43 : #include <com/sun/star/ucb/PostCommandArgument2.hpp>
44 : #include <com/sun/star/ucb/PropertyCommandArgument.hpp>
45 : #include <com/sun/star/ucb/TransferInfo.hpp>
46 : #include <com/sun/star/uno/Sequence.hxx>
47 : #include <com/sun/star/util/DateTime.hpp>
48 : #include <com/sun/star/ucb/Link.hpp>
49 : #include <com/sun/star/ucb/Lock.hpp>
50 : #include <com/sun/star/ucb/LockEntry.hpp>
51 : #include "webdavcontent.hxx"
52 : #include "webdavprovider.hxx"
53 : #include "DAVSession.hxx"
54 : #include "ContentProperties.hxx"
55 :
56 : using namespace com::sun::star;
57 : using namespace webdav_ucp;
58 :
59 :
60 :
61 : // ContentProvider implementation.
62 :
63 :
64 :
65 0 : bool ContentProvider::getProperty(
66 : const OUString & rPropName, beans::Property & rProp, bool bStrict )
67 : {
68 0 : if ( !m_pProps )
69 : {
70 0 : osl::MutexGuard aGuard( m_aMutex );
71 0 : if ( !m_pProps )
72 : {
73 0 : m_pProps = new PropertyMap;
74 :
75 :
76 : // Fill map of known properties...
77 :
78 :
79 : // Mandatory UCB properties.
80 : m_pProps->insert(
81 : beans::Property(
82 : OUString( "ContentType" ),
83 : -1,
84 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
85 : beans::PropertyAttribute::BOUND
86 0 : | beans::PropertyAttribute::READONLY ) );
87 :
88 : m_pProps->insert(
89 : beans::Property(
90 : OUString( "IsDocument" ),
91 : -1,
92 0 : getCppuBooleanType(),
93 : beans::PropertyAttribute::BOUND
94 0 : | beans::PropertyAttribute::READONLY ) );
95 :
96 : m_pProps->insert(
97 : beans::Property(
98 : OUString( "IsFolder" ),
99 : -1,
100 0 : getCppuBooleanType(),
101 : beans::PropertyAttribute::BOUND
102 0 : | beans::PropertyAttribute::READONLY ) );
103 :
104 : m_pProps->insert(
105 : beans::Property(
106 : OUString( "Title" ),
107 : -1,
108 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
109 0 : beans::PropertyAttribute::BOUND ) );
110 :
111 : // Optional UCB properties.
112 :
113 : m_pProps->insert(
114 : beans::Property(
115 : OUString( "DateCreated" ),
116 : -1,
117 0 : getCppuType( static_cast< const util::DateTime * >( 0 ) ),
118 : beans::PropertyAttribute::BOUND
119 0 : | beans::PropertyAttribute::READONLY ) );
120 :
121 : m_pProps->insert(
122 : beans::Property(
123 : OUString( "DateModified" ),
124 : -1,
125 0 : getCppuType( static_cast< const util::DateTime * >( 0 ) ),
126 : beans::PropertyAttribute::BOUND
127 0 : | beans::PropertyAttribute::READONLY ) );
128 :
129 : m_pProps->insert(
130 : beans::Property(
131 : OUString( "MediaType" ),
132 : -1,
133 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
134 : beans::PropertyAttribute::BOUND
135 0 : | beans::PropertyAttribute::READONLY ) );
136 :
137 : m_pProps->insert(
138 : beans::Property(
139 : OUString( "Size" ),
140 : -1,
141 0 : getCppuType( static_cast< const sal_Int64 * >( 0 ) ),
142 : beans::PropertyAttribute::BOUND
143 0 : | beans::PropertyAttribute::READONLY ) );
144 :
145 : m_pProps->insert(
146 : beans::Property(
147 : OUString( "BaseURI" ),
148 : -1,
149 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
150 : beans::PropertyAttribute::BOUND
151 0 : | beans::PropertyAttribute::READONLY ) );
152 :
153 : m_pProps->insert(
154 : beans::Property(
155 : OUString(
156 : "CreatableContentsInfo" ),
157 : -1,
158 : getCppuType( static_cast<
159 0 : const uno::Sequence< ucb::ContentInfo > * >( 0 ) ),
160 : beans::PropertyAttribute::BOUND
161 0 : | beans::PropertyAttribute::READONLY ) );
162 :
163 : // Standard DAV properties.
164 :
165 : m_pProps->insert(
166 : beans::Property(
167 : DAVProperties::CREATIONDATE,
168 : -1,
169 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
170 : beans::PropertyAttribute::BOUND
171 0 : | beans::PropertyAttribute::READONLY ) );
172 :
173 : m_pProps->insert(
174 : beans::Property(
175 : DAVProperties::DISPLAYNAME,
176 : -1,
177 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
178 0 : beans::PropertyAttribute::BOUND ) );
179 :
180 : m_pProps->insert(
181 : beans::Property(
182 : DAVProperties::GETCONTENTLANGUAGE,
183 : -1,
184 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
185 : beans::PropertyAttribute::BOUND
186 0 : | beans::PropertyAttribute::READONLY ) );
187 :
188 : m_pProps->insert(
189 : beans::Property(
190 : DAVProperties::GETCONTENTLENGTH,
191 : -1,
192 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
193 : beans::PropertyAttribute::BOUND
194 0 : | beans::PropertyAttribute::READONLY ) );
195 :
196 : m_pProps->insert(
197 : beans::Property(
198 : DAVProperties::GETCONTENTTYPE ,
199 : -1,
200 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
201 : beans::PropertyAttribute::BOUND
202 0 : | beans::PropertyAttribute::READONLY ) );
203 :
204 : m_pProps->insert(
205 : beans::Property(
206 : DAVProperties::GETETAG,
207 : -1,
208 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
209 : beans::PropertyAttribute::BOUND
210 0 : | beans::PropertyAttribute::READONLY ) );
211 :
212 : m_pProps->insert(
213 : beans::Property(
214 : DAVProperties::GETLASTMODIFIED,
215 : -1,
216 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
217 : beans::PropertyAttribute::BOUND
218 0 : | beans::PropertyAttribute::READONLY ) );
219 :
220 : m_pProps->insert(
221 : beans::Property(
222 : DAVProperties::LOCKDISCOVERY,
223 : -1,
224 : getCppuType( static_cast<
225 0 : const uno::Sequence< ucb::Lock > * >( 0 ) ),
226 : beans::PropertyAttribute::BOUND
227 0 : | beans::PropertyAttribute::READONLY ) );
228 :
229 : m_pProps->insert(
230 : beans::Property(
231 : DAVProperties::RESOURCETYPE,
232 : -1,
233 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
234 : beans::PropertyAttribute::BOUND
235 0 : | beans::PropertyAttribute::READONLY ) );
236 :
237 : m_pProps->insert(
238 : beans::Property(
239 : DAVProperties::SOURCE,
240 : -1,
241 : getCppuType( static_cast<
242 0 : const uno::Sequence< ucb::Link > * >( 0 ) ),
243 0 : beans::PropertyAttribute::BOUND ) );
244 :
245 : m_pProps->insert(
246 : beans::Property(
247 : DAVProperties::SUPPORTEDLOCK,
248 : -1,
249 : getCppuType( static_cast<
250 : const uno::Sequence<
251 0 : ucb::LockEntry > * >( 0 ) ),
252 : beans::PropertyAttribute::BOUND
253 0 : | beans::PropertyAttribute::READONLY ) );
254 :
255 : m_pProps->insert(
256 : beans::Property(
257 : DAVProperties::EXECUTABLE,
258 : -1,
259 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
260 0 : beans::PropertyAttribute::BOUND ) );
261 0 : }
262 : }
263 :
264 :
265 : // Lookup property.
266 :
267 :
268 0 : beans::Property aProp;
269 0 : aProp.Name = rPropName;
270 0 : const PropertyMap::const_iterator it = m_pProps->find( aProp );
271 0 : if ( it != m_pProps->end() )
272 : {
273 0 : rProp = (*it);
274 : }
275 : else
276 : {
277 0 : if ( bStrict )
278 0 : return false;
279 :
280 : // All unknown props are treated as:
281 0 : rProp = beans::Property(
282 : rPropName,
283 : - 1,
284 0 : getCppuType( static_cast< const OUString * >( 0 ) ),
285 0 : beans::PropertyAttribute::BOUND );
286 : }
287 :
288 0 : return true;
289 : }
290 :
291 :
292 :
293 : // Content implementation.
294 :
295 :
296 :
297 : // virtual
298 0 : uno::Sequence< beans::Property > Content::getProperties(
299 : const uno::Reference< ucb::XCommandEnvironment > & xEnv )
300 : {
301 : sal_Bool bTransient;
302 : SAL_WNODEPRECATED_DECLARATIONS_PUSH
303 0 : std::auto_ptr< DAVResourceAccess > xResAccess;
304 0 : std::auto_ptr< ContentProperties > xCachedProps;
305 : SAL_WNODEPRECATED_DECLARATIONS_POP
306 0 : rtl::Reference< ContentProvider > xProvider;
307 :
308 : {
309 0 : osl::Guard< osl::Mutex > aGuard( m_aMutex );
310 :
311 0 : bTransient = m_bTransient;
312 0 : xResAccess.reset( new DAVResourceAccess( *m_xResAccess.get() ) );
313 0 : if ( m_xCachedProps.get() )
314 : xCachedProps.reset(
315 0 : new ContentProperties( *m_xCachedProps.get() ) );
316 0 : xProvider.set( m_pProvider );
317 : }
318 :
319 : typedef std::set< OUString > StringSet;
320 0 : StringSet aPropSet;
321 :
322 : // No server access for just created (not yet committed) objects.
323 : // Only a minimal set of properties supported at this stage.
324 0 : if ( !bTransient )
325 : {
326 : // Obtain all properties supported for this resource from server.
327 : try
328 : {
329 0 : std::vector< DAVResourceInfo > props;
330 0 : xResAccess->PROPFIND( DAVZERO, props, xEnv );
331 :
332 : // Note: vector always contains exactly one resource info, because
333 : // we used a depth of DAVZERO for PROPFIND.
334 0 : aPropSet.insert( (*props.begin()).properties.begin(),
335 0 : (*props.begin()).properties.end() );
336 : }
337 0 : catch ( DAVException const & )
338 : {
339 : }
340 : }
341 :
342 : // Add DAV properties, map DAV properties to UCB properties.
343 0 : sal_Bool bHasCreationDate = sal_False; // creationdate <-> DateCreated
344 0 : sal_Bool bHasGetLastModified = sal_False; // getlastmodified <-> DateModified
345 0 : sal_Bool bHasGetContentType = sal_False; // getcontenttype <-> MediaType
346 0 : sal_Bool bHasGetContentLength = sal_False; // getcontentlength <-> Size
347 :
348 0 : sal_Bool bHasContentType = sal_False;
349 0 : sal_Bool bHasIsDocument = sal_False;
350 0 : sal_Bool bHasIsFolder = sal_False;
351 0 : sal_Bool bHasTitle = sal_False;
352 0 : sal_Bool bHasBaseURI = sal_False;
353 0 : sal_Bool bHasDateCreated = sal_False;
354 0 : sal_Bool bHasDateModified = sal_False;
355 0 : sal_Bool bHasMediaType = sal_False;
356 0 : sal_Bool bHasSize = sal_False;
357 0 : sal_Bool bHasCreatableInfos = sal_False;
358 :
359 : {
360 0 : std::set< OUString >::const_iterator it = aPropSet.begin();
361 0 : std::set< OUString >::const_iterator end = aPropSet.end();
362 0 : while ( it != end )
363 : {
364 0 : if ( !bHasCreationDate &&
365 0 : ( (*it) == DAVProperties::CREATIONDATE ) )
366 : {
367 0 : bHasCreationDate = sal_True;
368 : }
369 0 : else if ( !bHasGetLastModified &&
370 0 : ( (*it) == DAVProperties::GETLASTMODIFIED ) )
371 : {
372 0 : bHasGetLastModified = sal_True;
373 : }
374 0 : else if ( !bHasGetContentType &&
375 0 : ( (*it) == DAVProperties::GETCONTENTTYPE ) )
376 : {
377 0 : bHasGetContentType = sal_True;
378 : }
379 0 : else if ( !bHasGetContentLength &&
380 0 : ( (*it) == DAVProperties::GETCONTENTLENGTH ) )
381 : {
382 0 : bHasGetContentLength = sal_True;
383 : }
384 0 : else if ( !bHasContentType && (*it) == "ContentType" )
385 : {
386 0 : bHasContentType = sal_True;
387 : }
388 0 : else if ( !bHasIsDocument && (*it) == "IsDocument" )
389 : {
390 0 : bHasIsDocument = sal_True;
391 : }
392 0 : else if ( !bHasIsFolder && (*it) == "IsFolder" )
393 : {
394 0 : bHasIsFolder = sal_True;
395 : }
396 0 : else if ( !bHasTitle && (*it) == "Title" )
397 : {
398 0 : bHasTitle = sal_True;
399 : }
400 0 : else if ( !bHasBaseURI && (*it) == "BaseURI" )
401 : {
402 0 : bHasBaseURI = sal_True;
403 : }
404 0 : else if ( !bHasDateCreated && (*it) == "DateCreated" )
405 : {
406 0 : bHasDateCreated = sal_True;
407 : }
408 0 : else if ( !bHasDateModified && (*it) == "DateModified" )
409 : {
410 0 : bHasDateModified = sal_True;
411 : }
412 0 : else if ( !bHasMediaType && (*it) == "MediaType" )
413 : {
414 0 : bHasMediaType = sal_True;
415 : }
416 0 : else if ( !bHasSize && (*it) == "Size" )
417 : {
418 0 : bHasSize = sal_True;
419 : }
420 0 : else if ( !bHasCreatableInfos && (*it) == "CreatableContentsInfo" )
421 : {
422 0 : bHasCreatableInfos = sal_True;
423 : }
424 0 : ++it;
425 : }
426 : }
427 :
428 : // Add mandatory properties.
429 0 : if ( !bHasContentType )
430 : aPropSet.insert(
431 0 : OUString( "ContentType" ) );
432 :
433 0 : if ( !bHasIsDocument )
434 : aPropSet.insert(
435 0 : OUString( "IsDocument" ) );
436 :
437 0 : if ( !bHasIsFolder )
438 : aPropSet.insert(
439 0 : OUString( "IsFolder" ) );
440 :
441 0 : if ( !bHasTitle )
442 : {
443 : // Always present since it can be calculated from content's URI.
444 : aPropSet.insert(
445 0 : OUString( "Title" ) );
446 : }
447 :
448 : // Add optional properties.
449 :
450 0 : if ( !bHasBaseURI )
451 : {
452 : // Always present since it can be calculated from content's URI.
453 : aPropSet.insert(
454 0 : OUString( "BaseURI" ) );
455 : }
456 :
457 0 : if ( !bHasDateCreated && bHasCreationDate )
458 : aPropSet.insert(
459 0 : OUString( "DateCreated" ) );
460 :
461 0 : if ( !bHasDateModified && bHasGetLastModified )
462 : aPropSet.insert(
463 0 : OUString( "DateModified" ) );
464 :
465 0 : if ( !bHasMediaType && bHasGetContentType )
466 : aPropSet.insert(
467 0 : OUString( "MediaType" ) );
468 :
469 0 : if ( !bHasSize && bHasGetContentLength )
470 : aPropSet.insert(
471 0 : OUString( "Size" ) );
472 :
473 0 : if ( !bHasCreatableInfos )
474 : aPropSet.insert(
475 : OUString(
476 0 : "CreatableContentsInfo" ) );
477 :
478 : // Add cached properties, if present and still missing.
479 0 : if ( xCachedProps.get() )
480 : {
481 : const std::set< OUString >::const_iterator set_end
482 0 : = aPropSet.end();
483 :
484 : SAL_WNODEPRECATED_DECLARATIONS_PUSH
485 : const std::auto_ptr< PropertyValueMap > & xProps
486 0 : = xCachedProps->getProperties();
487 : SAL_WNODEPRECATED_DECLARATIONS_POP
488 :
489 0 : PropertyValueMap::const_iterator map_it = xProps->begin();
490 0 : const PropertyValueMap::const_iterator map_end = xProps->end();
491 :
492 0 : while ( map_it != map_end )
493 : {
494 0 : if ( aPropSet.find( (*map_it).first ) == set_end )
495 0 : aPropSet.insert( (*map_it).first );
496 :
497 0 : ++map_it;
498 : }
499 : }
500 :
501 : // std::set -> uno::Sequence
502 0 : sal_Int32 nCount = aPropSet.size();
503 0 : uno::Sequence< beans::Property > aProperties( nCount );
504 :
505 0 : std::set< OUString >::const_iterator it = aPropSet.begin();
506 0 : beans::Property aProp;
507 :
508 0 : for ( sal_Int32 n = 0; n < nCount; ++n, ++it )
509 : {
510 0 : xProvider->getProperty( (*it), aProp );
511 0 : aProperties[ n ] = aProp;
512 : }
513 :
514 0 : return aProperties;
515 : }
516 :
517 :
518 : // virtual
519 0 : uno::Sequence< ucb::CommandInfo > Content::getCommands(
520 : const uno::Reference< ucb::XCommandEnvironment > & xEnv )
521 : {
522 0 : osl::Guard< osl::Mutex > aGuard( m_aMutex );
523 :
524 0 : uno::Sequence< ucb::CommandInfo > aCmdInfo( 10 );
525 :
526 :
527 : // Mandatory commands
528 :
529 :
530 0 : aCmdInfo[ 0 ] =
531 : ucb::CommandInfo(
532 : OUString( "getCommandInfo" ),
533 : -1,
534 0 : getCppuVoidType() );
535 0 : aCmdInfo[ 1 ] =
536 : ucb::CommandInfo(
537 : OUString( "getPropertySetInfo" ),
538 : -1,
539 0 : getCppuVoidType() );
540 0 : aCmdInfo[ 2 ] =
541 : ucb::CommandInfo(
542 : OUString( "getPropertyValues" ),
543 : -1,
544 : getCppuType( static_cast<
545 0 : uno::Sequence< beans::Property > * >( 0 ) ) );
546 0 : aCmdInfo[ 3 ] =
547 : ucb::CommandInfo(
548 : OUString( "setPropertyValues" ),
549 : -1,
550 : getCppuType( static_cast<
551 0 : uno::Sequence< beans::PropertyValue > * >( 0 ) ) );
552 :
553 :
554 : // Optional standard commands
555 :
556 :
557 0 : aCmdInfo[ 4 ] =
558 : ucb::CommandInfo(
559 : OUString( "delete" ),
560 : -1,
561 0 : getCppuBooleanType() );
562 0 : aCmdInfo[ 5 ] =
563 : ucb::CommandInfo(
564 : OUString( "insert" ),
565 : -1,
566 : getCppuType( static_cast<
567 0 : ucb::InsertCommandArgument * >( 0 ) ) );
568 0 : aCmdInfo[ 6 ] =
569 : ucb::CommandInfo(
570 : OUString( "open" ),
571 : -1,
572 : getCppuType( static_cast<
573 0 : ucb::OpenCommandArgument2 * >( 0 ) ) );
574 :
575 :
576 : // New commands
577 :
578 :
579 0 : aCmdInfo[ 7 ] =
580 : ucb::CommandInfo(
581 : OUString( "post" ),
582 : -1,
583 : getCppuType( static_cast<
584 0 : ucb::PostCommandArgument2 * >( 0 ) ) );
585 0 : aCmdInfo[ 8 ] =
586 : ucb::CommandInfo(
587 : OUString( "addProperty" ),
588 : -1,
589 : getCppuType( static_cast<
590 0 : ucb::PropertyCommandArgument * >( 0 ) ) );
591 0 : aCmdInfo[ 9 ] =
592 : ucb::CommandInfo(
593 : OUString( "removeProperty" ),
594 : -1,
595 : getCppuType( static_cast<
596 0 : rtl::OUString * >( 0 ) ) );
597 :
598 0 : sal_Bool bFolder = sal_False;
599 :
600 : try
601 : {
602 0 : bFolder = isFolder( xEnv );
603 : }
604 0 : catch ( uno::Exception const & )
605 : {
606 0 : return aCmdInfo;
607 : }
608 :
609 0 : sal_Bool bSupportsLocking = supportsExclusiveWriteLock( xEnv );
610 :
611 0 : sal_Int32 nPos = aCmdInfo.getLength();
612 0 : sal_Int32 nMoreCmds = ( bFolder ? 2 : 0 ) + ( bSupportsLocking ? 2 : 0 );
613 0 : if ( nMoreCmds )
614 0 : aCmdInfo.realloc( nPos + nMoreCmds );
615 : else
616 0 : return aCmdInfo;
617 :
618 0 : if ( bFolder )
619 : {
620 :
621 : // Optional standard commands
622 :
623 :
624 0 : aCmdInfo[ nPos ] =
625 : ucb::CommandInfo(
626 : OUString( "transfer" ),
627 : -1,
628 0 : getCppuType( static_cast< ucb::TransferInfo * >( 0 ) ) );
629 0 : nPos++;
630 0 : aCmdInfo[ nPos ] =
631 : ucb::CommandInfo(
632 : OUString(
633 : "createNewContent" ),
634 : -1,
635 0 : getCppuType( static_cast< ucb::ContentInfo * >( 0 ) ) );
636 0 : nPos++;
637 : }
638 : else
639 : {
640 : // no document-only commands at the moment.
641 : }
642 :
643 0 : if ( bSupportsLocking )
644 : {
645 0 : aCmdInfo[ nPos ] =
646 : ucb::CommandInfo(
647 : OUString( "lock" ),
648 : -1,
649 0 : getCppuVoidType() );
650 0 : nPos++;
651 0 : aCmdInfo[ nPos ] =
652 : ucb::CommandInfo(
653 : OUString( "unlock" ),
654 : -1,
655 0 : getCppuVoidType() );
656 0 : nPos++;
657 : }
658 0 : return aCmdInfo;
659 : }
660 :
661 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|