SecretKeySpec for ColdFusion - coldfusion

I am trying to convert this code into java but the results are not accurate
private String hmacDigest(String msg, String keyString, String algo) throws Exception {
String digest = null;
SecretKeySpec key = new SecretKeySpec((keyString).getBytes("UTF-8"), algo);
Mac mac = Mac.getInstance(algo);
mac.init(key);
byte[] bytes = mac.doFinal(msg.getBytes("ASCII"));
StringBuffer hash = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
hash.append('0');
}
hash.append(hex);
}
digest = hash.toString();
return digest;
}
and my codfusion try till this point
<cfset keybytes = BinaryDecode(SECRET_KEY, "Hex")>
<cfset databytes = CharsetDecode(data, "UTF-8")>
<cfset secret = createObject("java", "javax.crypto.spec.SecretKeySpec").Init(keybytes,"HmacSHA256")>
<cfset mac = createObject("java", "javax.crypto.Mac")>
<cfset mac = mac.getInstance("HmacSHA256")>
<cfset mac.init(secret)>
<cfset digest = mac.doFinal(databytes)>
<cfset result = BinaryEncode(digest, "Base64")>
my knowledge is very limited in java so i am not sure if i am doing it right or wrong

You mixed up the string encodings, so the CFML is actually using different input values than the java code. That's why your results don't match
The java method decodes the key as "UTF-8"
(keyString).getBytes("UTF-8")
.. but the CFML is using "hexadecimal"
<cfset keybytes = BinaryDecode(SECRET_KEY, "Hex")>
The java method decodes the message as "ASCII"
msg.getBytes("ASCII")
... but the CFML is using "UTF-8"
CharsetDecode(data, "UTF-8")
The java result is encoded as "hexadecimal", but the CFML is using "base64"
BinaryEncode(digest, "Base64")
\n is interpreted as line feed in java. It doesn't work the same way in CF. Instead use chr(10).
While the errors are easily fixed, the java code isn't even needed. Use the built in HMAC function instead. It produces the same result. The only difference being that CF returns hexadecimal in all upper case, so you must wrap it in LCASE() to get an exact match.
Lcase( HMac( messageString, keyAsString, algorithm, "ASCII") )

Related

Remove the string from URL

How to remove the string after last '/' from a URL?
Example: <cfset normalURL = "http://test.com/myhome/live">
Expected outcome: http://test.com/myhome
Here is one way to do it.
<cfset normalURL = "http://test.com/myhome/live">
<cfoutput><p>#normalURL#</p></cfoutput>
Output from above code:
http://test.com/myhome/live
<cfset stringAfterLastSlash = ListLast(normalURL,"/")>
<cfoutput><p>#stringAfterLastSlash#</p></cfoutput>
Output from above code:
live
<cfset stringRemovedFromURL = Replace(normalURL,"/#stringAfterLastSlash#","")>
<cfoutput><p>#stringRemovedFromURL#</p></cfoutput>
Output from above code:
http://test.com/myhome
You can play with this code and see the results here.

cfspreadsheet save as .csv, Excel says "The file format and extension of FILE.csv don't match."

