1

I'm creating an Apple Wallet Pass using a combination of ColdFusion and Java. I have a cfc that creates the pass. It utilizes Java to sign and compressed the pass. I'm using these projects on GitHub https://github.com/dawesi/cfwheels-passkit and https://github.com/drallgood/jpasskit

Here I'm trying to create a pass:

    <cfset mypass = new passkit()>
    <cfset result = mypass.createPassbook()>

The passkit.cfc

<cfcomponent displayname="Apple Passbook Plugin" output="false">
<cffunction name="init" output="false">
    <cfscript>
        this.version = "1.1.8";
        return this;
    </cfscript>
</cffunction>

<cffunction name="createPassbook" returntype="any" output="false">
    <cfargument name="type" type="string" default="generic"/>
    <cfscript>
        var loc = {};

        loc.returnValue = createObject('component','PassBook').init(arguments.type);

        return loc.returnValue;
    </cfscript>
</cffunction>

The PassBook.cfc

   <cfcomponent displayname="Apple Passbook" output="false">
    <cffunction name="init" output="false">
        <cfargument name="type" type="string" default="generic"/>
        <cfscript>
            setType(arguments.type);
            return this;
        </cfscript>
    </cffunction>



     <cffunction name="build" returntype="any" output="false">
    <cfargument name="file" type="string" required="false"/>
    <cfargument name="password" type="string" default=""/>
    <cfscript>
        var loc = {};

        //Set things on the passbook at the end
        if(structKeyExists(variables,'barcode'))
            $getPassBook().setBarcode(variables.barcode);

        //Get the correct type that they set it too
        switch(variables.type){
            case 'boardingpass':
                loc.passClass = 'PKBoardingPass';
                loc.passMethod = 'setBoardingPass';
                break;
            case 'coupon':
                loc.passClass = 'PKCoupon';
                loc.passMethod = 'setCoupon';
                break;
            case 'eventticket':
                loc.passClass = 'PKEventTicket';
                loc.passMethod = 'setEventTicket';
                break;
            case 'storecard':
                loc.passClass = 'PKStoreCard';
                loc.passMethod = 'setStoreCard';
                break;
            default:
                loc.passClass = 'PKGenericPass';
                loc.passMethod = 'setGeneric';
                break;
        }

        //Create it, and add the fields
        loc.pass = $createJavaObject('de.brendamour.jpasskit.passes.#loc.passClass#');
        if(structKeyExists(variables,'fields')){
            for(loc.type in variables.fields){
                loc.method = variables.fields[loc.type].method;
                loc.fields = variables.fields[loc.type].fields;

                //loc.pass[loc.method](loc.fields);
                loc.dynamicMethod = loc.pass[loc.method]; // Get method
                loc.dynamicMethod(loc.fields); // Invoke it

        }

        //$getPassBook()[loc.passMethod](loc.pass);
        loc.dynamicMethod2 = $getPassBook()[loc.passMethod];
        loc.dynamicMethod2(loc.pass);



        //Sign and make the archive
        loc.signingUtil = $createJavaObject('de.brendamour.jpasskit.signing.PKSigningUtil');
        loc.signingInfo = loc.signingUtil.loadSigningInformationFromPKCS12FileAndIntermediateCertificateFile(
                $passBookCertificateLocation(),
                arguments.password,
                $intermediateCertificateLocation()
            );

        loc.bytes = loc.signingUtil.createSignedAndZippedPkPassArchive(
                $getPassBook(),
                variables.templatePath,
                loc.signingInfo
            );

        //See if we are writing this to a file
        if(structKeyExists(arguments,'file') && len(arguments.file)){
            loc.file = $createJavaObject('java.io.FileOutputStream').init(arguments.file);
            loc.file.write(loc.bytes);
            loc.file.close();
        }

        return loc.bytes;
    </cfscript>
</cffunction>


</cfcomponent>

But I'm running into this bug. I think it's missing something. Can anyone help?

Invalid CFML construct found on line 354 at column 42.

ColdFusion was looking at the following text:
loc.method

The CFML compiler was processing:

A script statement beginning with loc.pass on line 354, column 33.
A script statement beginning with { on line 351, column 58.
A script statement beginning with for on line 351, column 25.
A script statement beginning with { on line 350, column 56.
A script statement beginning with if on line 350, column 17.
A cfscript tag beginning on line 317, column 10.
A cfscript tag beginning on line 317, column 10.

The error occurred in /Applications/ColdFusion2016/cfusion/wwwroot/passkit/PassBook.cfc: line 354
Called from /Applications/ColdFusion2016/cfusion/wwwroot/passkit/passkit.cfc: line 14
Called from /Applications/ColdFusion2016/cfusion/wwwroot/passkit/test_walletpass.cfm: line 7
352 :                   loc.method = variables.fields[loc.type].method;
353 :                   loc.fields = variables.fields[loc.type].fields;
354 :                   loc.pass[loc.method](loc.fields);
355 :               }
Mrfroddo
  • 49
  • 6
  • 4
    You've posted 570 lines of code. Are you seriously thinking anyone is reading this? Cut out 550 lines and try again. – Tomalak Mar 22 '18 at 21:56
  • 1
    Because this was a compile error, I remembered your other question, I took a stab at it. But normally the more code there is, the less likely someone will wade through it ;-) To give you an idea of how little is needed, I [proposed an edit](https://stackoverflow.com/suggested-edits/3626846) that trims it down to *only* the relevant code (lines 347 to 356) like in the error message. Also linked to the project so folks know where to find the rest of the code if they really need it. – SOS Mar 23 '18 at 00:06
  • Ugh... looks like [the edit](https://stackoverflow.com/suggested-edits/3626846) got rejected, but as the author you can still copy and apply it yourself. – SOS Mar 23 '18 at 14:50
  • @Ageax I've edited the code to leave only the "build" function. I ran into another the problem with this line: $getPassBook()[loc.passMethod](loc.pass); I rewrote it as: loc.dynamicMethod2 = [loc.passMethod]; $getPassBook().loc.dynamicMethod2(loc.pass); But I got this error: The start tag must have a matching end tag. An explicit end tag can be provided by adding . If the body of the tag is empty, you can use the shortcut . – Mrfroddo Mar 23 '18 at 15:13
  • @Mrfroddo - Not the same line. It's a few lines down and has the exact same cause. Try and apply the same change to that line as well. Not related to the error, but .... S.O. takes some getting used to but .. the art of "posting good questions" is about how to make it as clear and easy as possible for others to help, which translates to faster answers. So it's best to leave in the link to the github project. Then anyone can easily download the files from github and try it themselves. Plus since the full source is accessible to anyone, it's okay to just post the 10/15 code lines. – SOS Mar 23 '18 at 15:29
  • See my updated answer. – SOS Mar 23 '18 at 15:43

1 Answers1

1

If it's not working with the latest CF, it might contain syntax specific to another engine, like Lucee. I didn't review the whole thing, but I think this is the line it's complaining about:

 loc.pass[loc.method](loc.fields);

AFAIK, Adobe's ColdFusion doesn't support that kind of dynamic method call. This thread describes a hack that might work around the limitation. Basically, split it into two actions. Store the method reference in a variable. Then invoke the method on that variable. Example:

loc.dynamicMethod = loc.pass[loc.method]; // Get method
loc.dynamicMethod(loc.fields); // Invoke it

// ... same issue a few lines down
//$getPassBook()[loc.passMethod](loc.pass);
loc.dynamicMethod = $getPassBook()[loc.passMethod];
loc.dynamicMethod(loc.pass);

Do note the important caveat

The caveat with the method I suggested is that it pulls the method out of the CFC, so it will be running in the context of the calling code, not the CFC instance. Depending on the code in the method, this might or might not matter.

Not having used the component, can't say whether or not it's relevant in this scenario.

SOS
  • 6,430
  • 2
  • 11
  • 29
  • Sorry, just noticed copy paste error in code sample. It should be `loc.dynamicMethod = loc.pass[loc.method]`. – SOS Mar 23 '18 at 15:39
  • Updated the question to reflect your suggestion but got the same error. – Mrfroddo Mar 23 '18 at 15:59
  • I don't see the update in the OP code. It still shows the old typo for both lines, ie `loc.dynamicMethod = [loc.method];` instead of `loc.dynamicMethod = loc.pass[loc.method]`. – SOS Mar 23 '18 at 16:56
  • Actually ... it looks like both lines [in the OP](https://stackoverflow.com/q/49438460/8895292) are incorrect. Replace your code with two examples above. It successfully compiles in CF11. – SOS Mar 23 '18 at 17:22
  • I changed it and I'm running it on cf2016. It complains on this line: 318 : var loc = {}; – Mrfroddo Mar 23 '18 at 20:43
  • Works fine with CF11, so it's likely a difference in your code. Unfortunately I've no idea without seeing the whole thing. It's too much for S.O. post it in something like gist, then post a link here. – SOS Mar 23 '18 at 20:57