1 /***************************************************************************
2 Copyright 2006 David Nolden <david.nolden.kdevelop@art-master.de>
3 ***************************************************************************/
5 /***************************************************************************
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
12 ***************************************************************************/
14 #include "patchmessage.h"
15 #include "lib/network/serialization.h"
16 #include <boost/serialization/list.hpp>
17 #include <boost/serialization/string.hpp>
18 #include <boost/serialization/vector.hpp>
21 #include <k3process.h>
23 #include <kmimetype.h>
25 #include <kio/netaccess.h>
27 #include <kio/jobclasses.h>
28 #include "teamworkfoldermanager.h"
30 #include "kdevteamwork.h"
32 #include "kdevteamwork_helpers.h"
34 PatchData::PatchData( const LocalPatchSourcePointer
& p
, LoggerPointer logg
) : m_patch( p
), logger( logg
), deserialized( false ), finished( false ), isBinary_( false ) {
35 projectDir
= TeamworkFolderManager::workspaceDirectory();
38 LocalPatchSourcePointer
PatchData::patch() {
42 PatchesListMessage::PatchesListMessage( InArchive
& arch
, const Teamwork::MessageInfo
& info
) : Precursor( arch
, info
) {
46 void PatchesListMessage::serialize( OutArchive
& arch
) {
47 Precursor::serialize( arch
);
51 uint
LocalPatchSource::patchDepth() const {
52 QString cmd
= ~applyCommand
;
53 int i
= cmd
.indexOf( "-p" );
54 if( i
== -1 ) return 0;
55 if( i
+1 >= cmd
.length() ) return 0;
56 QString f
= cmd
.mid( i
+2 ).trimmed();
57 if( f
.isEmpty() ) return 0;
60 uint ret
= f
.toUInt( &b
);
66 string
LocalPatchSource::patchTool( bool reverse
) const {
69 cmd
= (~unApplyCommand
).trimmed();
72 cmd
= (~applyCommand
).trimmed();
74 if( cmd
[0] == '-' ) return "patch";
75 int firstWhite
= cmd
.indexOf( " " );
77 if( firstWhite
== -1 )
80 ret
= cmd
.mid( 0, firstWhite
);
89 string
LocalPatchSource::patchParams( bool reverse
) const {
92 bool normalCommand
= false;
94 cmd
= (~unApplyCommand
).trimmed();
96 cmd
= (~applyCommand
).trimmed();
100 return string("-p0 --backup") + (reverse
? " --reverse" : "");
102 if( reverse
&& normalCommand
)
105 if( cmd
.startsWith( ~patchTool(reverse
) ) )
106 return ~cmd
.mid( patchTool(reverse
).length() ).trimmed();
108 return ~cmd
.trimmed();
112 void LocalPatchSource::setMimeType( KMimeType::Ptr mimeType ) {
115 QDataStream str( &array, QIODevice::WriteOnly );
116 mimeType->save( str );
118 mimetype.resize( array.count() );
119 memcpy( &( mimetype[ 0 ] ), array.data(), array.count() );
122 KMimeType::Ptr LocalPatchSource::getMimeType() {
123 QByteArray array( &( mimetype[ 0 ] ), mimetype.size() );
124 QDataStream str( &array, QIODevice::ReadOnly );
125 return KMimeType::Ptr( new KMimeType( str, 0 ) );
128 class AbstractPatchArchiver
{
129 virtual void put( QByteArray
& data
);
130 virtual void put( int i
);
131 virtual ~AbstractPatchArchiver() {
135 template<class Archive>
136 class PatchArchiver {
138 PatchArchiver( Archive& a ) : arch(a) {
141 virtual void put( QByteArray& data ) {
145 virtual void put( int i ) {
150 void PatchData::transferData( KIO::Job
* /*job*/, const QByteArray
& data
) {
151 std::vector
<char> vec
;
152 vec
.resize( data
.size() ); ///a serialization-function for QByteArray should be written instead of moving the data through std::vector
153 memcpy( &(vec
[0]), data
.data(), data
.size() );
154 EntryType v
= Vector
;
155 *currentArchive
<< v
;
156 *currentArchive
& vec
;
159 void PatchData::transferFinished() {
163 void PatchData::transferCanceled() {
167 //template<class Arch>
168 void PatchData::saveInternal( OutArchive
& arch
, const uint
/*version*/ ) {
169 auto_ptr
<PatchDataReceiver
> rec( new PatchDataReceiver( this ) );
171 if ( !m_patch
|| deserialized
)
172 throw CannotReserialize(); ///This type of Message can only be serialized after being constructed with a local patch-source
175 currentArchive
= &arch
;
179 throw "invalid patch-source";
180 LocalPatchSourcePointer::Locked lpatch
= m_patch
;
182 throw "patch-source could not be locked";
184 if ( !lpatch
->filename
.empty() ) {
187 EntryType v
= BinaryHeader
;
189 KUrl url
= TeamworkFolderManager::teamworkAbsolute( ~lpatch
->filename
);
190 log( Logger::Debug
) << "opening file for sending:" << ~url
.prettyUrl();
192 //(QWidget*)KDevApi::self()->mainWindow()->main()
193 if ( !KIO::NetAccess::exists( url
, KIO::NetAccess::SourceSide
, 0 ) )
194 throw ( "the file \"" + url
.prettyUrl() + "\" seems not to exist, or is not accessible" );
196 auto_ptr
<KIO::TransferJob
> transfer( KIO::get
197 ( url
, KIO::NoReload
, KIO::HideProgressInfo
) );
198 if ( !transfer
.get() )
199 throw ( "could not create transfer-job for reading " + url
.prettyUrl() );
200 //transfer->setWindow( (QWidget*)KDevApi::self()->mainWindow()->main() );
201 QObject::connect( &( *transfer
), SIGNAL( data( KIO::Job
*, const QByteArray
& ) ), &( *rec
), SLOT( transferData( KIO::Job
*, const QByteArray
& ) ), Qt::DirectConnection
);
202 QObject::connect( &( *transfer
), SIGNAL( result( KJob
* ) ), &( *rec
), SLOT( transferFinished( KJob
* ) ), Qt::DirectConnection
);
203 if ( !transfer
->exec() )
207 int error
= transfer
->error();
209 errorText
= transfer
->errorString();
214 throw ( "failed reading the file \"" + url
.prettyUrl() + "\" reason: \"" + errorText
+ "\"" );
219 } else if ( !lpatch
->command
.empty() ) {
220 ///Execute a command and send it's output
222 EntryType v
= TextHeader
;
224 QStringList args
= splitArgs( ~lpatch
->command
);
226 LoggerPrinter l
= log() << "calling process: \"" << lpatch
->command
<< "\" params: ";
227 for ( QStringList::iterator it
= args
.begin(); it
!= args
.end(); ++it
)
228 l
<< "\"" << ~( *it
) << "\"";
229 l
<< "in folder" << ~projectDir
.toLocalFile();
232 auto_ptr
<K3Process
> proc( new K3Process() );
233 QObject::connect( &( *proc
), SIGNAL( receivedStdout( K3Process
*, char*, int ) ), &( *rec
), SLOT( receivedStdout( K3Process
*, char*, int ) ), Qt::DirectConnection
);
235 proc
->setPriority( K3Process::PrioLowest
);
236 proc
->setWorkingDirectory( projectDir
.toLocalFile() );
238 if ( !proc
->start( K3Process::Block
, K3Process::Stdout
) )
239 throw "the process could not be started";
241 if ( !proc
->normalExit() )
242 throw "process did not exit normally";
243 if ( proc
->exitStatus() != 0 )
244 throw QString( "process returned with exit-status " + QString::number( proc
->exitStatus() ) );
246 log() << lpatch
->command
<< ": successful";
251 throw "the patch-data cannot be sent: missing command or url";
253 } catch ( const char * str
) {
255 throw NonFatalSerializationError( ( string( "PatchData::serialize: " ) + str
).c_str() );
256 } catch( QString str
) {
258 throw NonFatalSerializationError( ( string( "PatchData::serialize: " ) + str
.toUtf8().data() ).c_str() );
263 //template<class Arch>
264 void PatchData::load( InArchive
& arch
, const uint
/*version*/ ) {
266 //cout << "got patch: ";
272 if ( t
== BinaryHeader
) {
280 m_data
.append( QByteArray( &( vec
[ 0 ] ), vec
.size() ) );
283 throw NonFatalSerializationError( "stream-error in PatchData(Text)" );
296 m_data
.append( str
.c_str() );
299 throw NonFatalSerializationError( "stream-error in PatchData(Text)" );
308 template void PatchData::load( boost::archive::polymorphic_iarchive& arch, const uint );
310 template void PatchData::saveInternal( boost::archive::polymorphic_oarchive& arch, const uint );
312 template void PatchData::load( boost::archive::text_iarchive& arch, const uint );
314 template void PatchData::saveInternal( boost::archive::text_oarchive& arch, const uint );
316 template void PatchData::load( boost::archive::binary_iarchive& arch, const uint );
318 template void PatchData::saveInternal( boost::archive::binary_oarchive& arch, const uint );
321 void PatchData::receivedStdout( K3Process */
*proc*/
, char *buffer
, int /*buflen*/ ) {
322 if ( !currentArchive
|| errored
) {
324 log() << "received unexpected stdout-data";
330 *currentArchive
<< t
;
331 string
str( buffer
);
332 *currentArchive
<< str
;
335 bool PatchData::isBinary() {
339 const QByteArray
& PatchData::data() {
343 QStringList
splitArgs( const QString
& str
) {
345 QString current
= str
;
347 while ( ( pos
= current
.indexOf( ' ', pos
) ) != -1 ) {
348 if ( current
[ 0 ] == '"' ) {
349 int end
= current
.indexOf( '"' );
353 QString s
= current
.left( pos
);
354 if ( s
.length() > 0 )
356 current
= current
.mid( pos
+ 1 );
359 if ( current
.length() )
364 PatchRequestData::PatchRequestData( const LocalPatchSourcePointer
& id
, KDevTeamwork
* tw
, RequestType req
) : request_( id
), requestType_( req
), stat( Waiting
), emitter( new SafeTeamworkEmitter( tw
) ) {
365 LocalPatchSourcePointer::Locked l
= id
;
367 ident_
= l
->identity();
371 PatchDataReceiver::PatchDataReceiver( PatchData
* d
) : data( d
) {}
372 void PatchDataReceiver::receivedStdout( K3Process
*proc
, char *buffer
, int buflen
) {
373 data
->receivedStdout( proc
, buffer
, buflen
);
376 void PatchDataReceiver::transferData( KIO::Job
* job
, const QByteArray
& array
) {
377 data
->transferData( job
, array
);
380 void PatchDataReceiver::transferFinished( KJob */
*job*/
) {
381 data
->transferFinished();
384 QString
PatchRequestData::messageText() const {
385 return "patch-request: " + ~patchDesc();
389 QIcon
PatchRequestData::messageIcon() const {
390 IconCache
& cache( *IconCache::instance() );
392 if ( !selfMessage() ->info().isIncoming() ) {
393 return cache( "generalrequest_out" );
394 if ( stat
== Denied
) {
395 return cache( "generalrequest_out_denied" );
396 } else if ( stat
== Accepted
) {
397 return cache( "generalrequest_out_accepted" );
398 } else if ( stat
== Unknown
) {
399 return cache( "unknown" );
401 return cache( "generalrequest_out" );
404 if ( stat
== Denied
) {
405 return cache( "generalrequest_in_denied" );
406 } else if ( stat
== Accepted
) {
407 return cache( "generalrequest_in_accepted" );
408 } else if ( stat
== Unknown
) {
409 return cache( "unknown" );
411 return cache( "generalrequest_in" );
416 PatchRequestMessage
* PatchRequestData::selfMessage() {
417 return dynamic_cast<PatchRequestMessage
*>( this );
420 const PatchRequestMessage
* PatchRequestData::selfMessage() const {
421 return dynamic_cast<const PatchRequestMessage
*>( this );
424 MessageInterface::ReplyResult
PatchRequestMessage::gotReply( const MessagePointer
& p
) {
425 KDevSystemMessagePointer::Locked lmessage
= ( ( MessagePointer
) p
).cast
<KDevSystemMessage
>();
428 switch ( lmessage
->message() ) {
429 case KDevSystemMessage::ActionFailed
:
432 case KDevSystemMessage::ActionSuccessful
:
435 case KDevSystemMessage::ActionDenied
:
444 if ( emitter
.data() )
445 emitter
->updateMessageInfo( this );
447 return ReplyResult();
450 PatchRequestData::~PatchRequestData() {
453 #include "patchmessage.moc"
455 // kate: space-indent on; indent-width 2; tab-width 2; replace-tabs on