LibCurl SFTP Rename file - libcurl

SOLVED ...
After more shifting around, by using the "rename" as quoted command, it required the full path inclusive of the original name and full path inclusive of rename-to destination. I didn't try with the path in my other frustrated attempts.
Hi all, I've tried all different combinations, keep failing and don't know what I'm missing. I'm trying to do a simple file rename on a SFTP site using Libcurl. I've looked all over for answers, but keep coming up short.
BTW... For this testing, there is only one file name in the folder, so no issue about existing file overwrites... And I'm the login user who originally uploaded the file for any possible "permissions" issues.
My first test was to get the what would be expected as simple syntax correct, but resulted otherwise. I started with a NON-SFTP site first... just regular FTP.
// ex: fpt://mysite.com/subpathNeeded/
curl_easy_setopt(MyCurl, CURLOPT_URL, RemotePath );
// need a "QUOTE" command before rename will occur in postQuote
strcpy_s( NewCmd, _countof(NewCmd), "PWD \0" );
quotelist = curl_slist_append(quotelist, NewCmd );
// NOW, we can issue the rename from and rename to commands
strcpy_s( RenameFrom, _countof(RenameFrom), "RNFR " );
strcat_s( RenameFrom, _countof(RenameFrom), RemoteCurrentFileName );
postquotelist = curl_slist_append( postquotelist, RenameFrom );
strcpy_s( RenameTo, _countof(RenameTo), "RNTO " );
strcat_s( RenameTo, _countof(RenameTo), RemoteRenameToName );
postquotelist = curl_slist_append( postquotelist, RenameTo );
curl_easy_setopt(MyCurl, CURLOPT_QUOTE, quotelist );
curl_easy_setopt(MyCurl, CURLOPT_POSTQUOTE, postquotelist );
// NOW, perform the print working directory, then rename...
MyCurlResult = curl_easy_perform(MyCurl);
This works no problem. So now, I switch over to SFTP and it fails... By researching, SFTP doesn't like "PWD", but does allow "pwd" (case issue), no problem. Then, it doesn't like the RNFR and RNTO but does accept "mv" (move). So, if I'm in SFTP mode, I change to
// lower case "pwd" print working directory
strcpy_s( NewCmd, _countof(NewCmd), "pwd\0" );
quotelist = curl_slist_append(quotelist, NewCmd );
// "mv" = move "originalfile" "newfile"
strcpy_s( RenameFrom, _countof(RenameFrom), "mv \"\0" );
strcat_s( RenameFrom, _countof(RenameFrom), RemoteCurrentFileName );
strcat_s( RenameFrom, _countof(RenameFrom), "\" \"\0" );
strcat_s( RenameFrom, _countof(RenameFrom), RemoteRenameToName );
strcat_s( RenameFrom, _countof(RenameFrom), "\"\0" );
postquotelist = curl_slist_append( postquotelist, RenameFrom );
I then get a CURL ERROR when I perform this...
However, if I don't try the "mv" command and only send the QUOTE command of "pwd", it goes through fine and it does properly list the expected folder/subdirectory I'm trying to rename the file in. So I know its in the proper directory. The values I have in the "RemoteCurrentFileName" and "RemoteRenameToName" are just the stems of the file, no full path to them respectively. I've also tried including the full path as well and both versions fail. Ex:
RemoteCurrentFileName = "FileIWantToRename.txt"
or
RemoteCurrentFileName = "/subpathNeeded/FileIWantToRename.txt"
Additionally, I've looked at the documentation and noted "rename" is a valid "QUOTE" command and tried that too..
rename "original file" "new file"
and that failed too
This should not be this difficult to do. What simple/stupid am I missing... This is totally frustrating.

SOLVED ...
After more shifting around, by using the "rename" as quoted command, it required the full path inclusive of the original name and full path inclusive of rename-to destination. I didn't try with the path in my other frustrated attempts
strcpy_s( RenameFrom, _countof(RenameFrom), "rename \"\0" );
strcat_s( RenameFrom, _countof(RenameFrom), RemoteCurrentFileNameWithFullPath );
strcat_s( RenameFrom, _countof(RenameFrom), "\" \"\0" );
strcat_s( RenameFrom, _countof(RenameFrom), RemoteRenameToNameWithFullPath );
strcat_s( RenameFrom, _countof(RenameFrom), "\"\0" );
quotelist = curl_slist_append( quotelist, RenameFrom );
MyCurlResult = curl_easy_perform(MyCurl);

