File indexing completed on 2024-05-05 04:41:01

0001 /*
0002     SPDX-FileCopyrightText: 2007 Andreas Pakulat <apaku@gmx.de>
0003     SPDX-FileCopyrightText: 2007 Dukju Ahn <dukjuahn@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "svninternaljobbase.h"
0009 
0010 #include <QMutex>
0011 #include <QDateTime>
0012 
0013 #include <iostream>
0014 
0015 #include <KLocalizedString>
0016 
0017 extern "C" {
0018 #include <svn_auth.h>
0019 }
0020 
0021 #include <vcs/vcsrevision.h>
0022 
0023 #include <svnjobbase.h>
0024 #include "kdevsvncpp/context.hpp"
0025 #include "kdevsvncpp/apr.hpp"
0026 #include "kdevsvncpp/revision.hpp"
0027 
0028 SvnInternalJobBase::SvnInternalJobBase(SvnJobBase* parentJob)
0029     : m_ctxt( new svn::Context() )
0030     , m_guiSemaphore( 0 )
0031     , m_mutex()
0032     , m_killMutex()
0033     , m_parentJob(parentJob)
0034 {
0035     m_ctxt->setListener(this);
0036 }
0037 
0038 SvnInternalJobBase::~SvnInternalJobBase()
0039 {
0040     m_ctxt->setListener(nullptr);
0041     delete m_ctxt;
0042     m_ctxt = nullptr;
0043 }
0044 
0045 void SvnInternalJobBase::defaultBegin(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread)
0046 {
0047     emit started();
0048     ThreadWeaver::Job::defaultBegin(self, thread);
0049 }
0050 
0051 void SvnInternalJobBase::defaultEnd(const ThreadWeaver::JobPointer& self, ThreadWeaver::Thread *thread)
0052 {
0053     ThreadWeaver::Job::defaultEnd(self, thread);
0054     if (!self->success()) {
0055         emit failed();
0056     }
0057     emit done();
0058     // at this ppint this object cannot yet be deleted (e.g. as part of the parent job destruction,
0059     // ThreadWeaver logic still holds and uses a reference to finish the execution logic
0060 }
0061 
0062 bool SvnInternalJobBase::contextGetLogin( const std::string& realm,
0063                         std::string& username, std::string& password,
0064                         bool& maySave )
0065 {
0066 
0067     emit needLogin( QString::fromUtf8( realm.c_str() )  );
0068     m_guiSemaphore.acquire( 1 );
0069     QMutexLocker l(&m_mutex);
0070     if( m_login_username.isEmpty() || m_login_password.isEmpty() )
0071         return false;
0072     username = std::string(m_login_username.toUtf8().constData());
0073     password = std::string(m_login_password.toUtf8().constData());
0074     maySave = this->m_maySave;
0075     return true;
0076 }
0077 
0078 void SvnInternalJobBase::contextNotify( const char* path, svn_wc_notify_action_t action,
0079                     svn_node_kind_t kind, const char* mimetype,
0080                     svn_wc_notify_state_t contentState,
0081                     svn_wc_notify_state_t propState, svn_revnum_t rev )
0082 {
0083     QString notifyString;
0084     switch( action ){
0085         case svn_wc_notify_add:
0086             notifyString = i18nc( "A file was marked to be added to svn", "Added %1", QString::fromUtf8( path ) );
0087             break;
0088         case svn_wc_notify_delete:
0089             notifyString = i18nc( "A file was marked for deletion from svn", "Deleted %1", QString::fromUtf8( path ) );
0090             break;
0091         // various update notifications
0092         case svn_wc_notify_update_delete:
0093             notifyString = i18nc( "A file was deleted during an svn update operation", "Deleted %1", QString::fromUtf8( path ) );
0094             break;
0095         case svn_wc_notify_update_add:
0096             notifyString = i18nc( "A file was added during an svn update operation", "Added %1", QString::fromUtf8( path ) );
0097             break;
0098         case svn_wc_notify_update_update:
0099          /* If this is an inoperative dir change, do no notification.
0100             An inoperative dir change is when a directory gets closed
0101             without any props having been changed. */
0102             if (! ((kind == svn_node_dir)
0103                     && ((propState == svn_wc_notify_state_inapplicable)
0104                     || (propState == svn_wc_notify_state_unknown)
0105                     || (propState == svn_wc_notify_state_unchanged)))) {
0106 
0107                 if (kind == svn_node_file) {
0108                     if (contentState == svn_wc_notify_state_conflicted)
0109                         notifyString = QStringLiteral("Conflict On File");
0110                     else if (contentState == svn_wc_notify_state_merged)
0111                         notifyString = QStringLiteral("File Merged");
0112                     else if (contentState == svn_wc_notify_state_changed)
0113                         notifyString = QStringLiteral("File Updated");
0114                 }
0115 
0116                 if (propState == svn_wc_notify_state_conflicted)
0117                     notifyString += QLatin1String(" Conflict On Property");
0118                 else if (propState == svn_wc_notify_state_merged)
0119                     notifyString += QLatin1String(" Properties Merged");
0120                 else if (propState == svn_wc_notify_state_changed)
0121                     notifyString += QLatin1String(" Properties Updated");
0122                 else
0123                     notifyString += QLatin1Char(' ');
0124 
0125                 if (! ((contentState == svn_wc_notify_state_unchanged
0126                         || contentState == svn_wc_notify_state_unknown)
0127                         && (propState == svn_wc_notify_state_unchanged
0128                         || propState == svn_wc_notify_state_unknown)))
0129                     notifyString += QLatin1Char(' ') + QString::fromUtf8(path);
0130 
0131             }
0132             break;
0133 
0134         case svn_wc_notify_update_completed:
0135             // The last notification in an update (including updates of externals).
0136             notifyString = i18n("Revision %1", rev );
0137             break;
0138         case svn_wc_notify_update_external:
0139             notifyString = i18n("Updating externals: %1", QString::fromUtf8( path ) );
0140             break;
0141         case svn_wc_notify_status_completed:
0142             break;
0143         case svn_wc_notify_status_external:
0144             break;
0145         // various commit notifications
0146         case svn_wc_notify_commit_modified:
0147             notifyString = i18n( "Sending %1", QString::fromUtf8( path ) );
0148             break;
0149         case svn_wc_notify_commit_added:
0150             if( mimetype ){
0151                 notifyString = i18n("Adding %1 using mimetype %2.", QString::fromUtf8(path), QString::fromUtf8(mimetype));
0152             } else {
0153                 notifyString = i18n( "Adding %1.", QString::fromUtf8( path ) );
0154             }
0155             break;
0156         case svn_wc_notify_commit_deleted:
0157             notifyString = i18n( "Deleting %1.", QString::fromUtf8( path ) );
0158             break;
0159         case svn_wc_notify_commit_replaced:
0160             notifyString = i18n( "Replacing %1.", QString::fromUtf8( path ) );
0161             break;
0162         case svn_wc_notify_commit_postfix_txdelta:
0163             if ( sendFirstDelta ) {
0164                 sendFirstDelta = false;
0165                 notifyString=i18n("Transmitting file data ");
0166             } else {
0167                 notifyString = QStringLiteral(".");
0168             }
0169             break;
0170         case svn_wc_notify_blame_revision:
0171             notifyString = i18n( "Blame finished for revision %1, path %2", rev, QString::fromUtf8( path ) );
0172             break;
0173         case svn_wc_notify_revert:
0174             notifyString = i18n( "Reverted working copy %1", QString::fromUtf8( path ) );
0175             break;
0176         case svn_wc_notify_failed_revert:
0177             notifyString = i18n( "Reverting failed on working copy %1", QString::fromUtf8( path ) );
0178             break;
0179         case svn_wc_notify_copy:
0180             notifyString = i18n( "Copied %1", QString::fromUtf8( path ) );
0181             break;
0182         default:
0183             break;
0184     }
0185     emit showNotification( QString::fromUtf8( path ), notifyString );
0186 }
0187 
0188 bool SvnInternalJobBase::contextCancel()
0189 {
0190     QMutexLocker lock( &m_killMutex );
0191     return killed;
0192 }
0193 
0194 bool SvnInternalJobBase::contextGetLogMessage( std::string& msg )
0195 {
0196     emit needCommitMessage();
0197     m_guiSemaphore.acquire( 1 );
0198     QMutexLocker l( &m_mutex );
0199     QByteArray ba = m_commitMessage.toUtf8();
0200     msg = std::string( ba.data() );
0201     return true;
0202 }
0203 
0204 void SvnInternalJobBase::initBeforeRun()
0205 {
0206     connect( this, &SvnInternalJobBase::needCommitMessage,
0207              m_parentJob, &SvnJobBase::askForCommitMessage, Qt::QueuedConnection );
0208     connect( this, &SvnInternalJobBase::needLogin,
0209              m_parentJob,  &SvnJobBase::askForLogin, Qt::QueuedConnection );
0210     connect( this, &SvnInternalJobBase::needSslServerTrust,
0211              m_parentJob,  &SvnJobBase::askForSslServerTrust, Qt::QueuedConnection );
0212     connect( this, &SvnInternalJobBase::showNotification,
0213              m_parentJob,  &SvnJobBase::showNotification, Qt::QueuedConnection );
0214     connect( this, &SvnInternalJobBase::needSslClientCert,
0215              m_parentJob,  &SvnJobBase::askForSslClientCert, Qt::QueuedConnection );
0216     connect( this, &SvnInternalJobBase::needSslClientCertPassword,
0217              m_parentJob,  &SvnJobBase::askForSslClientCertPassword, Qt::QueuedConnection );
0218 }
0219 
0220 svn::ContextListener::SslServerTrustAnswer SvnInternalJobBase::contextSslServerTrustPrompt(
0221         const svn::ContextListener::SslServerTrustData& data,
0222         apr_uint32_t& acceptedFailures )
0223 {
0224 
0225     std::string host = data.hostname;
0226     std::string print = data.fingerprint;
0227     std::string from = data.validFrom;
0228     std::string until = data.validUntil;
0229     std::string issue = data.issuerDName;
0230     std::string realm = data.realm;
0231     acceptedFailures = data.failures;
0232     QStringList failures;
0233     if( data.failures & SVN_AUTH_SSL_NOTYETVALID )
0234     {
0235         failures << i18n("Certificate is not yet valid.");
0236     }
0237     if( data.failures & SVN_AUTH_SSL_EXPIRED )
0238     {
0239         failures << i18n("Certificate has expired.");
0240     }
0241     if( data.failures & SVN_AUTH_SSL_CNMISMATCH )
0242     {
0243         failures << i18n("Certificate's CN (hostname) doesn't match the remote hostname.");
0244     }
0245     if( data.failures & SVN_AUTH_SSL_UNKNOWNCA )
0246     {
0247         failures << i18n("Certificate authority is unknown.");
0248     }
0249     if( data.failures & SVN_AUTH_SSL_NOTYETVALID )
0250     {
0251         failures << i18n("Other unknown error.");
0252     }
0253     emit needSslServerTrust( failures,
0254                                      QString::fromUtf8( host.c_str() ),
0255                                      QString::fromUtf8( print.c_str() ),
0256                                      QString::fromUtf8( from.c_str() ),
0257                                      QString::fromUtf8( until.c_str() ),
0258                                      QString::fromUtf8( issue.c_str() ),
0259                                      QString::fromUtf8( realm.c_str() ) );
0260     m_guiSemaphore.acquire(1);
0261     QMutexLocker l(&m_mutex);
0262     return m_trustAnswer;
0263 }
0264 
0265 bool SvnInternalJobBase::contextSslClientCertPrompt( std::string& cert )
0266 {
0267     emit needSslClientCert( QString::fromUtf8( cert.c_str() ) );
0268     m_guiSemaphore.acquire( 1 );
0269     return true;
0270 }
0271 
0272 bool SvnInternalJobBase::contextSslClientCertPwPrompt( std::string& pw, const std::string& realm,
0273                                     bool& maySave )
0274 {
0275     Q_UNUSED(pw);
0276     Q_UNUSED(maySave);
0277     emit needSslClientCertPassword( QString::fromUtf8( realm.c_str() ) );
0278     m_guiSemaphore.acquire( 1 );
0279     return false;
0280 }
0281 
0282 bool SvnInternalJobBase::success() const
0283 {
0284     return m_success;
0285 }
0286 
0287 svn::Revision SvnInternalJobBase::createSvnCppRevisionFromVcsRevision( const KDevelop::VcsRevision& revision )
0288 {
0289     svn::Revision rev;
0290     QVariant value = revision.revisionValue();
0291     switch( revision.revisionType() )
0292     {
0293         case KDevelop::VcsRevision::Special:
0294         {
0295             if( value.canConvert<KDevelop::VcsRevision::RevisionSpecialType>() )
0296             {
0297                 auto specialtype =
0298                         value.value<KDevelop::VcsRevision::RevisionSpecialType>();
0299                 switch( specialtype )
0300                 {
0301                     case KDevelop::VcsRevision::Head:
0302                         rev = svn::Revision( svn::Revision::HEAD );
0303                         break;
0304                     case KDevelop::VcsRevision::Working:
0305                         rev = svn::Revision( svn::Revision::WORKING );
0306                         break;
0307                     case KDevelop::VcsRevision::Base:
0308                         rev = svn::Revision( svn::Revision::BASE );
0309                         break;
0310                     case KDevelop::VcsRevision::Previous:
0311                         rev = svn::Revision( svn_opt_revision_previous );
0312                         break;
0313                     case KDevelop::VcsRevision::Start:
0314                         rev = svn::Revision( svn::Revision::START );
0315                         break;
0316                     default:
0317                         break;
0318                 }
0319             }
0320             break;
0321         }
0322         case KDevelop::VcsRevision::GlobalNumber:
0323         case KDevelop::VcsRevision::FileNumber:
0324         {
0325             bool ok;
0326             qlonglong number = value.toLongLong(&ok);
0327             if( ok )
0328             {
0329                 rev = svn::Revision( number );
0330             }
0331             break;
0332         }
0333         case KDevelop::VcsRevision::Date:
0334         {
0335             QDateTime dt = value.toDateTime();
0336             if( dt.isValid() )
0337             {
0338                 rev = svn::Revision( dt.toSecsSinceEpoch() );
0339             }
0340             break;
0341         }
0342         default:
0343             break;
0344     }
0345     return rev;
0346 }
0347 
0348 
0349 void SvnInternalJobBase::setErrorMessage( const QString& msg )
0350 {
0351     QMutexLocker lock( &m_mutex );
0352     m_errorMessage = msg;
0353 }
0354 
0355 QString SvnInternalJobBase::errorMessage() const
0356 {
0357     QMutexLocker lock( &m_mutex );
0358     return m_errorMessage;
0359 }
0360 
0361 void SvnInternalJobBase::kill()
0362 {
0363     QMutexLocker lock( &m_killMutex );
0364     killed = true;
0365 }
0366 
0367 #include "moc_svninternaljobbase.cpp"