xml-rpc with coldfusion - how to pass in parameters? - perl

I'm trying to make an xml-rpc call from a coldfusion server. I found this xml-rpc cfc (Thanks, Brad Wood!) that formats the xml for the cfhttp call but I'm struggling with interpretting how to pass in any parameters besides the call name.
Here's the api documention using perl
This is my coldfusion code
<cfobject component="xmlrpc" name="c">
<cfset arr = arraynew(1)>
<cfset arrayappend(arr,"service.show")>
<cfset arrayappend(arr,myVIP)>
<cfset arrayappend(arr,myIP)>
<cfset arrayappend(arr,myPort)>
<cfset myxml = c.CFML2XMLRPC(arr,"call")>
<cfhttp url="#apiUrl#" method="POST">
<cfhttpparam name="request_body" value="#myxml#" type="XML">
</cfhttp>
The error message that I keep getting is:
Can't use string (myVIP...) as a HASH ref while "strict refs" in
use at /home/...
So what I don't understand is how to translate the notation I see in perl of {parameter1=>'value1',parameter2=>'value2'} to the array that I'm passing into CFML2XMLRPC.

I figured it out and now it seems so obvious... You have to use a struct to hold the parameters and pass that into the array after the method string.
<cfset arr = arraynew(1)>
<cfset arrayappend(arr,"service.show")>
<cfset paramObj = structnew()>
<cfset paramObj['vip'] = myVIP>
<cfset paramObj['ip'] = myIP>
<cfset paramObj['port'] = myPort>
<cfset paramObj['show'] = "status/state">
<cfset arrayappend(arr,paramObj)>
I found the jquery xml-rpc documentation helpful to understanding how the xml should actually be formatted.
Note: the notation of
<cfset paramObj['ip'] = myIP>
vs
<cfset paramObj.ip = myIP>
is important since the latter will result in all uppercase for the parameter name in xmlrpc.cfc. (thanks to comment by Dave Merrill on this post)

Related

Lucee form scope not recognized

The problem below is a puzzle, which I have not solved, but found a way to deal with. I finally tore apart xref2.cfm and put it back together one line at a time. As soon as I did that, the form scope began to work. Nothing changed in the code. So it remains a mystery.
I have a set of 4 procedures which are linked together like this:
person1.cfm: this originates with a call from another program and a url argument basetab=Person . I'm focusing here on the variable "fn".
<cfif IsDefined('URL.basetab')>
<cfset basetab = URL.basetab>
<cfset perloc = URL.perloc>
<cfset fn = ''>
<cfset ln = ''>
<cfset eof = "no">
however person1.cfm may also be accessed with a cfinclude further down the line, in which case I rely on the form scope to pass fn back to person1.cfm
<cfelseif IsDefined('form.fn')>
<cfset fn = form.fn>
<cfset ln = form.ln>
<cfset basetab = form.basetab>
<cfset perloc = form.perloc>
<cfset eof = form.eof>
</cfif>
however, if neither the URL scope nor the form scope is defined here, the variable fn should have been set in a program which is including person1.cfm, so it should be well defined at this point.
... stuff ...
<form action = "person2.cfm" method = "post" ... other stuff>
<input type = "text" name = "fn" value = "#fn#">
.... more stuff....
... submit ...
</form>
person2.cfm: this is the next step in the process
... stuff ...
<cfset fn = form.fn>
<cfinclude template = "person1.cfm">
<form action = "xref1.cfm" method = "post" ... stuff >
<cfoutput>
<input type = "hidden" name = "fn" value = "#fn#">
</cfoutput>
.... stuff ....
.....submit ....
</form>
xref1.cfm this is the next step
... stuff ...
<cfset fn = form.fn>
<cfinclude template = "person2.cfm">
<form action = "xref2.cfm" method = "post" ... stuff >
<cfoutput>
<input type = "hidden" name = "fn" value = "#fn#">
</cfoutput>
.... stuff ....
.....submit ....
</form>
xref2.cfm the final step, where something is done with the accumulated entries
.... stuff ...
<cfset fn = form.fn>
<cfinclude template = "xref1.cfm">
... no form ... other stuff ...
The problem is that in xref2.cfm the variable fn is not being picked up in the form scope. Checking things out, I find that form.fn is not defined at all in xref2.cfm. When things cascade back to Person1, fn is not defined and throws an error in the input tag.
form.fn is defined in person2.cfm and in xref1.cfm. But when we get to xref2.cfm, it disappears. I do not understand why. Can someone explain to me why that form scope is not there in xref2.cfm?
I think it was a typo or something.

Counter withing a form output "#Form.Item(u)#"