Related

Error opening physical file with _Ropen

I'm trying to put physical file on IFS.
So trying to open file just to guarantee its presence on source.
This is how I do it:
errno=0;
if ( ((pf = _Ropen(pfname, "rr, nullcap=Y")) == NULL) || (errno!=0) )
{
printf("\nError: Cannot open file %s\n",pfname);
//...
}
However, the file not gets opened with pf = SPP:*NULL result
That won't be the issue, but I can't also view errno.
The eval errno gives Syntax error occurred. without any clue what has happened.
I'm still able to view contents of pfname: it looks like 'MYLIB/MYFILE'
Absolute path was also tried:
'/QSYS.LIB/MYLIB.LIB/MYFILE.FILE', without any difference - same error persists.
IBM IFS explorer clearly shows contents of MYLIB and there is a MYFILE inside this lib.
UPD
I've added some debug logging just to extcact error description or error code:
numbytes = sprintf( (char *)NULL, "%s", strerror(errno) );
ret = (char *)malloc( ( numbytes + 1 ) * sizeof( char ) );
sprintf( ret, "%s", strerror(errno) );
And the result of ret is SPP:*NULL.
Any ideas to try?
It turned out to be super simple - null byte was missing so _Ropen can not really access the file thus no error to be passed as well.
To handle this the argument of null-terminated byte array needs to be passed from caller.
In case of rpg solution looks like 'MYFILE/MYLIB' + X'00'

Running ssh in c++ with popen function or using non-compiled library

I want to run some script on remote device using ssh, but I don't want to use a library. I want to run popen function and run ssh.
FILE *f = popen( "ssh -t -t root#192.168.1.2", "r" );
if(f)
{
fprintf(f, "/opt/somescript.sh\n");
pclose(f);
}
I can see that the connection established and closed on the terminal, when I run this code. But the script doesn't really run on the remote machine.
Don't want to use a library means, compiled library. Because this code will run different platforms and I cannot compile the library for every platform. If I can use pure c++ code, it will be OK. I downloaded libssh source codes but it's so compicated and I didn't understand how to use it without compiling.
Your input and output are wrong. You open the ssh with "r", which means you're interested in the script output, but then you try to fprintf into it, which makes no sense.
Try to replace the popen with:
FILE *f = popen( "ssh -t -t root#192.168.1.2 /opt/somescript.sh", "r" );
what is the output of this code?
#include <stdio.h>
int main() {
FILE * fp = popen( "ssh -t -t root#192.168.1.2", "r" );
if ( !fp ) {
fprintf( stderr, "Could not execute command \n" );
return 1;
}
const int BUFSIZE = 1000;
char buff[ BUFSIZE ];
while( fgets( buf, sizeof( buff ), fp ) ) {
fprintf( stdout, "%s", buff );
}
pclose( fp );
fp=NULL;
return 0;
}

Move files to Trash/Recycle Bin in Qt

Is there a Qt function to move files to Recycle Bin instead of truly deleting them, for OSes that support it, or do I need to use OS-specific code?
Since Qt 5.15.0 Alpha, this method has been added, which should be what you were looking for.
bool QFile::moveToTrash()
The corresponding code changes can be found here.
(This issue is old and the corresponding Bugreport at https://bugreports.qt.io/browse/QTBUG-47703 has already been posted, but I currently lack the reputation to comment, and found this to be a useful information.)
Qt doesnt provide a MoveToTrash. Here's a part of my code
for Windows
#ifdef Q_OS_WIN32
#include "windows.h"
void MoveToTrashImpl( QString file ){
QFileInfo fileinfo( file );
if( !fileinfo.exists() )
throw OdtCore::Exception( "File doesnt exists, cant move to trash" );
WCHAR from[ MAX_PATH ];
memset( from, 0, sizeof( from ));
int l = fileinfo.absoluteFilePath().toWCharArray( from );
Q_ASSERT( 0 <= l && l < MAX_PATH );
from[ l ] = '\0';
SHFILEOPSTRUCT fileop;
memset( &fileop, 0, sizeof( fileop ) );
fileop.wFunc = FO_DELETE;
fileop.pFrom = from;
fileop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
int rv = SHFileOperation( &fileop );
if( 0 != rv ){
qDebug() << rv << QString::number( rv ).toInt( 0, 8 );
throw OdtCore::Exception( "move to trash failed" );
}
}
#endif
and for Linux
#ifdef Q_OS_LINUX
bool TrashInitialized = false;
QString TrashPath;
QString TrashPathInfo;
QString TrashPathFiles;
void MoveToTrashImpl( QString file ){
#ifdef QT_GUI_LIB
if( !TrashInitialized ){
QStringList paths;
const char* xdg_data_home = getenv( "XDG_DATA_HOME" );
if( xdg_data_home ){
qDebug() << "XDG_DATA_HOME not yet tested";
QString xdgTrash( xdg_data_home );
paths.append( xdgTrash + "/Trash" );
}
QString home = QStandardPaths::writableLocation( QStandardPaths::HomeLocation );
paths.append( home + "/.local/share/Trash" );
paths.append( home + "/.trash" );
foreach( QString path, paths ){
if( TrashPath.isEmpty() ){
QDir dir( path );
if( dir.exists() ){
TrashPath = path;
}
}
}
if( TrashPath.isEmpty() )
throw Exception( "Cant detect trash folder" );
TrashPathInfo = TrashPath + "/info";
TrashPathFiles = TrashPath + "/files";
if( !QDir( TrashPathInfo ).exists() || !QDir( TrashPathFiles ).exists() )
throw Exception( "Trash doesnt looks like FreeDesktop.org Trash specification" );
TrashInitialized = true;
}
QFileInfo original( file );
if( !original.exists() )
throw Exception( "File doesnt exists, cant move to trash" );
QString info;
info += "[Trash Info]\nPath=";
info += original.absoluteFilePath();
info += "\nDeletionDate=";
info += QDateTime::currentDateTime().toString("yyyy-MM-ddThh:mm:ss.zzzZ");
info += "\n";
QString trashname = original.fileName();
QString infopath = TrashPathInfo + "/" + trashname + ".trashinfo";
QString filepath = TrashPathFiles + "/" + trashname;
int nr = 1;
while( QFileInfo( infopath ).exists() || QFileInfo( filepath ).exists() ){
nr++;
trashname = original.baseName() + "." + QString::number( nr );
if( !original.completeSuffix().isEmpty() ){
trashname += QString( "." ) + original.completeSuffix();
}
infopath = TrashPathInfo + "/" + trashname + ".trashinfo";
filepath = TrashPathFiles + "/" + trashname;
}
QDir dir;
if( !dir.rename( original.absoluteFilePath(), filepath ) ){
throw Exception( "move to trash failed" );
}
File infofile;
infofile.createUtf8( infopath, info );
#else
Q_UNUSED( file );
throw Exception( "Trash in server-mode not supported" );
#endif
}
#endif
There is no API yet.
https://bugreports.qt.io/browse/QTBUG-181
The issue is closed and the fix version is: Some future release
Edit: A new issue has been opened at https://bugreports.qt.io/browse/QTBUG-47703.
Edit Apparently it is now done in 5.15.0 Alpha bool QFile::moveToTrash()
I think that there is no cross-platform way. Simple moving files to "trash" location will not give effect, because user may switch off this possibility.
Maybe, this url will help: http://www.hardcoded.net/articles/send-files-to-trash-on-all-platforms.htm
I'm relatively certain that there is no Qt API that wraps this for all supported platforms. That means, unfortunately, that you will have to write platform-specific code.
I don't know anything about where/how Linux distributions store deleted files, and I imagine that it probably varies depending on which file manager you're using. I believe that moving files to a ~/.Trash folder is the standard way of doing it, but I'm not sure if this is reliable. For example, in the case of files stored on external volumes.
Things are a bit easier on Mac OS X, where there is a supported API to do this: FSMoveObjectToTrashSync, provided by Core Services. At least, that's how I remember you're supposed to do it. The documentation claims that this method is now deprecated in OS X 10.8. I have no idea what the recommended alternative is.
As a Windows programmer, I think that platform is much easier. :-) The basic solution is to call the SHFileOperation function:
#include <Windows.h> // general Windows header file
#include <ShellAPI.h> // for shell functions, like SHFileOperation
#include <string> // (or use QString)
void RecycleFileOnWindows()
{
std::wstring path = L"C:\\Users\\Administrator\\Documents\\deleteme.txt";
path.append(1, L'\0'); // path string must be double nul-terminated
SHFILEOPSTRUCT shfos = {};
shfos.hwnd = nullptr; // handle to window that will own generated windows, if applicable
shfos.wFunc = FO_DELETE;
shfos.pFrom = path.c_str();
shfos.pTo = nullptr; // not used for deletion operations
shfos.fFlags = FOF_ALLOWUNDO; // use the recycle bin
const int retVal = SHFileOperation(&shfos);
if (retVal != 0)
{
// The operation failed...
if (shfos.fAnyOperationsAborted)
{
// ...but that's because the user canceled.
MessageBox(nullptr, L"Operation was canceled", nullptr, MB_OK | MB_ICONINFORMATION);
}
else
{
// ...for one of the other reasons given in the documentation.
MessageBox(nullptr, L"Operation failed", nullptr, MB_OK | MB_ICONERROR);
}
}
}
There are also flags that you can set to customize confirmation, error reporting, and other behavior. The linked documentation contains all the details you need to build upon this basic example.
On Windows Vista and later, the SHFileOperation function has been superseded by the methods provided by the IFileOperation interface. If you're targeting only these later versions of Windows, you should prefer to use this interface. Otherwise, SHFileOperation will continue to work fine.
if(QSysInfo::kernelType()=="linux")
{
QDateTime currentTime(QDateTime::currentDateTime()); // save System time
QString trashFilePath=QDir::homePath()+"/.local/share/Trash/files/"; // trash file path contain delete files
QString trashInfoPath=QDir::homePath()+"/.local/share/Trash/info/"; // trash info path contain delete files information
// create file format for trash info file----- START
QFile infoFile(trashInfoPath+FileName.completeBaseName()+"."+FileName.completeSuffix()+".trashinfo"); //filename+extension+.trashinfo // create file information file in /.local/share/Trash/info/ folder
infoFile.open(QIODevice::ReadWrite);
QTextStream stream(&infoFile); // for write data on open file
stream<<"[Trash Info]"<<endl;
stream<<"Path="+QString(QUrl::toPercentEncoding(FileName.absoluteFilePath(),"~_-./"))<<endl; // convert path string in percentage decoding scheme string
stream<<"DeletionDate="+currentTime.toString("yyyy-MM-dd")+"T"+currentTime.toString("hh:mm:ss")<<endl; // get date and time format YYYY-MM-DDThh:mm:ss
infoFile.close();
// create info file format of trash file----- END
QDir file;
file.rename(FileName.absoluteFilePath(),trashFilePath+FileName.completeBaseName()+"."+FileName.completeSuffix()); // rename(file old path, file trash path)
}
Trash files in linux exist /home/user_name/.local/share/Trash/files/ directory but it also require info file for each trash file which exist in /home/user_name/.local/share/Trash/info/ directory. when we want to move file into trash, actually move file into /home/user_name/.local/share/Trash/files/ directory and create info file in /home/user_name/.local/share/Trash/info/ directory. inside .trashinfo format use percentage decoding scheme for set file path where file existed, info file also contain time and date of deletion.

load xml values and pass it into string in vc++

Am working in vc++ and trying to load an xml file and load the entire data into a string but am not getting the results
char text[700] = {""};
TiXmlDocument doc( "'demotest.xml" );
bool loadOkay = doc.LoadFile();
if ( !loadOkay )
{
printf( "Could not load test file 'demotest.xml'. Error='%s'. Exiting.\n", doc.ErrorDesc() );
system("PAUSE");
exit( 1 );
}
printf( "** Demo doc read from disk: ** \n\n" );
printf( "** Printing via doc.Print **\n" );
//doc.Print( stdout );
{
printf( "** Printing via TiXmlPrinter **\n" );
TiXmlPrinter printer;
doc.Accept( &printer );
fprintf( stdout, "%s", printer.CStr() );
//upto this line its working fine in console. but when I convert this string am getting struck
wsprintf(text, "%s", (char*)printer.CStr());
AddLOG_message(text, 0, true);
}
Last two lines I should get the entire content of the xml including header, elements and values.
Please help.
I'd do it like this, with less C code, more C++ code and deprecating the risky char array of length magic number 700:
TiXmlPrinter printer;
doc.Accept( &printer );
doc.Print(); // simpler for stdout output
std::string text = printer.CStr(); // easier, safer this way
AddLOG_message( text.c_str(), 0, true );