I've created a cold fusion page to output a client list from MYSQL into a CSV file for easy uploading to SalesForce.com
I can generate the file with all the correct information. However, when I try to open it with excel I get the error:
"The file format and extension of 'SalesForceDailyLeads-20160613125138.csv' don't match. The file could be corrupted or unsafe. Unless you trust its source, don't open it. Do you want to open it anyway?" I can open it(excel for MAC), but it appears to me that CFSpreadsheet is not creating a legit .csv file and is instead making a xlsx.
<cfset FileCSV = "SalesForceDailyLeads-#dateformat(getBatch.BATCH,"yyyymmdd")##timeformat(getBatch.BATCH,"HHmmss")#.csv" >
<cfset filename = "/SF/#fileCSV#">
<cfset s = spreadsheetNew() >
<cfset spreadsheetAddRow(s, "FIRST, LAST, MIDDLE, STREET, CITY, ZIP, STATE")>
<cfinclude template="SFgetList.cfm">
<cfset spreadsheetAddRows(s, getList)>
<cfspreadsheet
action="write"
overwrite = "true"
format ="csv"
name ="s"
filename ="#filename#"
>
If I make an XLS file I have no issues like I do with CSVs. Is this a problem with the code, CFSpreadsheet, or excel(for mac)? Can I fix it?
Use cffile, not cfspreadsheet to create the files. Per the documentation:
The cfspreadsheet tag writes only XLS[X] format files. To write a CSV
file, put your data in a CSV formatted string variable and use the
cffile tag to write the variable contents in a file.
Thanks! Of course, right after I posted this I found the docs and saw that I was using it incorrectly and I used CFFILE instead. I used a script/function I found to do this. It took a little work however, to convert the query into CSV- luckily someone else already did it. In case anyone care to see it:
I got the querytoCSV script here:
https://gist.github.com/CreativeNotice/2775372
<cfscript>
/**
* queryToCsv
* Allows us to pass in a query object and returns that data as a CSV.
* This is a refactor of Ben Nadel's method, http://www.bennadel.com/blog/1239-Updated-Converting-A-ColdFusion-Query-To-CSV-Using-QueryToCSV-.htm
* #param {Query} q {required} The cf query object to convert. E.g. pass in: qry.execute().getResult();
* #param {Boolean} hr {required} True if we should include a header row in our CSV, defaults to TRUE
* #param {String} d {required} Delimiter to use in CSV, defaults to a comma (,)
* #return {String} CSV content
*/
public string function queryToCsv(required query q, required boolean hr = true, required string d = ","){
var colNames = listToArray( lCase(arguments.q.columnlist) );
var newLine = (chr(13) & chr(10));
var buffer = CreateObject('java','java.lang.StringBuffer').Init();
// Check if we should include a header row
if(arguments.hr){
// append our header row
buffer.append(
ArrayToList(colNames,arguments.d) & newLine
);
}
// Loop over query and build csv rows
for(var i=1; i <= arguments.q.recordcount; i=i+1){
// this individual row
var thisRow = [];
// loop over column list
for(var j=1; j <= arrayLen(colNames); j=j+1){
// create our row
thisRow[j] = replace( replace( arguments.q[colNames[j]][i],',','','all'),'""','""""','all' );
}
// Append new row to csv output
buffer.append(
JavaCast( 'string', ( ArrayToList( thisRow, arguments.d ) & iif(i < arguments.q.recordcount, "newLine","") ) )
);
}
return buffer.toString();
};
</cfscript>
<cfinclude template="getDups.cfm">
<cfinclude template="SFgetList.cfm">
<cfset FileCSV = "SalesForceDailyLeads-#dateformat(getBatch.BATCH,"yyyymmdd")##timeformat(getBatch.BATCH,"HHmmss")#.CSV" >
<cfset filename = "/mnt/nas-share/data/feed/SF/#fileCSV#">
<cfset qc = #queryToCsv(getList, false, ",")# >
<cfoutput>#qc#</cfoutput>
<cfset heads= "FIRST, LAST, MIDDLE, STREET, CITY, ZIP, STATE">
>
<cffile
action = "write"
file = #filename#
output = #heads#
addNewLine = "yes"
fixnewline = "no">
<cffile
action = "append"
file = #filename#
output = #qc#
addNewLine = "yes"
fixnewline = "no">

Coldfusion Struct getting only numeric key list

