Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include <stdio.h>
22 :
23 : #include <com/sun/star/ucb/SimpleFileAccess.hpp>
24 : #include <com/sun/star/ucb/XCommandEnvironment.hpp>
25 : #include <com/sun/star/ucb/XContent.hpp>
26 : #include <com/sun/star/ucb/InsertCommandArgument.hpp>
27 : #include <com/sun/star/ucb/InteractiveIOException.hpp>
28 : #include <com/sun/star/io/WrongFormatException.hpp>
29 :
30 : #include <osl/time.h>
31 : #include <osl/security.hxx>
32 : #include <osl/socket.hxx>
33 :
34 : #include <rtl/string.hxx>
35 : #include <rtl/ustring.hxx>
36 : #include <rtl/strbuf.hxx>
37 : #include <rtl/ustrbuf.hxx>
38 :
39 : #include <comphelper/processfactory.hxx>
40 : #include <ucbhelper/content.hxx>
41 :
42 : #include <tools/stream.hxx>
43 : #include <unotools/bootstrap.hxx>
44 : #include <unotools/streamwrap.hxx>
45 :
46 : #include <unotools/useroptions.hxx>
47 :
48 : #include <svl/sharecontrolfile.hxx>
49 :
50 : using namespace ::com::sun::star;
51 :
52 : namespace svt {
53 :
54 :
55 0 : ShareControlFile::ShareControlFile( const OUString& aOrigURL )
56 0 : : LockFileCommon( aOrigURL, OUString( ".~sharing." ) )
57 : {
58 0 : OpenStream();
59 :
60 0 : if ( !IsValid() )
61 0 : throw io::NotConnectedException();
62 0 : }
63 :
64 :
65 0 : ShareControlFile::~ShareControlFile()
66 : {
67 : try
68 : {
69 0 : Close();
70 : }
71 0 : catch( uno::Exception& )
72 : {}
73 0 : }
74 :
75 :
76 0 : void ShareControlFile::OpenStream()
77 : {
78 : // if it is called outside of constructor the mutex must be locked already
79 :
80 0 : if ( !m_xStream.is() && !m_aURL.isEmpty() )
81 : {
82 0 : uno::Reference< ucb::XCommandEnvironment > xDummyEnv;
83 0 : ::ucbhelper::Content aContent = ::ucbhelper::Content( m_aURL, xDummyEnv, comphelper::getProcessComponentContext() );
84 :
85 0 : uno::Reference< ucb::XContentIdentifier > xContId( aContent.get().is() ? aContent.get()->getIdentifier() : 0 );
86 0 : if ( !xContId.is() || xContId->getContentProviderScheme() != "file" )
87 0 : throw io::IOException(); // the implementation supports only local files for now
88 :
89 0 : uno::Reference< io::XStream > xStream;
90 :
91 : // Currently the locking of the original document is intended to be used.
92 : // That means that the shared file should be accessed only when the original document is locked and only by user who has locked the document.
93 : // TODO/LATER: should the own file locking be used?
94 :
95 : try
96 : {
97 0 : xStream = aContent.openWriteableStreamNoLock();
98 : }
99 0 : catch ( ucb::InteractiveIOException const & e )
100 : {
101 0 : if ( e.Code == ucb::IOErrorCode_NOT_EXISTING )
102 : {
103 : // Create file...
104 0 : SvMemoryStream aStream(0,0);
105 0 : uno::Reference< io::XInputStream > xInput( new ::utl::OInputStreamWrapper( aStream ) );
106 0 : ucb::InsertCommandArgument aInsertArg;
107 0 : aInsertArg.Data = xInput;
108 0 : aInsertArg.ReplaceExisting = sal_False;
109 0 : aContent.executeCommand( OUString("insert"), uno::makeAny( aInsertArg ) );
110 :
111 : // try to let the file be hidden if possible
112 : try {
113 0 : aContent.setPropertyValue("IsHidden", uno::makeAny( sal_True ) );
114 0 : } catch( uno::Exception& ) {}
115 :
116 : // Try to open one more time
117 0 : xStream = aContent.openWriteableStreamNoLock();
118 : }
119 : else
120 0 : throw;
121 : }
122 :
123 0 : m_xSeekable.set( xStream, uno::UNO_QUERY_THROW );
124 0 : m_xInputStream.set( xStream->getInputStream(), uno::UNO_QUERY_THROW );
125 0 : m_xOutputStream.set( xStream->getOutputStream(), uno::UNO_QUERY_THROW );
126 0 : m_xTruncate.set( m_xOutputStream, uno::UNO_QUERY_THROW );
127 0 : m_xStream = xStream;
128 : }
129 0 : }
130 :
131 :
132 0 : void ShareControlFile::Close()
133 : {
134 : // if it is called outside of destructor the mutex must be locked
135 :
136 0 : if ( m_xStream.is() )
137 : {
138 : try
139 : {
140 0 : if ( m_xInputStream.is() )
141 0 : m_xInputStream->closeInput();
142 0 : if ( m_xOutputStream.is() )
143 0 : m_xOutputStream->closeOutput();
144 : }
145 0 : catch( uno::Exception& )
146 : {}
147 :
148 0 : m_xStream = uno::Reference< io::XStream >();
149 0 : m_xInputStream = uno::Reference< io::XInputStream >();
150 0 : m_xOutputStream = uno::Reference< io::XOutputStream >();
151 0 : m_xSeekable = uno::Reference< io::XSeekable >();
152 0 : m_xTruncate = uno::Reference< io::XTruncate >();
153 0 : m_aUsersData.realloc( 0 );
154 : }
155 0 : }
156 :
157 :
158 0 : uno::Sequence< uno::Sequence< OUString > > ShareControlFile::GetUsersData()
159 : {
160 0 : ::osl::MutexGuard aGuard( m_aMutex );
161 :
162 0 : if ( !IsValid() )
163 0 : throw io::NotConnectedException();
164 :
165 0 : if ( !m_aUsersData.getLength() )
166 : {
167 0 : sal_Int64 nLength = m_xSeekable->getLength();
168 0 : if ( nLength > SAL_MAX_INT32 )
169 0 : throw uno::RuntimeException();
170 :
171 0 : uno::Sequence< sal_Int8 > aBuffer( (sal_Int32)nLength );
172 0 : m_xSeekable->seek( 0 );
173 :
174 0 : sal_Int32 nRead = m_xInputStream->readBytes( aBuffer, (sal_Int32)nLength );
175 0 : nLength -= nRead;
176 0 : while ( nLength > 0 )
177 : {
178 0 : uno::Sequence< sal_Int8 > aTmpBuf( (sal_Int32)nLength );
179 0 : nRead = m_xInputStream->readBytes( aTmpBuf, (sal_Int32)nLength );
180 0 : if ( nRead > nLength )
181 0 : throw uno::RuntimeException();
182 :
183 0 : for ( sal_Int32 nInd = 0; nInd < nRead; nInd++ )
184 0 : aBuffer[aBuffer.getLength() - (sal_Int32)nLength + nInd] = aTmpBuf[nInd];
185 0 : nLength -= nRead;
186 0 : }
187 :
188 0 : m_aUsersData = ParseList( aBuffer );
189 : }
190 :
191 0 : return m_aUsersData;
192 : }
193 :
194 :
195 0 : void ShareControlFile::SetUsersDataAndStore( const uno::Sequence< uno::Sequence< OUString > >& aUsersData )
196 : {
197 0 : ::osl::MutexGuard aGuard( m_aMutex );
198 :
199 0 : if ( !IsValid() )
200 0 : throw io::NotConnectedException();
201 :
202 0 : if ( !m_xTruncate.is() || !m_xOutputStream.is() || !m_xSeekable.is() )
203 0 : throw uno::RuntimeException();
204 :
205 0 : m_xTruncate->truncate();
206 0 : m_xSeekable->seek( 0 );
207 :
208 0 : OUStringBuffer aBuffer;
209 0 : for ( sal_Int32 nInd = 0; nInd < aUsersData.getLength(); nInd++ )
210 : {
211 0 : if ( aUsersData[nInd].getLength() != SHARED_ENTRYSIZE )
212 0 : throw lang::IllegalArgumentException();
213 :
214 0 : for ( sal_Int32 nEntryInd = 0; nEntryInd < SHARED_ENTRYSIZE; nEntryInd++ )
215 : {
216 0 : aBuffer.append( EscapeCharacters( aUsersData[nInd][nEntryInd] ) );
217 0 : if ( nEntryInd < SHARED_ENTRYSIZE - 1 )
218 0 : aBuffer.append( ',' );
219 : else
220 0 : aBuffer.append( ';' );
221 : }
222 : }
223 :
224 0 : OString aStringData( OUStringToOString( aBuffer.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ) );
225 0 : uno::Sequence< sal_Int8 > aData( (sal_Int8*)aStringData.getStr(), aStringData.getLength() );
226 0 : m_xOutputStream->writeBytes( aData );
227 0 : m_aUsersData = aUsersData;
228 0 : }
229 :
230 :
231 0 : uno::Sequence< OUString > ShareControlFile::InsertOwnEntry()
232 : {
233 0 : ::osl::MutexGuard aGuard( m_aMutex );
234 :
235 0 : if ( !IsValid() )
236 0 : throw io::NotConnectedException();
237 :
238 0 : GetUsersData();
239 0 : uno::Sequence< ::uno::Sequence< OUString > > aNewData( m_aUsersData.getLength() + 1 );
240 0 : uno::Sequence< OUString > aNewEntry = GenerateOwnEntry();
241 :
242 0 : bool bExists = false;
243 0 : sal_Int32 nNewInd = 0;
244 0 : for ( sal_Int32 nInd = 0; nInd < m_aUsersData.getLength(); nInd++ )
245 : {
246 0 : if ( m_aUsersData[nInd].getLength() == SHARED_ENTRYSIZE )
247 : {
248 0 : if ( m_aUsersData[nInd][SHARED_LOCALHOST_ID] == aNewEntry[SHARED_LOCALHOST_ID]
249 0 : && m_aUsersData[nInd][SHARED_SYSUSERNAME_ID] == aNewEntry[SHARED_SYSUSERNAME_ID]
250 0 : && m_aUsersData[nInd][SHARED_USERURL_ID] == aNewEntry[SHARED_USERURL_ID] )
251 : {
252 0 : if ( !bExists )
253 : {
254 0 : aNewData[nNewInd] = aNewEntry;
255 0 : bExists = true;
256 : }
257 : }
258 : else
259 : {
260 0 : aNewData[nNewInd] = m_aUsersData[nInd];
261 : }
262 :
263 0 : nNewInd++;
264 : }
265 : }
266 :
267 0 : if ( !bExists )
268 0 : aNewData[nNewInd++] = aNewEntry;
269 :
270 0 : aNewData.realloc( nNewInd );
271 0 : SetUsersDataAndStore( aNewData );
272 :
273 0 : return aNewEntry;
274 : }
275 :
276 :
277 0 : bool ShareControlFile::HasOwnEntry()
278 : {
279 0 : ::osl::MutexGuard aGuard( m_aMutex );
280 :
281 0 : if ( !IsValid() )
282 : {
283 0 : throw io::NotConnectedException();
284 : }
285 :
286 0 : GetUsersData();
287 0 : uno::Sequence< OUString > aEntry = GenerateOwnEntry();
288 :
289 0 : for ( sal_Int32 nInd = 0; nInd < m_aUsersData.getLength(); ++nInd )
290 : {
291 0 : if ( m_aUsersData[nInd].getLength() == SHARED_ENTRYSIZE &&
292 0 : m_aUsersData[nInd][SHARED_LOCALHOST_ID] == aEntry[SHARED_LOCALHOST_ID] &&
293 0 : m_aUsersData[nInd][SHARED_SYSUSERNAME_ID] == aEntry[SHARED_SYSUSERNAME_ID] &&
294 0 : m_aUsersData[nInd][SHARED_USERURL_ID] == aEntry[SHARED_USERURL_ID] )
295 : {
296 0 : return true;
297 : }
298 : }
299 :
300 0 : return false;
301 : }
302 :
303 :
304 0 : void ShareControlFile::RemoveEntry( const uno::Sequence< OUString >& aArgEntry )
305 : {
306 0 : ::osl::MutexGuard aGuard( m_aMutex );
307 :
308 0 : if ( !IsValid() )
309 0 : throw io::NotConnectedException();
310 :
311 0 : GetUsersData();
312 :
313 0 : uno::Sequence< OUString > aEntry = aArgEntry;
314 0 : if ( aEntry.getLength() != SHARED_ENTRYSIZE )
315 0 : aEntry = GenerateOwnEntry();
316 :
317 0 : uno::Sequence< ::uno::Sequence< OUString > > aNewData( m_aUsersData.getLength() + 1 );
318 :
319 0 : sal_Int32 nNewInd = 0;
320 0 : for ( sal_Int32 nInd = 0; nInd < m_aUsersData.getLength(); nInd++ )
321 : {
322 0 : if ( m_aUsersData[nInd].getLength() == SHARED_ENTRYSIZE )
323 : {
324 0 : if ( m_aUsersData[nInd][SHARED_LOCALHOST_ID] != aEntry[SHARED_LOCALHOST_ID]
325 0 : || m_aUsersData[nInd][SHARED_SYSUSERNAME_ID] != aEntry[SHARED_SYSUSERNAME_ID]
326 0 : || m_aUsersData[nInd][SHARED_USERURL_ID] != aEntry[SHARED_USERURL_ID] )
327 : {
328 0 : aNewData[nNewInd] = m_aUsersData[nInd];
329 0 : nNewInd++;
330 : }
331 : }
332 : }
333 :
334 0 : aNewData.realloc( nNewInd );
335 0 : SetUsersDataAndStore( aNewData );
336 :
337 0 : if ( !nNewInd )
338 : {
339 : // try to remove the file if it is empty
340 0 : RemoveFile();
341 0 : }
342 0 : }
343 :
344 :
345 0 : void ShareControlFile::RemoveFile()
346 : {
347 0 : ::osl::MutexGuard aGuard( m_aMutex );
348 :
349 0 : if ( !IsValid() )
350 0 : throw io::NotConnectedException();
351 :
352 0 : Close();
353 :
354 0 : uno::Reference<ucb::XSimpleFileAccess3> xSimpleFileAccess(ucb::SimpleFileAccess::create(comphelper::getProcessComponentContext()));
355 0 : xSimpleFileAccess->kill( m_aURL );
356 0 : }
357 :
358 : } // namespace svt
359 :
360 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|