2

Good Afternoon,

I am attempting to create a socket program which connects to Apple Push Notification Service (APNS) and sends a push notification.

I am wondering if anyone can help me with the actual writing of the APNS notification.

Below you can see my program which I used Scott Klement's Socket Programming tutorial to help me with.

The code that is probably relevant to all of this is marked as 'APNS Notification Format Begin', 'Initialize', and 'Formulate message to APNS' in comments although I included the entire program for reference.

I have read the Provider Requirements on Apple's site Provider Requirements but I am still having trouble getting everything working.

My program compiles and when I debug it / run through the steps I get past establishing an actual connection so I 'think' that this part is OK.

What I am hoping is to get back an error identifier from Apple in the variable errid within the item3 data structure. Apple states in the Provider Requirements that they return a status code if there is an error but that particular variable remains at 1077952576

There are also quite a few other things that I am unsure of

1) I have been given the device token as a Base64 string. The string is 40 some odd characters long but from what I understand, Apple states device token length should be 32 bytes. In RPGLE doesn't 1 character represent 1 byte? If that's the case then I couldn't just declare my variable token as

D   token                       32a

because it would get cut off?

2) Is anyone able to tell me what I am doing wrong when it comes back to receiving a proper error identifier from Apple in my variable errid?

Any help would be greatly appreciated

H DFTACTGRP(*NO) BNDDIR('QC2LE')
D/copy socket_h
D/copy gskssl_h

*==============================================================*
*  APNS Notification Format Begin                               
*==============================================================*
D request         s           1000a   varying

D framehdr        ds                         
D   command                      3I 0 inz(2) 
D   framelen                    10I 0        

D framedta        s            500a   varying          

D item1           ds                                   
D   itemid1                      3I 0 inz(1)           
D   itemlen1                     5I 0 inz(%size(token))
D   token                       64a    

D item2           ds                                   
D   itemid2                      3I 0 inz(2)           
D   itemlen2                     5I 0                  
D   payload                    100a   varying          

D item3           ds                                   
D   itemid3                      3I 0 inz(3)           
D   itemlen3                     5I 0 inz(%size(errid))
D   errid                       10I 0

D item4           ds                                      
D   itemid4                      3I 0 inz(4)              
D   itemlen4                     5I 0 inz(%size(expire))  
D   expire                      10I 0 inz(0)              

D item5           ds                                      
D   itemid5                      3I 0 inz(5)              
D   itemlen5                     5I 0 inz(%size(priority))
D   priority                    10I 0 inz(10)    

*==============================================================*
*  APNS Notification Format End                                 
*==============================================================*         

D gsk_strerror    PR              *   extproc('gsk_strerror')
D  gsk_ret_value                10I 0 value                  

D CreateEnv       PR                  like(gsk_handle)       

D ConnSock        PR            10I 0                        
d   host                       256A   const                  
D   port                        10I 0 value                  

D UpgradeSock     PR                  like(gsk_handle)      
D    SslEnv                           like(gsk_handle) value
D    sock                       10I 0 value          

D CloseSsl        PR                                        
D    Handle                           like(gsk_handle) value

D CloseSslEnv     PR                                        
D    SslEnv                           like(gsk_handle) value

D ReportError     PR                                     

D EscapeMsg       PR          

D errMsg          s             80A   varying            
D CRLF            c                   x'0d25'            
D env             s                   like(gsk_handle)   
D s               s             10I 0                    
D connto          ds                  likeds(sockaddr_in)
D SslSock         s                   like(gsk_handle)    
D cmd             s            400A  
D len             s             10I 0
D bytesSent       s             10I 0
D Reply           s           1000A  
D bytesRead       s             10I 0
D left            s             10I 0
D buf             s               *  
D received        s             10I 0
D dataPos         s             10I 0
D wait            s              1A  
D rc              s             10I 0

/free                     

// Initialize
token = 'MyDevToken';
payload = '{"aps":{"alert":"You have mail"}}';         
itemlen1 = %len(payload);                              
framedta = item1 + item2 + item3 + item4 + item5;      
framelen = %len(framedta);                             

request = framehdr + framedta;                         

// Create SSL Environment

env = CreateEnv();       
If (env = *NULL);        
   EscapeMsg();          
Endif;               

// Connect a socket to an SSL server (using normal socket calls)
// NOTE: Sandbox is the development environment                 

s = ConnSock('gateway.sandbox.push.apple.com': 2195);        

// Upgrade the socket to SSL                                    

SSLSock = UpgradeSock(env: s);                                  
If (SSLSock = *NULL);   
   EscapeMsg();
Endif;                                                    

// **** Formulate message to APNS ******
len = %len(%trimr(request));

callp gsk_secure_soc_write(SSLSock
                          : %addr(request)
                          : len
                          : bytesSent); 



 // Close everything and end the program

 CloseSsl(SslSock);                  
 CloseSslEnv(Env);                   

 *inlr = *on;                           

 /end-free       


