Trying to communicate with a REST service with NSIS - web-services

All I am trying to do is send an HTTP GET with Querystring or HTTP POST to a web service from within a custom page in an NSIS installer.
The problem is that most plugins can upload/download files, but not simply call a service and return the response. The recommend plugin to use is NsisUrlLib. I tried this, and while it works with simple GET requests, it fails and throws an error if you have querystring parameters in the URL, such as:
NsisUrlLib::UrlOpen /NOUNLOAD "http://tc.hwidev.com?var=value"
Pop $connectResult
NSISdl,INetC, and other plugins work with querystrings but can only be used to upload/download files.
I tried using the ExecDos plugin to call cURL.exe from the command line like so:
ExecDos::exec /TOSTACK "curl" "http://tc.hwidev.com"
Pop $connectResult
But I can't figure out how to get the data returned from the URL passed back to NSIS. The above code just returns an integer into $connectResult which probably represents the return code from CURL. How do I get the actual HTML returned from the URL onto the NSIS stack or into a variable?
Alternatively, can anyone recommend a better way to accomplish this? Calling a webservice seems so basic that thousands of people should have already figured this out.

NSISdl, Inetc etc, aren't just for uploading/downloading files. I think you are misunderstanding this point. You can use them to call REST apis, but you'll need to save the answer to a local file, and then parse it.
Example: Calling to an REST api that answers XML:
# Call to REST API
NSISdl::download_quiet "http://remotesite?restparams" "$TEMP/result.xml"
# Parse answer
nsisXML::create
nsisXML::load "$TEMP/result.xml"
nsisXML::select "/response/status"
# Clean
delete "$TEMP/result.xml"
That's it.

NSISdl::download_quiet "http://127.0.0.1/vid.php?vid=1" "$TEMP/checkvid.txt"
FileOpen $0 $TEMP\checkvid.txt r
FileRead $0 $1
DetailPrint $1
FileClose $0
${If} $1 == "vidNTYE1203"
MessageBox MB_OK "video found"
Delete "$TEMP\checkvid.txt"
${Else}
MessageBox MB_OK "bye bye"
Delete "$TEMP\checkvid.txt"
Quit
${EndIf}
This is my work snippet for production.Working good,for me.Pay attention for 'Delete' command and '\' slash .

I would recommend INetC over NsisUrlLib, the older firefox installer used it with much success.

Related

Open a c++ application installed on computer with a custom url in browser [duplicate]