I have a coldfusion Struct containing mix keys numeric and alpha, alphanumerics
I need to access only the numeric keys.
My code looks like
<cfset ids = structkeyList(st ) />
<cfset numericIDs = "" />
<cfloop list="#ids#" index="i">
<cfif IsNumeric(i)>
<cfset numericIDs = ListAppend( numericIDs , i ) />
</cfif>
</cfloop>
Is there a better method to solve such problems?
Is there a better method to solve such problems?
I would use something like this:
<cfset numericIDs = arrayToList(reMatch('\b\d+(?=,|$)\b', structKeyList(st)))>
Is there a better method to solve such problems?
I'd generally recommend working with arrays instead of lists.
In CF9 a loop similar to yours is as good as it gets. You can make a utility function out of it if you need it more than once. This one avoids StructKeyList() to be able to deal with all kinds of keys, independent of a separator character:
<cfscript>
function GetNumericKeys(struct) {
var keys = struct.keys();
var result = ArrayNew(1);
var key = "";
while (keys.hasNext()) {
key = keys.next();
if (IsNumeric(key)) ArrayAppend(result, key);
}
return result;
}
</cfscript>
and
<cfset nkeys = GetNumericKeys(st)>
In CF11 you can get a little more sophisticated (tested on CF11, can't say how CF10 handles this code).
<cfscript>
numericIDs = arrayFilter(structKeyArray(st), function (key) {
return IsNumeric(key);
});
</cfscript>
To ensure integer keys, use:
<cfscript>
numericIDs = arrayFilter(structKeyArray(st), function (key) {
return Int(key) eq key;
});
</cfscript>
I really don't see what's wrong with this. It should work quite well already, and it is very readable.
Sometimes working with a List is faster than an Array.
I had this:
<cfscript> function ListNumeric(principal) {
a=principal;
cleanlist = ''; for (i=1; i <= ListLen(a);i=i+1) { if(IsNumeric(ListGetAt(a,i))){ cleanlist = ListAppend(cleanlist,ListGetAt(a,i)); } } Return cleanlist; } </cfscript>
Also possible to work with regular expression:
inList2 = REReplace(inList,"[^0-9.]", "","ALL");

Why is the Barbecue Barcode library generating a different image from other Barcode generators?

I am having a problem with the Barbecue Barcode Library. I am trying to create a simple code128 barcode, but the image I get is different from what I get from other online (i.e. http://barcode-generator.org) and desktop (i.e. Zing) Barcode generators.
Here is the ColdFusion code I am using:
<cfscript>
LOCAL.BarcodeData = "10047846";
LOCAL.BarcodeFactory = CreateObject("java", "net.sourceforge.barbecue.BarcodeFactory");
LOCAL.Barcode = LOCAL.BarCodeFactory.createCode128(LOCAL.BarcodeData);
LOCAL.BarcodeImageHandler = CreateObject("java", "net.sourceforge.barbecue.BarcodeImageHandler");
LOCAL.BarcodeBufferedImage = LOCAL.BarcodeImageHandler.getImage(LOCAL.Barcode);
LOCAL.BarcodeImage = ImageNew(LOCAL.BarcodeBufferedImage);
LOCAL.BarcodeImagePath =
"C:\temp_\barcode-" & LOCAL.BarcodeData & ".jpg";
ImageWrite(LOCAL.BarcodeImage, LOCAL.BarcodeImagePath, 1);
</cfscript>
<cfimage action="writeToBrowser" source="#LOCAL.BarcodeImagePath#" />
This outputs the following image:
Yet, here is what I get from the Zing desktop program:
And here is what I get from barcode-generator.org:
Now, I have no issue with the sizing, scaling, etc. But, you can easily tell that the Barbecue-generated image is much different - simply look at the first several bars.
Why is this happening? Is this a Barbecue bug or am I doing something wrong?
Not sure this is the "answer", per se, but, when I changed the code to use the Code128C format, the image came out as expected. I just had to do some resizing to get it exactly the size I needed:
Code:
<cfscript>
LOCAL.BarcodeData = "10047846";
LOCAL.BarcodeFactory = CreateObject("java", "net.sourceforge.barbecue.BarcodeFactory");
LOCAL.Barcode = LOCAL.BarCodeFactory.createCode128C(LOCAL.BarcodeData);
LOCAL.Barcode.setDrawingText(false);
LOCAL.Barcode.setDrawingQuietSection(false);
LOCAL.Barcode.setBarWidth(1);
LOCAL.Barcode.setBarHeight(30);
LOCAL.BarcodeImageHandler = CreateObject("java", "net.sourceforge.barbecue.BarcodeImageHandler");
LOCAL.BarcodeBufferedImage = LOCAL.BarcodeImageHandler.getImage(LOCAL.Barcode);
LOCAL.BarcodeImage = ImageNew(LOCAL.BarcodeBufferedImage);
LOCAL.BarcodeImagePath =
"C:\temp_\barcode-" & LOCAL.BarcodeData & ".jpg";
ImageWrite(LOCAL.BarcodeImage, LOCAL.BarcodeImagePath, 1);
</cfscript>
<cfimage action="writeToBrowser" source="#LOCAL.BarcodeImagePath#" />
It looks like the bar width of your image is larger then the examples. Set the bar width to 1 px. by adding LOCAL.Barcode.setBarWidth(1);before you generate the barcode.
<cfscript>
LOCAL.BarcodeData = "10047846";
LOCAL.BarcodeFactory = CreateObject("java", "net.sourceforge.barbecue.BarcodeFactory");
LOCAL.Barcode = LOCAL.BarCodeFactory.createCode128(LOCAL.BarcodeData);
LOCAL.Barcode.setBarWidth(1); // decrease the width of the bars
LOCAL.Barcode.setBarHeight(50); // if you want a taller barcode like the examples
LOCAL.BarcodeImageHandler = CreateObject("java", "net.sourceforge.barbecue.BarcodeImageHandler");
LOCAL.BarcodeBufferedImage = LOCAL.BarcodeImageHandler.getImage(LOCAL.Barcode);
LOCAL.BarcodeImage = ImageNew(LOCAL.BarcodeBufferedImage);
LOCAL.BarcodeImagePath = gettempDirectory()& "\barcode-" & LOCAL.BarcodeData & ".jpg";
ImageWrite(LOCAL.BarcodeImage, LOCAL.BarcodeImagePath, 1);
</cfscript>
I scanned it with a barcode scanner and all three images read the same string "10047846". For barcodes, I use CFBarbecue (http://cfbarbecue.riaforge.org/), a ColdFusion wrapper for Barbecue. Using the same string, below is the image I was able to generate using CFBarbecue.

QueryAddRow throwing error in Coldfusion Webservice

I am facing a weird issue.
When I am consuming the below snippet of code as a webservice residing in a CF9 server I am getting the error "The value coldfusion.runtime.Struct cannot be converted to a number."
The call returns an array of structures. I would like to create a query from this array of structure. When I place this code as a standalone code in my local server(CF10) it works fine. But as soon i place it in the remote server to be invoked i get the error.
I almost pulled out my hair when I got the same error message even when I replaced the variable 'tempstruct' with a hard coded structure. As soon I remove the QueryAddRow I am able to return anything.
Any help is appreciated.
<cfset myquery=querynew("category,category_id,event_description","varchar,integer,varchar")>
<cfinvoke
webservice="http://199.99.99.999/vod_queries.cfc?wsdl"
method="getAllCategoryByResort"
returnvariable="arrAllSpaEvents"
refreshwsdl="true" >
<cfinvokeargument name="Resort" value="SRB" >
</cfinvoke>
<cfif arraylen(arrAllSpaEvents) GT 0>
<cfloop array="#arrAllSpaEvents#" index="cur_row">
<cfset tempstruct=StructNew()>
<cfset tempstruct.CATEGORY=cur_row.CATEGORY>
<cfset tempstruct.CATEGORY_ID=cur_row.CATEGORY_ID>
<cfset tempstruct.EVENT_DESCRIPTION=cur_row.EVENT_DESCRIPTION>
<cfset QueryAddRow(myquery,#tempstruct#)>
</cfloop>
</cfif>
<cfreturn myquery>
You almost got.
However, indeed you are using new CF10 overloading in CF9. What's more, if you were using CF10, it looks like you could stuff the whole top array in with looping like that.
But you can almost do the same thing. CF9 will take an array overload for the value.
Not quite as clean as CF10 but you do what you can.
Also, the extra # signs are superfluous.
Here is an example with something that your data might look like:
<cfscript> // I did this all in a cfscript block for simplicity
Your retrieved data might look something like this guessin from example
arrAllSpaEvents = [
{category='fun', category_id=1, event_description='massage'},
{category='work', category_id=2, event_description='spinning'},
{category='beauty', category_id=3, event_description='mani'},
{category='beauty', category_id=3, event_description='pedi'}
];
Create a more useful struct to build the query dynamically
s = {
category = {colType = 'varchar', colVals = []},
category_id = {colType = 'integer', colVals = []},
event_description = {colType = 'varchar', colVals = []}
};
This is looping the data to fill the colVals arrays
for(c = 1; c <= arrAllSpaEvents.size(); c++ ) {
for(k in arrAllSpaEvents[c]) {
s[k].colVals[c] = arrAllSpaEvents[c][k];
}
}
This is the short form of the same double loop above in a single line
for(c = 1; c <= arrAllSpaEvents.size(); c++ ) for(k in arrAllSpaEvents[c]) s[k].colVals[c] = arrAllSpaEvents[c][k];
Now build your query. Start with an empty query (pass in a blank);
q = queryNew('');
Then loop your struct and using the keys for the column names (for simplicity they are the same key)
for(k in s ) queryAddColumn(q,k,s[k].colType,s[k].colVals);
Verify your struct and query:
writedump(s);
writedump(q);
</cfscript>
I ran this in CF9 so should work fine for you.
This should get you going.