*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* CreateEnv(): Create an SSL environment for client sockets 
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P CreateEnv       B                                          
D CreateEnv       PI                  like(gsk_handle)       
D rc              s             10I 0                        
D SslEnv          s                   like(Gsk_handle)       

/free                                                        

// Create an SSL environment with default values:           

rc = gsk_environment_open(SslEnv);                         
If (rc <> GSK_OK);                                         
 errMsg = %str(gsk_strerror(rc));                        
 return *NULL;                                           
Endif;                                                     

// Instruct environment to use the *SYSTEM certificate store

rc = gsk_attribute_set_buffer( SslEnv                      
                           : GSK_OS400_APPLICATION_ID    
                           : 'SUMITOMO_APNS_PUSH'        
                           :0 ); 
If (rc<>GSK_OK);
   errMsg = %str(gsk_strerror(rc));
   gsk_environment_close( SslEnv );
   return *NULL;
Endif;

//Tell the environment that this is a client connection
rc = gsk_attribute_set_enum( SslEnv
     : GSK_SESSION_TYPE     
     : GSK_CLIENT_SESSION );

If (rc <> GSK_OK);                 
   errMsg = %str(gsk_strerror(rc));
   gsk_environment_close( SslEnv );
   return *NULL;                   
Endif;                             

// Activate the new environment     

rc = gsk_environment_init( SslEnv );
If (rc <> GSK_OK);                  
   errMsg = %str(gsk_strerror(rc)); 
   gsk_environment_close( SslEnv ); 
   return *NULL;                    
Endif;  

return SslEnv;

/end-free
P            E             

*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* ConnSock(): Create a TCP Socket and connect to a host     
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P ConnSock        B                                          
D ConnSock        PI            10I 0                        
d   host                       256A   const                  
D   port                        10I 0 value                  
D s               s             10I 0                        
D addr            s             10U 0                        

/free                                                           

// look up host                           

addr = inet_addr(%trim(host));            
If (addr = INADDR_NONE);                  
   p_hostent = gethostbyname(%trim(host));
If (p_hostent = *NULL);                
   errMsg = 'Host not found!';         
   EscapeMsg();                        
Endif;                                 
   addr = h_addr;                         
Endif;

// Create a socket                           

s = socket(AF_INET: SOCK_STREAM: IPPROTO_IP);
If (s < 0);                              
   ReportError();                        
Endif;

// connect to the host                                

connto = *ALLx'00';                                
connto.sin_family = AF_INET;                       
connto.sin_addr   = addr;                          
connto.sin_port   = port;                          

If (connect(s: %addr(Connto): %size(connto)) = -1);
  callp close(S);                                 
  ReportError();                                  
Endif;                                             

return s;
/end-free
P             E

*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* UpgradeSock():  Upgrade a socket to use SSL               
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P UpgradeSock     B                                          
D UpgradeSock     PI                  like(gsk_handle)       
D    SslEnv                           like(gsk_handle) value 
D    sock                       10I 0 value                  
D Handle          s                   like(Gsk_handle)       

/free     

rc = gsk_secure_soc_open(SslEnv: Handle);    
If (rc <> GSK_OK);                 
   errMsg = %str(gsk_strerror(rc));
   return *NULL;                   
Endif; 

rc = gsk_attribute_set_numeric_value(Handle
     : GSK_HANDSHAKE_TIMEOUT
     : 30 );

If (rc <> GSK_OK);                 
   errMsg = %str(gsk_strerror(rc));
   gsk_secure_soc_close(Handle);   
   return *NULL;    
Endif;

rc = gsk_secure_soc_init( Handle );
If (rc <> GSK_OK);                 
   errMsg = %str(gsk_strerror(rc));
   gsk_secure_soc_close(Handle);   
   return *NULL;                   
Endif;         

return Handle;
/end-free
P             E

*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* CloseSsl():  Close an SSL socket                          
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P CloseSsl        B                                          
D CloseSsl        PI                                         
D    Handle                           like(gsk_handle) value 
/free                                                       
   gsk_secure_Soc_close( handle);                        
/end-free                                                   
P                 E    

*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* CloseSslEnv():  Close SSL Environment                     
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P CloseSslEnv     B                                          
D CloseSslEnv     PI                                         
D    SslEnv                           like(gsk_handle) value 
/free                                                       
   gsk_environment_close( SslEnv );                      
/end-free
P     E         

*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* EscapeMsg(): Send an escape message w/reason for SSL failure      
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P EscapeMsg       B                                                  
D EscapeMsg       PI                                                 
D SndPgmMsg       PR                  ExtPgm('QMHSNDPM')             
D   MessageID                    7A   Const                          
D   QualMsgF                    20A   Const                          
D   MsgData                    256A   Const                          
D   MsgDtaLen                   10I 0 Const                          
D   MsgType                     10A   Const                          
D   CallStkEnt                  10A   Const                          
D   CallStkCnt                  10I 0 Const                          
D   MessageKey                   4A                                  
D   ErrorCode                    1A        