How do i set up a custom protocol handler in chrome? Something like:
myprotocol://testfile
I would need this to send a request to http://example.com?query=testfile, then send the httpresponse to my extension.
The following method registers an application to a URI Scheme. So, you can use mycustproto: in your HTML code to trigger a local application. It works on a Google Chrome Version 51.0.2704.79 m (64-bit).
I mainly used this method for printing document silently without the print dialog popping up. The result is pretty good and is a seamless solution to integrate the external application with the browser.
HTML code (simple):
Click Me
HTML code (alternative):
<input id="DealerName" />
<button id="PrintBtn"></button>
$('#PrintBtn').on('click', function(event){
event.preventDefault();
window.location.href = 'mycustproto:dealer ' + $('#DealerName').val();
});
URI Scheme will look like this:
You can create the URI Scheme manually in registry, or run the "mycustproto.reg" file (see below).
HKEY_CURRENT_USER\Software\Classes
mycustproto
(Default) = "URL:MyCustProto Protocol"
URL Protocol = ""
DefaultIcon
(Default) = "myprogram.exe,1"
shell
open
command
(Default) = "C:\Program Files\MyProgram\myprogram.exe" "%1"
mycustproto.reg example:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\mycustproto]
"URL Protocol"="\"\""
#="\"URL:MyCustProto Protocol\""
[HKEY_CURRENT_USER\Software\Classes\mycustproto\DefaultIcon]
#="\"mycustproto.exe,1\""
[HKEY_CURRENT_USER\Software\Classes\mycustproto\shell]
[HKEY_CURRENT_USER\Software\Classes\mycustproto\shell\open]
[HKEY_CURRENT_USER\Software\Classes\mycustproto\shell\open\command]
#="\"C:\\Program Files\\MyProgram\\myprogram.exe\" \"%1\""
C# console application - myprogram.exe:
using System;
using System.Collections.Generic;
using System.Text;
namespace myprogram
{
class Program
{
static string ProcessInput(string s)
{
// TODO Verify and validate the input
// string as appropriate for your application.
return s;
}
static void Main(string[] args)
{
Console.WriteLine("Raw command-line: \n\t" + Environment.CommandLine);
Console.WriteLine("\n\nArguments:\n");
foreach (string s in args)
{
Console.WriteLine("\t" + ProcessInput(s));
}
Console.WriteLine("\nPress any key to continue...");
Console.ReadKey();
}
}
}
Try to run the program first to make sure the program has been placed in the correct path:
cmd> "C:\Program Files\MyProgram\myprogram.exe" "mycustproto:Hello World"
Click the link on your HTML page:
You will see a warning window popup for the first time.
To reset the external protocol handler setting in Chrome:
If you have ever accepted the custom protocol in Chrome and would like to reset the setting, do this (currently, there is no UI in Chrome to change the setting):
Edit "Local State" this file under this path:
C:\Users\Username\AppData\Local\Google\Chrome\User Data\
or Simply go to:
%USERPROFILE%\AppData\Local\Google\Chrome\User Data\
Then, search for this string: protocol_handler
You will see the custom protocol from there.
Note: Please close your Google Chrome before editing the file. Otherwise, the change you have made will be overwritten by Chrome.
Reference:
https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx
Chrome 13 now supports the navigator.registerProtocolHandler API. For example,
navigator.registerProtocolHandler(
'web+custom', 'http://example.com/rph?q=%s', 'My App');
Note that your protocol name has to start with web+, with a few exceptions for common ones (like mailto, etc). For more details, see: http://updates.html5rocks.com/2011/06/Registering-a-custom-protocol-handler
This question is old now, but there's been a recent update to Chrome (at least where packaged apps are concerned)...
http://developer.chrome.com/apps/manifest/url_handlers
and
https://github.com/GoogleChrome/chrome-extensions-samples/blob/e716678b67fd30a5876a552b9665e9f847d6d84b/apps/samples/url-handler/README.md
It allows you to register a handler for a URL (as long as you own it). Sadly no myprotocol:// but at least you can do http://myprotocol.mysite.com and can create a webpage there that points people to the app in the app store.
This is how I did it. Your app would need to install a few reg keys on installation, then in any browser you can just link to foo:\anythingHere.txt and it will open your app and pass it that value.
This is not my code, just something I found on the web when searching the same question. Just change all "foo" in the text below to the protocol name you want and change the path to your exe as well.
(put this in to a text file as save as foo.reg on your desktop, then double click it to install the keys)
-----Below this line goes into the .reg file (NOT including this line)------
REGEDIT4
[HKEY_CLASSES_ROOT\foo]
#="URL:foo Protocol"
"URL Protocol"=""
[HKEY_CLASSES_ROOT\foo\shell]
[HKEY_CLASSES_ROOT\foo\shell\open]
[HKEY_CLASSES_ROOT\foo\shell\open\command]
#="\"C:\\Program Files (x86)\\Notepad++\\notepad++.exe\" \"%1\""
Not sure whether this is the right place for my answer, but as I found very few helpful threads and this was one of them, I am posting my solution here.
Problem: I wanted Linux Mint 19.2 Cinnamon to open Evolution when clicking on mailto links in Chromium. Gmail was registered as default handler in chrome://settings/handlers and I could not choose any other handler.
Solution:
Use the xdg-settings in the console
xdg-settings set default-url-scheme-handler mailto org.gnome.Evolution.desktop
Solution was found here https://alt.os.linux.ubuntu.narkive.com/U3Gy7inF/kubuntu-mailto-links-in-chrome-doesn-t-open-evolution and adapted for my case.
I've found the solution by Jun Hsieh and MuffinMan generally works when it comes to clicking links on pages in Chrome or pasting into the URL bar, but it doesn't seem to work in a specific case of passing the string on the command line.
For example, both of the following commands open a blank Chrome window which then does nothing.
"c:\Program Files (x86)\Google\Chrome\Application\chrome.exe" "foo://C:/test.txt"
"c:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --new-window "foo://C:/test.txt"
For comparison, feeding Chrome an http or https URL with either of these commands causes the web page to be opened.
This became apparent because one of our customers reported that clicking links for our product from a PDF being displayed within Adobe Reader fails to invoke our product when Chrome is the default browser. (It works fine with MSIE and Firefox as default, but not when either Chrome or Edge are default.)
I'm guessing that instead of just telling Windows to invoke the URL and letting Windows figure things out, the Adobe product is finding the default browser, which is Chrome in this case, and then passing the URL on the command line.
I'd be interested if anyone knows of Chrome security or other settings which might be relevant here so that Chrome will fully handle a protocol handler, even if it's provided via the command line. I've been looking but so far haven't found anything.
I've been testing this against Chrome 88.0.4324.182.
open
C:\Users\<Username>\AppData\Local\Google\Chrome\User Data\Default
open Preferences then search for excluded_schemes you will find it in 'protocol_handler' delete this excluded scheme(s) to reset chrome to open url with default application