I have a form that outputs different items in order the value of the items is coming out properly from the form (Value1,Value2,Value3) however when I try to do the same sort of counting method in my cfsets it does not work.
<cfloop index="u" from="1" to="#arraylen( session.cart )#">
<cfset updateRow = session.cart[u]/>
<cfset session.cart[#u#].Ohm = "#Form.ohmicValue1#">
Essentially what I need the "1" to be is a variable (#u#) but how do I got about landing that within the #Form.OhmicValue[#u#]# I guess is the final question.
Thanks for the help.
Tom
The following should work
<cfloop index="u" from="1" to="#arraylen( session.cart )#">
<cfset updateRow = session.cart[u]/>
<cfset session.cart[u].Ohm = form['ohmicValue' & u]>
</cfloop>

How to declare dynamic form field defaults inside a Coldfusion CFC?

I'm looking for a way to declare form default values dynamically in a CFC, I'm calling via AJAX. The current CFC sends orders, which I need to break down into sub-orders.
I had been using this:
<!--- static defaults --->
<cffunction name="Defaults" access="public" returntype="struct" output="false"
hint="Assign default values to instance">
<cfscript>
var formDefaults = {
versenden=""
, speichern=""
...
}
</cfscript>
<cfreturn formDefaults />
</cffunction>
<cffunction name="Commit" access="public" returntype="struct" output="false" hint="database handler">
<!--- add dynamic form fields --->
<cfscript>
var LOCAL = {};
variables.defs = THIS.Defaults();
</cfscript>
<cfloop collection="#VARIABLES.Instance.FormData#" item="formField">
<cfscript>
if ( LEFT(formField, 5) EQ "MENGE"
OR LEFT(formField, 3) EQ "EAN"
OR LEFT(formField, 12) EQ "BESTELL_TEXT"
OR LEFT(formField, 10) EQ "BESTELLTYP"
...
) {
variables.defs[formField]="";
}
</cfscript>
</cfloop>
<cfscript>
structAppend(variables.defs, VARIABLES.Instance.FormData);
LOCAL.Basket = variables.defs;
</cfscript>
...
So I first declare static form fields (single instance only) and then try to dynamically append dynamic form fields to my array, which might be transferred multiple times (MENGE38, MENGE39, MENGE40 etc)
While this works ok, I need to add another counting element to my form-names, so I would have to change MENGE to something like counter.MENGE or MENGE.counter which will then send form values like this:
MENGE.1.38
MENGE.1.40
MENGE.1.41
MENGE.2.37
With the counter denoting the sub-order, this field is used for.
Problem is, this breaks my dynamic form field declaration and I don't understand why. I'm getting the following errors:
Diagnose: Element MENGE.1 is undefined in a CFML structure referenced as part of an expression.
Question:
Can anyone give me a hint on what the problem might be? Do I have to param the form fields on the HTML page as well (shouldn't have to)?
Thanks!
EDIT:
Problem was in my validate function, I also need to declare the modifications I did above. The new function looks like this:
<cffunction name="Validate" access="public" returntype="array" output="false" hint="validate form inputs and return an array of faulty field names.">
<cfscript>
var LOCAL = {};
var double = structNew();
double.form = VARIABLES.Instance.FormData;
double.criteria = VARIABLES.Instance.Validation;
</cfscript>
<!--- add dynamic form fields for validation... I FORGOT TO UPDATE THIS--->
<cfloop collection="#VARIABLES.Instance.FormData#" item="formField">
<cfscript>
if ( LEFT(formField, 5) EQ "MENGE"
OR LEFT(formField, 10) EQ 'BESTELLTYP'
OR LEFT(formField, 3) EQ "EAN"
OR LEFT(formField, 12) EQ "BESTELL_TEXT"
...
) {
VARIABLES.Instance.Validation[formField]="pass";
}
</cfscript>
</cfloop>
<!--- Get error names and type --->
<cfinvoke component="form_validate" method="validate_fields" double="#double#" returnvariable="validation_errors"></cfinvoke>
<cfset LOCAL.ErrorMessages = validation_errors />
<cfreturn LOCAL.ErrorMessages />
Because I did not add the new updated the if-clause in this function, I was getting the error.
To build on Dan Bracuk's answer, use underscores (though you'd need to change the name of "BESTELL_TEXT"). Use this with a combination of listFirst, listGetAt, and listLast to determine field name structure, using underscore as delimiter. Note how I cleaned up your big IF a bit using list function. This code as written probably doesn't do what you need, but wanted to illustrate the concepts without having to understand your business need.
<cfscript>
var orders=structNew();
item=listFirst(formField,'_');
orderNames = "MENGE,EAN,BESTELLTEXT,BESTELLTYPE";
if (listFindNoCase(orderNames,item,'_')){
if (!structKeyExists(orders,item)){
// initialize item
orders[item]=structNew();
}
orderID="";
subOrderId="";
if (listLen(formField,'_') gt 1) {
orderID=listGetAt(formField,2,'_');
}
if (listLen(formField,'_') eq 2) {
orders[item][orderId]=formData[formField];
}
if (listLen(formField,'_') eq 3) {
subOrderId=listLast(formField,'_');
orders[item][orderId][subOrderId]=formData[formField];
}
}
</cfscript>

MSXML2.ServerHTTP in Coldfusion 8 fails to post data

In trying to work around the buggy <cfhttp> SSL implementation, I switched to using COM and MSXML2.ServerHTTP directly. Unfortunately the POST data fails to arrive at the destination page. GET works, but I'd rather not use it, since I have no idea if the request urls are logged and I don't want to pass sensitive data if I can help it. (Am I worrying too much?)
My problem is similar to this one: http://objectmix.com/xml-soap/87408-sending-post-variables-using-msxml2-serverxmlhttp-3-0-a.html
Here's my code:
<cfset querystring = "?CustomerID=#CustomerID#&OrderDelRecipient=#OrderDelRecipient#&OrderCompany=#OrderCompany#&OrderTelephone=#OrderTelephone#&OrderNotes=#OrderNotes#&OrderDelStreet=#OrderDelStreet#&OrderDelCity=#OrderDelCity#&OrderDelState=#OrderDelState#&OrderDelZip=#OrderDelZip#">
<cfobject type="COM" action="Create" name="objSrvHTTP" class="MSXML2.ServerXMLHTTP.3.0">
<cfset urlstring = "http://www.pascaltechnologies.com/saveorder.cfm">
<cfset temp = objSrvHTTP.open("post", urlstring, false)>
<cfset temp = objSrvHTTP.setRequestHeader("Content-Type", "application/x-www-form-
urlencoded")>
<cfset temp = objSrvHTTP.send("#querystring#")>
<cfset FileContent = objSrvHTTP.responsetext>
<cfoutput>
#filecontent#
</cfoutput>
When I check for the Form vars on the called page (saveorder.cfm)
<cfif Not IsDefined("CustomerID") OR NOT IsNumeric(CustomerID)>
ERR 609 - INVALID PARAMETERS PASSED.
<cfabort>
</cfif>
they are empty, so the above code gets triggered.
Is this because I need to copy the response to the form var? e.g. <cfset FORM.CustomerID = objSrvHTTP.CustomerID> (You'd think it wouldn't be necessary since it isn't needed with GET.) Or am I simply not implementing something correctly?

Using Coldfusion to decode a facebook signed_request

I'm trying to use ColdFusion 9 to decode the signed_request variable that facebook passes. It is a base64 URL encoded string that needs to be decoded. In order to decode base64 strings one should do:
ToString( BinaryDecode( 'Base64 URL Encoded String' ) )
When I do this I get the error: input and output encodings are not same.
I took this same string to this website and decoded it correctly: http://www.motobit.com/util/base64-decoder-encoder.asp
The issue is that the Facebook signed_request parameter doesn't have any "=" padding at end. It is expecting the receiving end to add the padding before being put through BinaryDecode().
Try
<cfset signed_request = listtoarray(form.signed_request,".")>
<cfset fb_str = replacelist(signed_request[2], "-,_", "+,/")>
<cfset padding = repeatstring("=",4-len(fb_str) mod 4)>
<cfset data = toString(BinaryDecode(fb_str & padding,"base64"))>
<cfoutput>#data#</cfoutput>
I found a solution to this problem. It seems like the built in ColdFusion BinaryDecode function can handle some of these strings. Here's the code:
<cfset fb_str = Replace(Replace(ListGetAt(FORM.signed_request, 2, "."), "-", "+", "ALL"), "_", "/", "ALL")>
<cfset jstr = JavaCast("string", fb_str)>
<cfset decoder = CreateObject("java", "org.apache.commons.codec.binary.Base64")>
<cfset result = decoder.decodeBase64(jstr.getBytes())>
<cfoutput>#ToString(result)#</cfoutput>
A quick search around the net leads me to think that the facebook response is "base64url" which is encoded for passing in a url.
So, try doing a
ToString( BinaryDecode( UrlDecode('Base64 URL Encoded String') ) )
And, if that doesn't work, I found this function
<cffunction name="Base64URLDecode" access="public" output="false" returntype="string" hint="Returns a base64url decoded string.">
<cfargument name="String" type="string" required="true" hint="String to decode.">
<cfreturn Variables.Base64Decode( Replace( Replace( Arguments.String, "-", "+", "all"), "_", "/", "all") & RepeatString("=", Len(Arguments.String) + (4 - Len(Arguments.String) % 4) % 4))>
</cffunction>
from this url: http://pastebin.com/bFb1bBpU
Check out Ben Nadel's post on how he did this:oAuth and CF
His code samples show you exactly what you need to do to solve this issue. It took him a while, but it works!
I know I'm late to the game on this comment but I wrote a tutorial on exactly this here!
It turns out that Facebook don't use a standard base64 encoding and it needs padding out to the right length. Check the link to get a struct containing the data from the signed_request.