D ErrorCode       DS                        
D  BytesProv                    10I 0 inz(0)
D  BytesAvail                   10I 0 inz(0)  

D wwTheKey        S              4A 

/free
SndPgmMsg( 'CPF9897'
: 'QCPFMSG   *LIBL'   
: errMsg              
: %len(%trimr(errMsg))
: '*ESCAPE'           
: '*CTLBDY'           
: 1                   
: wwTheKey            
: ErrorCode );
/end-free
P          E

*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* ReportError():  Send an escape message explaining any errors    
*                 that occurred.                                  
*                                                                 
*  This function requires binding directory QC2LE in order        
*  to access the __errno() function.                              
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
P ReportError     B                                                
D ReportError     PI                                               

D get_errno       PR              *   ExtProc('__errno')           
D ptrToErrno      s               *                                
D errno           s             10I 0 based(ptrToErrno)      

D QMHSNDPM        PR                  ExtPgm('QMHSNDPM')
D   MessageID                    7A   Const             
D   QualMsgF                    20A   Const             
D   MsgData                      1A   Const             
D   MsgDtaLen                   10I 0 Const             
D   MsgType                     10A   Const             
D   CallStkEnt                  10A   Const             
D   CallStkCnt                  10I 0 Const             
D   MessageKey                   4A                     
D   ErrorCode                 8192A   options(*varsize)

D ErrorCode       DS                  qualified
D  BytesProv              1      4I 0 inz(0)   
D  BytesAvail             5      8I 0 inz(0)   

D MsgKey          S              4A
D MsgID           s              7A

/free 

ptrToErrno = get_errno();    
MsgID = 'CPE' + %char(errno);

QMHSNDPM( MsgID
 : 'QCPFMSG   *LIBL'
 : ' '              
 : 0                
 : '*ESCAPE'        
 : '*PGMBDY'        
 : 1     
 : MsgKey              
 : ErrorCode         );           
 /end-free
 P          E
  • 1
    What errors are you getting? You should take a look at Scott Klement's HTTP API and maybe borrow the debug logging if nothing else. – Charles Apr 01 '15 at 22:47
  • Hey Charles, thanks for your response. My declaration of the variable cmd currently is a character field. At this point I'm just unsure what to declare it as. Should I be creating variables for each part of the notification (Command, frame length, frame data, etc) as binary fields? We've never used binary fields in our applications so I'm unfamiliar with that. Also I've looked at Scott's HTTPAPI but it looks like it only works with URLS that begin with HTTPS as far as SSL goes. I've tried to use it in conjunction with the apple URL I have in my code and receive errors along those lines 2) – Shadowstep2003 Apr 02 '15 at 12:42
  • Yeah, I don't think HTTP API level interface would do you much good. But underneath that, Scott has defined a TCP communication interface that you may be able to use directly. See COMMSSLR4, COMMTCPR4. – Charles Apr 02 '15 at 12:57

1 Answers1

1

This isn't even close, you're using characters instead of binary data.

cmd = '2' + 3 + 1 + 64 + 'x' + 2 + 50 + '{"aps":{"alert":"You have mail"}} +
      3 + 3 + '001'

This should be closer; but it is off the top of my head and completely untested...

 d request         s           1000a   varying

 d frame_hdr       ds
 d  cmd                           1a   inz(x'02')
 d  frame_len                    10i 0

 d frame_data      s            500a   varying

 d device_item     ds 
 d                                1a   inz(x'01')
 d                                5i 0 inz(%size(device_item))
 d  token                        32a 

 d item_hdr        ds
 d  id                            1a   inz(x'02')
 d  item_len                      5i 0


 d item_data       s            100a   varying

   item_data = '{"aps":{"alert":"You have mail"}}';
   item_len = %len(item_data);

   token = myDevID;
   frame_data = device_item + item_hdr + item_data;
   frame_len = %len(frame_data);

   request = frame_hdr + frame_data;

   callp gsk_secure_soc_write(SSLSock
                      : %addr(request:*DATA)
                      : %len(request)
                      : bytesSent); 

I've not done raw sockets on the IBM i before, you may have to convert EBCDIC to ASCII. I don't think you'll need to worry about little vs. big endian.

RPG is probably not the best choice for this, Java might be a better choice given the existence of projects like Java PNS

Stackoverflow doesn't have a whole lot of RPG / IBM i traffic. You may get a better response on the following:

Charles
  • 21,637
  • 1
  • 20
  • 44
  • Thank you Charles, I will also review some of the links you provided. What you said makes sense and I have revised the code in my program and in my original post to reflect that. Right now I am working on trying to receive a legitimate error message back from Apple since we are still not getting any notifications in our app I will continue to keep this post updated on any new occurrences. – Shadowstep2003 Apr 02 '15 at 19:17