OTRS Webservice as Requestor Test

I'm new to OTRS (3.2) and also new to PERL but I have been given the task of setting up OTRS so that it will make a call to our remote webservice so a record can be created on our end when a ticket is set as "Closed".
I set up various dynamic fields so the customer service rep can fill in additional data that will be passed into the webservice call along with ticket details.
I couldn't get the webservice call to trigger when the ticket was "Closed" but I did get it to trigger when the "priority" was changed so I'm just using that now to test the webservice.
I'm just using the Test.pm and TestSimple.pm files that were included with OTRS.
When I look at the Debugger for the Webserice, I can see that the calls were being made:
$VAR1 = {
'TicketID' => '6'
};
My webservice currently just has one method "create" which just returns true for testing.
however I get the following from the Test.pm
"Got no TicketNumber (2014-09-02 09:20:42, error)"
and the following from the TestSimple.pm
"Error in SOAP call: 404 Not Found at /TARGET/SHARE/var/otrs/Kernel/GenericInterface/Transport/HTTP/SOAP.pm line 578 (2014-09-02 09:20:43, error)
I've spent countless hours on Google but couldn't find anything on this. All I could find is code for the Test.pm and TestSimple.pm but nothing really helpful to help me create a custom invoker for my needs and configure the webservice in OTRS to get it to work.
Does anyone have any sample invokers that I can look at to see how to set it up?
Basically I need to pass the ticket information along with my custom dynamic fields to my webservice. From there I can create the record on my end and do whatever processing.
I'm not sure how to setup the Invoker to pass the necessary ticket fields and dynamic fields and how to make it call a specific method in my remote webservice.
I guess getting the Test.pm and TestSimple.pm to work is the first step then I can modify those for my needs. I have not used PERL at all so any help is greatly appreciated.
I'm also struggling with similar set of requirements too. I've also never programmed in PERL, but I can tell you at least that the "Got no TicketNumber" in the Test.pm is right from the PrepareRequest method, there you can see this block of code:
# we need a TicketNumber
if ( !IsStringWithData( $Param{Data}->{TicketNumber} ) ) {
return $Self->{DebuggerObject}->Error( Summary => 'Got no TicketNumber' );
}
You should change all references to TicketNumber to TicketID, or remove the validation whatsoever (also there is mapping to ReturnedData variable).
Invoking specific methods on your WS interface is quite simple (but poorly documented). The Invoker name that you specify in the "OTRS as requester" section of web service configuration corresponds to the WS method that will be called. So if you have WS interface with a method called "create" just name the Invoker "create" too.
As far as the gathering of dynamic field goes, can't help you on that one yet, sorry.
Cheers

