[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kfm-devel
Subject:    Re: HTTPPostJob
From:       "Dawit A." <adawit () kde ! org>
Date:       2003-02-14 2:26:30
[Download RAW message or body]

Hello,

Okay. Here is the class.  It is only a rough draft and meant to get ideas on 
how to improve it.  It is going to require the end applications provide a 
pointer to a QIODevice.

Regards,
Dawit A.

["postjob.cpp" (text/x-c++src)]

#define KIO_DEFAULT_BLOCK_SIZE 1024*256


PostJob::PostJob (const KURL& url, int command, const QByteArray& packedArgs,
                          QIODevice * source, bool showProgressInfo)
            :TransferJob (url, command, packedArgs, QByteArray(), showProgressInfo),
             _bytesSent(0), _blockSize (KIO_DEFAULT_BLOCK_SIZE),
             _autoDeleteSource (false), _dataSource(source)
{
}

PostJob::PostJob (const KURL& url, int command, const QByteArray& packedArgs,
                  const QByteArray& data, bool showProgressInfo)
        :TransferJob (url, command, packedArgs, QByteArray(), showProgressInfo),
         _bytesSent(0), _blockSize (KIO_DEFAULT_BLOCK_SIZE),
         _autoDeleteSource (true)
{
  // If the data is larger than the default block size use a temp file
  // to save the data and send it in segments to the io-slave.  Otherwise,
  // simply use a QBuffer to store and transmit the data...
  if (data.size() < KIO_DEFAULT_BLOCK_SIZE)
  {
    _dataSource = new QBuffer (data);
  }
  else
  {
    KTempFile temp (QString::null, QString::null, 0600);

    temp.close ();

    _tempfile = temp.name ();

    _dataSource = new QFile (_tempfile);

    if (_dataSource->open (IO_WriteOnly))
    {
      KIO::filesize_t offset = 0;
      KIO::filesize_t segment;

      while (offset < data.size())
      {
        segment = (data.size () < _blockSize) ? data.size ():_blockSize;
        _dataSource->writeBlock (data.data() + offset, segment);
        offset += segment;
      }

      // Close it to force a flush. It will be re-opened when
      // start is invoked...
      _dataSource->close();
    }
  }
}

PostJob::~PostJob ()
{
  if (_autoDeleteSource && _dataSource)
  {
    kdDebug(7007) << "PostJob dtor: Deleting pointer to data source" << endl;
    _dataSource->close ();
    delete _dataSource;
    _dataSource = 0;
  }
  
  // Unlink the tempfile if it exists.
  if (!_tempfile.isEmpty())
    QFile::remove (_tempfile);
}

bool PostJob::isPortSafe()
{
  bool valid = true;

  // filter out non https? protocols
  if ((m_url.protocol() != "http") && (m_url.protocol() != "https" ))
    valid = false;

  // filter out some malicious ports
  static const int bad_ports[] = 
  {
    1,   // tcpmux
    7,   // echo
    9,   // discard
    11,   // systat
    13,   // daytime
    15,   // netstat
    17,   // qotd
    19,   // chargen
    20,   // ftp-data
    21,   // ftp-cntl
    22,   // ssh
    23,   // telnet
    25,   // smtp
    37,   // time
    42,   // name
    43,   // nicname
    53,   // domain
    77,   // priv-rjs
    79,   // finger
    87,   // ttylink
    95,   // supdup
    101,  // hostriame
    102,  // iso-tsap
    103,  // gppitnp
    104,  // acr-nema
    109,  // pop2
    110,  // pop3
    111,  // sunrpc
    113,  // auth
    115,  // sftp
    117,  // uucp-path
    119,  // nntp
    123,  // NTP
    135,  // loc-srv / epmap
    139,  // netbios
    143,  // imap2
    179,  // BGP
    389,  // ldap
    512,  // print / exec
    513,  // login
    514,  // shell
    515,  // printer
    526,  // tempo
    530,  // courier
    531,  // Chat
    532,  // netnews
    540,  // uucp
    556,  // remotefs
    587,  // sendmail
    601,  //
    989,  // ftps data
    990,  // ftps
    992,  // telnets
    993,  // imap/SSL
    995,  // pop3/SSL
    1080, // SOCKS
    2049, // nfs
    4045, // lockd
    6000, // x11
    6667, // irc
    0
  };
    
  for (int cnt=0; bad_ports[cnt]; ++cnt)
  {
    if (m_url.port() == bad_ports[cnt])
    {
      valid = false;
      break;
    }
  }

  if( !valid )
  {
    static bool override_loaded = false;
    static QValueList< int >* overriden_ports = NULL;

    if( !override_loaded )
    {
      KConfig cfg( "kio_httprc", true );
      overriden_ports = new QValueList< int >;
      *overriden_ports = cfg.readIntListEntry( "OverriddenPorts" );
      override_loaded = true;
    }

    for (QValueList< int >::ConstIterator it = overriden_ports->begin();
        it != overriden_ports->end(); ++it)
    {
      if( overriden_ports->contains(m_url.port()))
        valid = true;
    }
  }

  // if request is not valid, return an invalid transfer job
  if (!valid)
  {
    m_error = KIO::ERR_POST_DENIED;
    m_errorText = m_url.url();  
  }
  
  return valid;  
}

void PostJob::setBlockSize (KIO::filesize_t size)
{
  _blockSize = size;
}

KIO::filesize_t PostJob::blockSize () const
{
  return _blockSize;
}

void PostJob::start (Slave* slave)
{
  assert (_dataSource);

  if (_validatePort && !isPortSafe())
  {
    slotFinished ();
    return;
  }
  
  // Open the source device if it is currently not opened.
  if (!_dataSource->isOpen() && !_dataSource->open (IO_ReadOnly))
  {
    kdDebug (7007) << "PostJob::slotDataReq: Unable to open the post data source!!" << endl;
    
    m_error = KIO::ERR_INTERNAL;
    m_errorText = m_url.url();  
    
    slotFinished();
    return;
  }  
  
  m_totalSize = _dataSource->size();
  
  if (m_totalSize == 0)
  {  
    kdDebug (7007) << "PostJob::slotDataReq: Hmmm... the post data source is empty!!" << endl;
    
    m_error = KIO::ERR_INTERNAL;
    m_errorText = m_url.url();

    slotFinished();
    return;
  }

  addMetaData ("content-length", KIO::number (m_totalSize));
  TransferJob::start (slave);
}

void PostJob::slotDataReq()
{
  assert(_dataSource);

  // If we reached the end of the device, indicate the condition
  // to the io-slave and reset the device position to 0 so that
  // the data can be re-transmitted if the io-slave requests it
  // again...
  if (_dataSource->atEnd())
  {
    _dataSource->reset();
    staticData = QByteArray();
    _bytesSent = 0;
    kdDebug (7007) << "PostJob::slotDataReq: DONE => resetting data buffer..." << endl;
  }
  else
  {
    KIO::filesize_t dataSize = _blockSize;

    if ((m_totalSize - _bytesSent) < dataSize)
      dataSize = m_totalSize - _bytesSent;

    staticData.fill (0, dataSize);

    if (_dataSource->readBlock (staticData.data(), dataSize) < 0)
    {
      m_error = ERR_INTERNAL;
      m_errorText = i18n("PostJob::slotDataReq: Could not read POST data from source.");

      slotFinished ();
      kdDebug(7007) << "PostJob::slotDataReq: Error reading data..." << endl;
      return;
    }

    _bytesSent += dataSize;
    kdDebug (7007) << "PostJob::slotDataReq: Sent " << KIO::number(_bytesSent)
                   << "/" << KIO::number(m_totalSize) << " bytes..." << endl;
  }

  TransferJob::slotDataReq();
}

void PostJob::setEnablePortCheck (bool enable)
{
  _validatePort = enable;
}

bool PostJob::isPortCheckEnabled () const
{
  return _validatePort;
}

["postjob.hpp" (text/x-c++src)]

/**
* A job to stream data to the remote machine.
*
* @author Dawit Alemayehu <adawit@kde.org>
*/

class PostJob : public TransferJob {
Q_OBJECT

public:
    PostJob (const KURL& url, int command, const QByteArray& packedArgs,
            QIODevice * device, bool showProgressInfo);

    /**
    * @deprecated. This ctor is only provided for compatability sake
    * until all protocols that use KIO::http_post are ported to use
    * the newer API above.  Use the above ctor instead.
    */
    PostJob (const KURL& url, int command, const QByteArray& packedArgs,
            const QByteArray& data, bool showProgressInfo);

    ~PostJob ();

    /**
    * Set the size at which data is streamed (sent) to the io-slave.
    * The default block size (segement) is 256K. Due to the limitation
    * in the underlying framework (copying of data), the maximum size
    * allowed 1 MB.  Any value above that will be truncated.
    *
    * @param size segement in bytes at which data should be sent to the
    * io-slave.
    */
    void setBlockSize (KIO::filesize_t size);

    /**
    * @return the size in bytes of the data segement sent to an io-slave
    * at one time.
    */
    KIO::filesize_t blockSize () const;

    /**
    * Re-implemented for internal reasons.  API is unaffected.
    *
    * Note that the job will fail and return error if the POST action
    * is being done to a non-safe port. This of course depends on whether
    * non-safe port checking has been enabled or not.
    */
    virtual void start (Slave* slave);

    /**
    * Enable/disable port verification to avoid maclicious posting.
    * Default is enabled.  The default might not be applicable to all
    * protocols. If you rather do the checking yourself internally,
    * disable the check using this function.
    *
    * @param enable if true perform port check before starting a job. Do
    *               not perform any checks otherwise.
    */
    void setEnablePortCheck (bool enable);

    /**
    * @return true if non-safe port checking has been enabled. False otherwise.
    */
    bool isPortCheckEnabled () const;

protected:
    /**
    * Verifies whether or not the POST action is being done to a "safe"
    * (non-malicious) port number.
    *
    * @return true if port number is safe, false otherwise.
    */
    virtual bool isPortSafe ();

protected slots:
    /**
    * Re-implemented for internal reasons. API remains unaffected.
    */
    virtual void slotDataReq ();

private:
    KIO::filesize_t _bytesSent;
    KIO::filesize_t _blockSize;
    bool _autoDeleteSource;
    bool _validatePort;

    QIODevice* _dataSource;
    QString _tempfile;
    class HTTPPostPrivate* d;
};

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic