Fix no newlines warnings. Patch by Peter Oberndorfer
[kdevelopdvcssupport.git] / plugins / teamwork / patchmessage.cpp
blob86047f6a00eec80537a422f5d92ce6e6030cf1ed
1 /***************************************************************************
2 Copyright 2006 David Nolden <david.nolden.kdevelop@art-master.de>
3 ***************************************************************************/
5 /***************************************************************************
6 * *
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. *
11 * *
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>
19 #include <memory>
21 #include <k3process.h>
22 #include <kurl.h>
23 #include <kmimetype.h>
24 #include <memory.h>
25 #include <kio/netaccess.h>
26 #include <kio/job.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() {
39 return m_patch;
42 PatchesListMessage::PatchesListMessage( InArchive& arch, const Teamwork::MessageInfo& info ) : Precursor( arch, info ) {
43 serial( arch );
46 void PatchesListMessage::serialize( OutArchive& arch ) {
47 Precursor::serialize( arch );
48 serial( 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;
58 f.truncate( 1 );
59 bool b = false;
60 uint ret = f.toUInt( &b );
61 if( !b ) return 0;
62 else
63 return ret;
66 string LocalPatchSource::patchTool( bool reverse ) const {
67 QString cmd;
68 if( reverse )
69 cmd = (~unApplyCommand).trimmed();
71 if( cmd.isEmpty() )
72 cmd = (~applyCommand).trimmed();
74 if( cmd[0] == '-' ) return "patch";
75 int firstWhite = cmd.indexOf( " " );
76 QString ret;
77 if( firstWhite == -1 )
78 ret = cmd;
79 else
80 ret = cmd.mid( 0, firstWhite );
82 ret = ret.trimmed();
83 if( ret.isEmpty() )
84 return "patch";
85 else
86 return ~ret;
89 string LocalPatchSource::patchParams( bool reverse ) const {
91 QString cmd;
92 bool normalCommand = false;
93 if( reverse )
94 cmd = (~unApplyCommand).trimmed();
95 if( cmd.isEmpty() ) {
96 cmd = (~applyCommand).trimmed();
97 normalCommand = true;
99 if( cmd.isEmpty() )
100 return string("-p0 --backup") + (reverse ? " --reverse" : "");
101 else
102 if( reverse && normalCommand )
103 cmd += " --reverse";
105 if( cmd.startsWith( ~patchTool(reverse) ) )
106 return ~cmd.mid( patchTool(reverse).length() ).trimmed();
107 else
108 return ~cmd.trimmed();
112 void LocalPatchSource::setMimeType( KMimeType::Ptr mimeType ) {
113 QByteArray array;
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 {
137 Archive& arch;
138 PatchArchiver( Archive& a ) : arch(a) {
141 virtual void put( QByteArray& data ) {
142 arch & data;
145 virtual void put( int i ) {
146 arch & 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() {
160 finished = true;
163 void PatchData::transferCanceled() {
164 errored = true;
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
173 try {
174 arch & m_patch;
175 currentArchive = &arch;
176 errored = false;
177 finished = false;
178 if ( !m_patch )
179 throw "invalid patch-source";
180 LocalPatchSourcePointer::Locked lpatch = m_patch;
181 if ( !lpatch )
182 throw "patch-source could not be locked";
184 if ( !lpatch->filename.empty() ) {
185 ///Load a file
186 isBinary_ = true;
187 EntryType v = BinaryHeader;
188 arch << v;
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() )
204 errored = true;
206 QString errorText;
207 int error = transfer->error();
208 if ( error )
209 errorText = transfer->errorString();
211 transfer->kill();
213 if ( error )
214 throw ( "failed reading the file \"" + url.prettyUrl() + "\" reason: \"" + errorText + "\"" );
216 EntryType t = End;
217 arch << t;
219 } else if ( !lpatch->command.empty() ) {
220 ///Execute a command and send it's output
221 isBinary_ = false;
222 EntryType v = TextHeader;
223 arch << v;
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() );
237 *proc << args;
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";
248 EntryType t = End;
249 arch << t;
250 } else {
251 throw "the patch-data cannot be sent: missing command or url";
253 } catch ( const char * str ) {
254 currentArchive = 0;
255 throw NonFatalSerializationError( ( string( "PatchData::serialize: " ) + str ).c_str() );
256 } catch( QString str ) {
257 currentArchive = 0;
258 throw NonFatalSerializationError( ( string( "PatchData::serialize: " ) + str.toUtf8().data() ).c_str() );
260 currentArchive = 0;
263 //template<class Arch>
264 void PatchData::load( InArchive& arch, const uint /*version*/ ) {
265 deserialized = true;
266 //cout << "got patch: ";
268 arch & m_patch;
270 EntryType t;
271 arch >> t;
272 if ( t == BinaryHeader ) {
273 isBinary_ = true;
274 while ( 1 ) {
275 arch >> t;
276 if ( t == Vector ) {
277 vector<char> vec;
278 arch & vec;
279 //cout << str;
280 m_data.append( QByteArray( &( vec[ 0 ] ), vec.size() ) );
281 } else {
282 if ( t != End )
283 throw NonFatalSerializationError( "stream-error in PatchData(Text)" );
284 else
285 break;
288 } else {
289 isBinary_ = false;
290 while ( 1 ) {
291 arch >> t;
292 if ( t == Text ) {
293 string str;
294 arch & str;
295 //cout << str;
296 m_data.append( str.c_str() );
297 } else {
298 if ( t != End )
299 throw NonFatalSerializationError( "stream-error in PatchData(Text)" );
300 else
301 break;
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 ) {
323 if ( !errored ) {
324 log() << "received unexpected stdout-data";
325 errored = true;
327 return ;
329 EntryType t = Text;
330 *currentArchive << t;
331 string str( buffer );
332 *currentArchive << str;
335 bool PatchData::isBinary() {
336 return isBinary_;
339 const QByteArray& PatchData::data() {
340 return m_data;
343 QStringList splitArgs( const QString& str ) {
344 QStringList ret;
345 QString current = str;
346 int pos = 0;
347 while ( ( pos = current.indexOf( ' ', pos ) ) != -1 ) {
348 if ( current[ 0 ] == '"' ) {
349 int end = current.indexOf( '"' );
350 if ( end > pos )
351 pos = end;
353 QString s = current.left( pos );
354 if ( s.length() > 0 )
355 ret << s;
356 current = current.mid( pos + 1 );
357 pos = 0;
359 if ( current.length() )
360 ret << current;
361 return ret;
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;
366 if( l ) {
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" );
400 } else {
401 return cache( "generalrequest_out" );
403 } else {
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" );
410 } else {
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>();
427 if ( lmessage ) {
428 switch ( lmessage->message() ) {
429 case KDevSystemMessage::ActionFailed:
430 stat = Failed;
431 break;
432 case KDevSystemMessage::ActionSuccessful:
433 stat = Accepted;
434 break;
435 case KDevSystemMessage::ActionDenied:
436 stat = Denied;
437 break;
438 default:
439 stat = Unknown;
440 break;
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