Dynamic messages with gettext (AngularJS)

I have a application with a Django backend and an AngularJS front-end.
I use the angular-gettext plugin along with Grunt to handle translations.
The thing is, I sometimes received dynamic strings from my backend through the API. For instance a MySQL error about a foreign key constraint or duplicate key entry.
How can I add this strings to the .pot file or non harcoded string in general ?
I've tried to following but of course it cannot work :
angular.module('app').factory('HttpInterceptor', ['$q', '$injector', '$rootScope', '$cookieStore', 'gettext', function ($q, $injector, $rootScope, $cookieStore, gettext) {
responseError: function (rejection) {
gettext('static string'); //it works
gettext(rejection.data.error); //does not work
$rootScope.$emit('errorModal', rejection.data);
}
// Return the promise rejection.
return $q.reject(rejection);
}
};
}]);
})();
One solution I could think of would be to write every dynamic strings into a JSON object. Send this json to server and from there, write a static file containing these strings so gettext can extract them.
What do you suggest ?
I also use angular-gettext and have strings returned from the server that need to be translated. We did not like the idea of having a separate translation system for those messages so we send them over in the default language like normal.
To allow this to work we did two things. We created a function in our backend which we can call to retrieve all the possible strings to translate. In our case it's mainly static data that only changes once in a while. Ideally this would be automated but it's fine for now.
That list is formatted properly through code into html with the translate tag. This file is not deployed, it is just there to allow the extraction task to find the strings.
Secondly we created a filter to do the translation on the interpolated value, so instead of translating {{foo}} it will translate the word bar if that's was the value of foo. We called this postTranslate and it's a simple:
angular
.module('app')
.filter('postTranslate', ['gettextCatalog', function (gettextCatalog) {
return function (s) {
return gettextCatalog.getString(s);
};
}]);
As for things that are not in the database we have another file for those where we manually put them in. So your error messages may go here.
If errors are all you are worried about though, you may rather consider not showing all the error messages directly and instead determine what user friendly error message to show. That user friendly error message is in the front end and therefore circumvents all of this other headache :)

Perl - SOAP::WSDL - wsdl2perl.pl

We are working on a system in perl that has to communicate with multiple web services that provide SOAP endpoints to work with. They provide WSDL files to describe the services (in C# .Net, "Add Service Reference" would use these). We use wsdl2perl.pl to import these files into perl modules.
All is working, except for when the services return a fault.
We call a service like this:
my $service = OurInterfaces::OurService::OurPort->new();
my $result = $service->ourMethod({ someParameter => '1234' });
die $result->get_faultstring()->serialize() if not $result;
When we try to access the fault string, our perl script dies with this:
<Fault><faultcode>SOAP-ENV:Server</faultcode><faultstring>Error deserializing message: Cannot resolve class for Fault/detail/Errors/ErrorDetail via UPSRateTypemaps::RateService at /usr/lib/perl5/site_perl/5.8.8/SOAP/WSDL/Expat/MessageParser.pm line 147.
at line 1 at /usr/lib/perl5/site_perl/5.8.8/SOAP/WSDL/Expat/Base.pm line 82
.
Message was:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header/><soapenv:Body><soapenv:Fault><faultcode>Client</faultcode><faultstring>An exception has been raised as a result of client data.</faultstring><detail><err:Errors xmlns:err="http://www.ups.com/schema/xpci/1.0/error"><err:ErrorDetail><err:Severity>Hard</err:Severity><err:PrimaryErrorCode><err:Code>10001</err:Code><err:Description>The XML document is not well formed</err:Description><err:Digest>&lt;/RateRequest> does not close tag &lt;Request>.</err:Digest></err:PrimaryErrorCode><err:Location/></err:ErrorDetail></err:Errors></detail></soapenv:Fault></soapenv:Body></soapenv:Envelope></faultstring><faultactor>urn:localhost</faultactor></Fault>
It appears that the XML being returned for faults isn't mapped to a class (Class::Std) properly. How can we fix this?
Also is there any articles out there describing what we need to do to map the error messages? For the most part wsdl2perl.pl is just plain magic to us.
Since no answers yet. I'll post what we did to get it working.
We opened up the Typemap.pm file that has our $typemap_1, and added lines like this:
'Fault/detail' => 'UPSTrackElements::Errors'
'Fault/detail/Errors' => 'UPSTrackElements::Errors',
'Fault/detail/Errors/ErrorDetail' => 'UPSTrackTypes::ErrorDetailType',
...
wsdl2perl.pl imported the error classes we needed, but it didn't hook them up properly in the typemap file. In general, we had to append 'Fault/detail' to the front of where it mapped all of our Error classes. I'm not sure why wsdl2perl.pl couldn't map them right, when it obviously had no issue generating the classes.
We had to fill out the appropriate XPath for every single field in the fault xml. It is working for us now, but was rather ugly to figure out.

how to PUT multiple query parameter in REST web service

Do anyone know how to PUT multiple query parameter in REST web service?
I have write using java.My curl sample is like this:
curl -X PUT http://localhost:8080/project/resources/user/directory/sub-directory?name=shareuser&type=Read -v
My program is :
#PUT
#Path("{user}/{directory:.+}")
public Response doshare(#PathParam("user")String name,
#PathParam("directory")String dir,
#QueryParam("name")String sharename,
#QueryParam("type")String type){
mongoDAOImpl impl=new mongoDAOImpl();
Mongo mongo=impl.getConnection("127.0.0.1","27017");
DB db=impl.getDataBase(mongo,"public");
DBCollection coll=impl.getColl(db,name);
DBCollection coll2=impl.getColl(db,"sharedb");
shareDTO sharedto=new shareDTO();
String authority=type.toLowerCase();
if(authority.equals("rd")){
sharedto.setAuthority("4");
}else if(authority.equals("rw")){
sharedto.setAuthority("12");
}
sharedto.setTargetuser(sharename);
sharedto.setRealURI("/home/public/"+name+"/"+dir);
sharedto.setIdentifier(name);
sharedto.setParentURI("/home/public/"+sharename);
boolean bool = false;
sharefun=new sharefunction();
if(sharefun.checksubfoldershared(coll, coll2, sharedto)){
bool=sharefun.sharefiles(coll, coll2, sharedto);
}else{
System.out.println("Error");
}
// ...
But I only get the name query parameter.How to get or how to type in curl command in order to get all query parameter?
Your code is fine - the problem is with the way you're invoking curl. When passing a URL to curl that contains a '&', you have to put quotes around the URL. Otherwise, the shell will interpret the stuff after the '&' as a separate command.
EDIT: My text is getting munged when I submit it as a comment. Here's what you need to do:
curl -X PUT 'http://localhost:8080/project/resources/user/directory/sub-directory?name=shareuser&type=Read' -v
Have you tried using the -d option? e.g.
curl -X PUT -d "name=shareuser&type=Read" http://locahost:8080/project/resources/user/directory/sub-directory -v
You could try handling this all as a multipart upload (the server-side details aren't standardized and you didn't say what framework you're using so I can't give more clues) but I question why you're having the user and permissions set (via any parameters) in the first place. Surely you'd be better off deriving the user to store as through inspecting the user's login credentials and using a default set of permissions that they can change later on? Nearly as cheap, and enormously simpler.