📄 ftp.cpp
字号:
kDebug(7102) << "Ftp::stat : cleaned path='" << path << "'"; // We can't stat root, but we know it's a dir. if( path.isEmpty() || path == "/" ) { UDSEntry entry; //entry.insert( KIO::UDSEntry::UDS_NAME, UDSField( QString() ) ); entry.insert( KIO::UDSEntry::UDS_NAME, QString::fromLatin1( "." ) ); entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR ); entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ); entry.insert( KIO::UDSEntry::UDS_USER, QString::fromLatin1( "root" ) ); entry.insert( KIO::UDSEntry::UDS_GROUP, QString::fromLatin1( "root" ) ); // no size statEntry( entry ); finished(); return; } KUrl tempurl( url ); tempurl.setPath( path ); // take the clean one QString listarg; // = tempurl.directory(KUrl::ObeyTrailingSlash); QString parentDir; QString filename = tempurl.fileName(); Q_ASSERT(!filename.isEmpty()); QString search = filename; // Try cwd into it, if it works it's a dir (and then we'll list the parent directory to get more info) // if it doesn't work, it's a file (and then we'll use dir filename) bool isDir = ftpFolder(path, false); // if we're only interested in "file or directory", we should stop here QString sDetails = metaData("details"); int details = sDetails.isEmpty() ? 2 : sDetails.toInt(); kDebug(7102) << "Ftp::stat details=" << details; if ( details == 0 ) { if ( !isDir && !ftpSize( path, 'I' ) ) // ok, not a dir -> is it a file ? { // no -> it doesn't exist at all ftpStatAnswerNotFound( path, filename ); return; } ftpShortStatAnswer( filename, isDir ); // successfully found a dir or a file -> done return; } if (!isDir) { // It is a file or it doesn't exist, try going to parent directory parentDir = tempurl.directory(KUrl::AppendTrailingSlash); // With files we can do "LIST <filename>" to avoid listing the whole dir listarg = filename; } else { // --- New implementation: // Don't list the parent dir. Too slow, might not show it, etc. // Just return that it's a dir. UDSEntry entry; entry.insert( KIO::UDSEntry::UDS_NAME, filename ); entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR ); entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ); // No clue about size, ownership, group, etc. statEntry(entry); finished(); return; // --- Old implementation:#if 0 // It's a dir, remember that // Reason: it could be a symlink to a dir, in which case ftpReadDir // in the parent dir will have no idea about that. But we know better. isDir = true; // If the dir starts with '.', we'll need '-a' to see it in the listing. if ( search[0] == '.' ) listarg = "-a"; parentDir = "..";#endif } // Now cwd the parent dir, to prepare for listing if( !ftpFolder(parentDir, true) ) return; if( !ftpOpenCommand( "list", listarg, 'I', ERR_DOES_NOT_EXIST ) ) { kError(7102) << "COULD NOT LIST"; return; } kDebug(7102) << "Starting of list was ok"; Q_ASSERT( !search.isEmpty() && search != "/" ); bool bFound = false; KUrl linkURL; FtpEntry ftpEnt; while( ftpReadDir(ftpEnt) ) { // We look for search or filename, since some servers (e.g. ftp.tuwien.ac.at) // return only the filename when doing "dir /full/path/to/file" if ( !bFound ) { if ( ( search == ftpEnt.name || filename == ftpEnt.name ) ) { if ( !filename.isEmpty() ) { bFound = true; UDSEntry entry; ftpCreateUDSEntry( filename, ftpEnt, entry, isDir ); statEntry( entry ); } }#if 0 // goes with the "old implementation" above else if ( isDir && ( ftpEnt.name == listarg || ftpEnt.name+'/' == listarg ) ) { // Damn, the dir we're trying to list is in fact a symlink // Follow it and try again if ( ftpEnt.link.isEmpty() ) kWarning(7102) << "Got " << listarg << " as answer, but empty link!"; else { linkURL = url; kDebug(7102) << "ftpEnt.link=" << ftpEnt.link; if ( ftpEnt.link[0] == '/' ) linkURL.setPath( ftpEnt.link ); // Absolute link else { // Relative link (stat will take care of cleaning ../.. etc.) linkURL.setPath( listarg ); // this is what we were listing (the link) linkURL.setPath( linkURL.directory() ); // go up one dir linkURL.addPath( ftpEnt.link ); // replace link by its destination kDebug(7102) << "linkURL now " << linkURL.prettyUrl(); } // Re-add the filename we're looking for linkURL.addPath( filename ); } bFound = true; }#endif } // kDebug(7102) << ftpEnt.name; } ftpCloseCommand(); // closes the data connection only if ( !bFound ) { ftpStatAnswerNotFound( path, filename ); return; } if ( !linkURL.isEmpty() ) { if ( linkURL == url || linkURL == tempurl ) { error( ERR_CYCLIC_LINK, linkURL.prettyUrl() ); return; } stat( linkURL ); return; } kDebug(7102) << "stat : finished successfully"; finished();}void Ftp::listDir( const KUrl &url ){ kDebug(7102) << "Ftp::listDir " << url.prettyUrl(); if( !ftpOpenConnection(loginImplicit) ) return; // No path specified ? QString path = url.path(); if ( path.isEmpty() ) { KUrl realURL; realURL.setProtocol( "ftps" ); if ( m_user != FTP_LOGIN ) realURL.setUser( m_user ); // We set the password, so that we don't ask for it if it was given if ( m_pass != FTP_PASSWD ) realURL.setPass( m_pass ); realURL.setHost( m_host ); if ( m_port > 0 && m_port != DEFAULT_FTP_PORT ) realURL.setPort( m_port ); if ( m_initialPath.isEmpty() ) m_initialPath = "/"; realURL.setPath( m_initialPath ); kDebug(7102) << "REDIRECTION to " << realURL.prettyUrl(); redirection( realURL ); finished(); return; } kDebug(7102) << "hunting for path '" << path << "'"; if (!ftpOpenDir( path ) ) { if ( ftpSize( path, 'I' ) ) // is it a file ? { error( ERR_IS_FILE, path ); return; } // not sure which to emit //error( ERR_DOES_NOT_EXIST, path ); error( ERR_CANNOT_ENTER_DIRECTORY, path ); return; } UDSEntry entry; FtpEntry ftpEnt; while( ftpReadDir(ftpEnt) ) { //kDebug(7102) << ftpEnt.name; //Q_ASSERT( !ftpEnt.name.isEmpty() ); if ( !ftpEnt.name.isEmpty() ) { //if ( S_ISDIR( (mode_t)ftpEnt.type ) ) // kDebug(7102) << "is a dir"; //if ( !ftpEnt.link.isEmpty() ) // kDebug(7102) << "is a link to " << ftpEnt.link; entry.clear(); ftpCreateUDSEntry( ftpEnt.name, ftpEnt, entry, false ); listEntry( entry, false ); } } listEntry( entry, true ); // ready ftpCloseCommand(); // closes the data connection only finished();}void Ftp::slave_status(){ kDebug(7102) << "Got slave_status host = " << (!m_host.toAscii().isEmpty() ? m_host.toAscii() : "[None]") << " [" << (m_bLoggedOn ? "Connected" : "Not connected") << "]"; slaveStatus( m_host, m_bLoggedOn );}bool Ftp::ftpOpenDir( const QString & path ){ //QString path( _url.path(KUrl::RemoveTrailingSlash) ); // We try to change to this directory first to see whether it really is a directory. // (And also to follow symlinks) QString tmp = path.isEmpty() ? QString("/") : path; // We get '550', whether it's a file or doesn't exist... if( !ftpFolder(tmp, false) ) return false; // Don't use the path in the list command: // We changed into this directory anyway - so it's enough just to send "list". // We use '-a' because the application MAY be interested in dot files. // The only way to really know would be to have a metadata flag for this... // Since some windows ftp server seems not to support the -a argument, we use a fallback here. // In fact we have to use -la otherwise -a removes the default -l (e.g. ftp.trolltech.com) if( !ftpOpenCommand( "list -la", QString(), 'I', ERR_CANNOT_ENTER_DIRECTORY ) ) { if ( !ftpOpenCommand( "list", QString(), 'I', ERR_CANNOT_ENTER_DIRECTORY ) ) { kWarning(7102) << "Can't open for listing"; return false; } } kDebug(7102) << "Starting of list was ok"; return true;}bool Ftp::ftpReadDir(FtpEntry& de){ assert(m_data != NULL); // get a line from the data connecetion ... while( true ) { while (!m_data->canReadLine() && m_data->waitForReadyRead()) {} QByteArray data = m_data->readLine(); if (data.size() == 0) break; const char* buffer = data.data(); kDebug(7102) << "dir > " << buffer; //Normally the listing looks like // -rw-r--r-- 1 dfaure dfaure 102 Nov 9 12:30 log // but on Netware servers like ftp://ci-1.ci.pwr.wroc.pl/ it looks like (#76442) // d [RWCEAFMS] Admin 512 Oct 13 2004 PSI // we should always get the following 5 fields ... const char *p_access, *p_junk, *p_owner, *p_group, *p_size; if( (p_access = strtok((char*)buffer," ")) == 0) continue; if( (p_junk = strtok(NULL," ")) == 0) continue; if( (p_owner = strtok(NULL," ")) == 0) continue; if( (p_group = strtok(NULL," ")) == 0) continue; if( (p_size = strtok(NULL," ")) == 0) continue; //kDebug(7102) << "p_access=" << p_access << " p_junk=" << p_junk << " p_owner=" << p_owner << " p_group=" << p_group << " p_size=" << p_size; de.access = 0; if ( strlen( p_access ) == 1 && p_junk[0] == '[' ) { // Netware de.access = S_IRWXU | S_IRWXG | S_IRWXO; // unknown -> give all permissions } const char *p_date_1, *p_date_2, *p_date_3, *p_name; // A special hack for "/dev". A listing may look like this: // crw-rw-rw- 1 root root 1, 5 Jun 29 1997 zero // So we just ignore the number in front of the ",". Ok, its a hack :-) if ( strchr( p_size, ',' ) != 0L ) { //kDebug(7102) << "Size contains a ',' -> reading size again (/dev hack)"; if ((p_size = strtok(NULL," ")) == 0) continue; } // Check whether the size we just read was really the size // or a month (this happens when the server lists no group) // Used to be the case on sunsite.uio.no, but not anymore // This is needed for the Netware case, too. if ( !isdigit( *p_size ) ) { p_date_1 = p_size; p_size = p_group; p_group = 0; //kDebug(7102) << "Size didn't have a digit -> size=" << p_size << " date_1=" << p_date_1; } else { p_date_1 = strtok(NULL," "); //kDebug(7102) << "Size has a digit -> ok. p_date_1=" << p_date_1; } if ( p_date_1 != 0 && (p_date_2 = strtok(NULL," ")) != 0 && (p_date_3 = strtok(NULL," ")) != 0 && (p_name = strtok(NULL,"\r\n")) != 0 ) { { QByteArray tmp( p_name ); if ( p_access[0] == 'l' ) { int i = tmp.lastIndexOf( " -> " ); if ( i != -1 ) { de.link = remoteEncoding()->decode(p_name + i + 4); tmp.truncate( i ); } else de.link.clear(); } else de.link.clear(); if ( tmp[0] == '/' ) // listing on ftp://ftp.gnupg.org/ starts with '/' tmp.remove( 0, 1 ); if (tmp.indexOf('/') != -1) continue; // Don't trick us! // Some sites put more than one space between the date and the name // e.g. ftp://ftp.uni-marburg.de/mirror/ de.name = remoteEncoding()->decode(tmp.trimmed()); } de.type = S_IFREG; switch ( p_access[0] ) { case 'd': de.type = S_IFDIR; break; case 's': de.type = S_IFSOCK; break; case 'b': de.type = S_IFBLK; break; case 'c': de.type = S_IFCHR; break; case 'l': de.type = S_IFREG; // we don't set S_IFLNK here. de.link says it. break; default: break; } if ( p_access[1] == 'r' ) de.access |= S_IRUSR; if ( p_access[2] == 'w' ) de.access |= S_IWUSR; if ( p_access[3] == 'x' || p_access[3] == 's' ) de.access |= S_IXUSR; if ( p_access[4] == 'r' ) de.access |= S_IRGRP; if ( p_access[5] == 'w' ) de.access |= S_IWGRP; if ( p_access[6] == 'x' || p_access[6] == 's' ) de.access |= S_IXGRP; if ( p_access[7] == 'r' ) de.access |= S_IROTH; if ( p_access[8] == 'w' ) de.access |= S_IWOTH; if ( p_access[9] == 'x' || p_access[9] == 't' ) de.access |= S_IXOTH; if ( p_access[3] == 's' || p_access[3] == 'S' ) de.access |= S_ISUID; if ( p_access[6] == 's' || p_access[6] == 'S' ) de.access |= S_ISGID; if ( p_access[9] == 't' || p_access[9] == 'T' ) de.access |= S_ISVTX; de.owner = remoteEncoding()->decode(p_owner); de.group = remoteEncoding()->decode(p_group); de.size = charToLongLong(p_size); // Parsing the date is somewhat tricky // Examples : "Oct 6 22:49", "May 13 1999" // First get current time - we need the current month and year time_t currentTime = time( 0L ); struct tm * tmptr = gmtime( ¤tTime ); int currentMonth = tmptr->tm_mon; //kDebug(7102) << "Current time :" << asctime( tmptr ); // Reset time fields tmptr->tm_sec = 0; tmptr->tm_min = 0; tmptr->tm_hour = 0; // Get day number (always second field) tmptr->tm_mday = atoi( p_date_2 ); // Get month from first field // NOTE : no, we don't want to use KLocale here // It seems all FTP servers use the English way //kDebug(7102) << "Looking for month " << p_date_1; static const char * s_months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; for ( int c = 0 ; c < 12 ; c ++ ) if ( !strcmp( p_date_1, s_months[c]) ) { //kDebug(7102) << "Found month " << c << " for " << p_date_1; tmptr->tm_mon = c; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -