📜 ⬆️ ⬇️

Two-wheeled automation of uploading files to the server from Notepad ++

It just so happened that by work, I have to edit the files to which I have access only through the CMS Bitrix file manager, which entails opening multiple tabs in the browser and a huge amount of unnecessary gestures necessary only to edit a few files.
Below I will tell how I solved this problem with the help of Node.js and free time.

The first thing I did was find out how the bitrix approaches editing files in general. Everything turned out to be quite simple and did not exceed my expectations at all - the text of the file is sent to the server by the form along with the file name, where the php script opens the specified file and writes the contents to it.
The most logical thing that occurred to me was to imitate sending the form to the same handler.
To do this, you need to do 3 things:

Anyone who knows at least a little about the http protocol knows that the User-Agent string in the request header is responsible for determining the client program.
It is here that you should indicate the signature of the program under which we mask. I have this:

Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0

Slightly more complicated is the situation with impersonation of the user. By the method of typing, or rather the successive removal of cookies, it was found that in order for the site to consider the user logged in - he only needed his username and current phpsessid . I already know the login, but you can find out the session number in several ways.
First, you can simply view it using the “Developer Tools” available in any browser.
Secondly, since we are sending the form, you can first send the login form to the site so that the session number comes in the form of a cookie.
Considering that in any case file uploading is connected with the entrance to the site, I decided to use the first method, however, instead of opening FireBug every time, I simply added phpsessid output to one of the site pages.
The last and most important thing that we need to do is send the data to the server as if from a form.
Here, too, there is nothing difficult. In the same request header, specify the Content-Type, which determines the type of content attached to the header, and add the Content-Length, which is necessary for the server to know how much content to accept.
After that, in the body of the request, we send the serialized data, which is the text of the file, its name on the server, and some other variables necessary for the form handler.
Now that the “calculations” are over, we can proceed to practice.
To begin with, we will determine what information is generally needed to successfully upload files to the server. In addition to the specified login and session number, the path to the files on the local machine and the path on the server where these files will be saved are required.
The first was decided to be passed to the script as an argument, and all other information stored in a file in the folder that is passed to the script.
In nodejs, the command line arguments are stored in the argv field of the global process variable, and even if no arguments are passed to the program, there are 2 lines in the process.argv array - “node” and the name of the program being executed, so the first argument is at offset 2. First, we check to see if at least some argument is passed to our script, and if it does not suddenly appear, we exit.

 if(process.argv.length<3){ console.log("  !"); process.exit(1); } 

')
Having received the path to the local folder, open the file config.json that should be there.
The fs (FileSystem) module is responsible for working with files in nodejs. Modules in nodejs are essentially just sets of functions, but we all know that functions in javascript are not exactly like in other languages.
We also need quite an ordinary function fs.readFile (path, callback) which, as you yourself can guess, reads the contents of the file. If everything is clear with the first of its arguments, then the second is a function that will be executed when the file is fully read. Moreover, the program will not stop waiting for this moment, but will continue its execution. Such an approach to program organization is called Event-Oriented Programming. So - read the settings file in the specified folder. Its contents will be passed to the callback function as the second argument. The first is an error or false if everything is fine.

 var fs = require('fs'); // ,     fs fs.readFile(process.argv[2]+'\config.json', function (err, data) { if (err) throw err; c = JSON.parse(data);//    json       //...-     }); 


Next, you need to get a list of files in the folder and upload each file to the server, unless this file is a configuration file or another file to be ignored.

 //...   fs.readdir(process.argv[2],function(err,files){ //      if (err) throw err; files.forEach(function(el,index,array){ if(el.split('.').length>1) if(el!='config.json' && el!='.git' && el!='.gitattributes' && el!='.gitignore') //    upload(el); //     }); }); 


Having received the name of the file, we can read its contents and send it to the server. That is what we will do. In order to send an http request to nodejs there is an http module. Having connected it, we can call the http.request function (method, path, port, hostname, headers, callback) , where the penultimate argument literally makes us omnipotent, because it allows you to send a request with any header, exactly here we specify the User-Agent, the cookie and the content type of the request. By making a request, we get it into a variable, through which we can influence its behavior. For example, the write () and end () methods. Send data in the request body with the only difference that write can be called several times in a row, and end - only once. immediately after calling it, the completion of the request.
As mentioned earlier, the data must be sent in serialized form, namely, as we would have seen in the address bar. In nodejs there is a module for working with such data, and it is called querystring. It allows you to save an object in a string and vice versa. All form data is stored in the global query variable, which we serialize for later submission.

 var http = require('http'); var querystring = require('querystring'); function upload(file){ console.log("  %s:",file); var fileData=fs.readFileSync(process.argv[2]+'\\'+file); //    query.path=c.path+file;//       query.filesrc=fileData.toString(); var str=querystring.stringify(query);//    var len=str.length; var request = http.request( //   { method:'post', path:'/bitrix/admin/fileman_file_edit.php', port:80, hostname:'***.****.ru', headers:{ 'host':'***.****.ru', 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0', 'Cookie':'PHPSESSID='+c.phpsessid+';BITRIX_SM_LOGIN='+c.login, 'Content-Type':'application/x-www-form-urlencoded', 'Content-Length':len } },rListener); //         request.end(str); //    ( )    } 


Well, in order to know whether our file has been downloaded or if our works were in vain, we accept the response to the request. The callback that is called when the answer is received, the first argument takes all the response information, including the status. By the same method of typing, it was found that when the file was successfully downloaded, the redirect returned in return, in the remaining cases the page came back.

 function rListener(response){ var status=''; switch(response.statusCode){ case 302: status=' ';break; //    -   case 200: status='  ';break; default : status=''; } console.log('%s (%d).',status,response.statusCode); } 


The full script code is here .
Having such a bike, it’s pretty easy to set up a script call by pressing a key in your favorite editor. I have this Notepad ++ with the NppExec plugin.

Source: https://habr.com/ru/post/190272/


All Articles