Starting a program fails with error code 1

I made an application and a dll, which are working this way:
I have to register the dll. After registering the dll if i right click on an .exe file, the pop-up menu appears, and i have inserted into this menu one line ("Start MyApp"), and if i click there, it should start MyApp. MyApp has one parameter which is the full path of the selected .exe file. After starting MyApp with this path it should create a process with CreateProcessWithLogonW(). This application reads the username, password and the domain from an .ini file. My problem is, that after MyApp starts, it fails always, because it can't find the ini file. Errorcode is: 1 (Incorrect function).
If i start MyApp manually, than it works fine.
Does anyone has any idea why is this, and how could i fix this problem?
Thanks in advance!
kampi
Update1:
Here is the code which reads from the ini file.
int main ( int argc, char *argv[] )
{
int i, slash = 0, j;
char application[size];
wchar_t wuser[65], wdomain[33], wpass[129];
memset( user, 0, sizeof ( user ) );
memset( password, 0, sizeof ( password ) );
memset( domain, 0, sizeof ( domain ) );
file_exists( "RunAs.ini" );
readfile( "RunAs.ini" );
....
....
....
}
void file_exists( const char * filename )
{
if (FILE * file = fopen(filename, "r"))
{
fclose(file);
}
else
{
printf("\nCan't find %s!\n",filename);
getch();
exit(1);
}
}//file_exists
void readfile( char * filename )
{
FILE *inifile;
char tmp[256], buf[256], what[128];
int i, j;
inifile = fopen( "RunAs.ini", "r" );
while ( fgets(tmp, sizeof tmp, inifile) != NULL )
{
if ( tmp[ strlen(tmp) - 1 ] == '\n' )
{
tmp[ strlen(tmp) - 1 ] = '\0';
}//if
memset ( buf, 0, sizeof( buf ) );
for ( i = 0; tmp[i]!= '='; i++ )
{
buf[i] = tmp[i];
}
buf[i] = '\0';
i++;
// memset ( what, 0, sizeof( what ) );
SecureZeroMemory( what, sizeof(what) * 128 );
for ( j = 0; i != strlen(tmp); i++ )
{
what[j] = tmp[i];
j++;
}
what[j] = '\0';
upcase( buf );
removespace( what );
if ( strcmp( buf, "USERNAME" ) == 0 )
{
strcpy( user, what );
}
if ( strcmp( buf, "PASSWORD" ) == 0 )
{
strcpy( password, what );
}
if ( strcmp( buf, "DOMAIN" ) == 0 )
{
strcpy( domain, what );
}
}//while
fclose (inifile);
}//readfile
As others have said, your problem is here:
file_exists( "RunAs.ini" );
readfile( "RunAs.ini" );
Neither of the function calls provides a path. You're expecting the current working directory to be the folder where your application is located, but it doesn't have to be (in fact, you should never assume that it is). The context menu isn't setting the working directory first.
Your safest bet is to retrieve the path to your folder using the path provided in argv[] (the 0th element is the fully qualified path and name of the application itself, and you can extract the path from that). You'll then have exact knowledge of where the file is located, and can append the name of the ini file to that path.
I suspect you're looking for the ini file in the wrong folder. I would try changing the ini file name in the application to the fully qualified name of the ini file. (i.e from "foo.ini" to "c:\\temp\\foo.ini")
(Please note that I've doubled the backslashes because without this, the single backslash may change the meaning of the next character or the backslash may be ignored.)
Are you providing an absolute path or a relative path? Your CWD may be different on startup.
When starting your application directly, the current path is the path that your application is installed to.
However, when starting it from that context menu, the current path is something else.
There are two ways to resolve this. First, don't use an ini file. Instead, store your information to the registry. That way you don't care where the program is started from.
Alternatively, your app will have to locate the directory where it was actually installed, then load the ini file from there.
Obviously the first choice is the easiest path.
Have you checked whether file path for the ini is valid ?