Monday, April 13, 2009

APNS : How to generate JSON payload in C

For the communication program with APNS, you have many implementation choices, either php, perl, python, ruby or even C#.

You may wonder why the sample of raw interface given by Apple is a C function.
static bool sendPayload(SSL *sslPtr, char *deviceTokenBinary, char *payloadBuff, size_t payloadLength)

Would there be many implementations that use C /C++ or Objective C ? I guess the number will be increasing if the developer is looking for scalability and performance.

If you would like to implement it in C variant, you need to implement a raw TLS/SSL socket program (and may be with threading) and the handling of JSON payload. One of the possibilities is to use open source JSON C Library, but I think it is too heavy to use it here as the communication program needs to construct the Payload message only. So I write this function (genPayloadData ) to generate the payload message for the APNS.

This source code includes the JSON escape string function and the C structure to generate the APNS Payload.

apns.c Select all

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <stdbool.h>

#define Debug 1
#define logerror dprintf
#define dprintf if (Debug) printf

#define DEVICE_BINARY_SIZE 32
#define MAXPAYLOAD_SIZE 256

typedef struct
{
char* alert;
int badge;
char* sound;
char* name_key[4]; //custom key
char* str_value[4]; // custom key value
int int_value[4]; // custom key int
char* action_loc_key; // for custom button label
char* loc_key; // formatted localized string
char* loc_args[4]; // formatted localized string arguments
} PayloadData;

/* string escaping */
const char *json_number_chars = "0123456789.+-eE";
const char *json_hex_chars = "0123456789abcdef";

char *json_escape_str(char *str)
{
char *results = (char*)malloc(200);
char *resultsPt = results;
int pos = 0, start_offset = 0;
unsigned char c;
do {
c = str[pos];
switch(c) {
case '\0':
break;
case '\b':
case '\n':
case '\r':
case '\t':
case '"':
case '\\':
case '/':
if(pos - start_offset > 0)
{memcpy(resultsPt, str + start_offset, pos - start_offset); resultsPt+=pos - start_offset;}
if(c == '\b') {memcpy(resultsPt, "\\b", 2); resultsPt+=2;}
else if(c == '\n') {memcpy(resultsPt, "\\n", 2); resultsPt+=2;}
else if(c == '\r') {memcpy(resultsPt, "\\r", 2); resultsPt+=2;}
else if(c == '\t') {memcpy(resultsPt, "\\t", 2); resultsPt+=2;}
else if(c == '"') {memcpy(resultsPt, "\\\"", 2); resultsPt+=2;}
else if(c == '\\') {memcpy(resultsPt, "\\\\", 2); resultsPt+=2;}
else if(c == '/') {memcpy(resultsPt, "\\/", 2); resultsPt+=2;}
start_offset = ++pos;
break;
default:
if(c < ' ') {
if(pos - start_offset > 0)
{memcpy(resultsPt, str + start_offset, pos - start_offset); resultsPt+=pos-start_offset;}
sprintf(resultsPt, "\\u00%c%c",
json_hex_chars[c >> 4],
json_hex_chars[c & 0xf]);
start_offset = ++pos;
} else pos++;
}
} while(c);
if(pos - start_offset > 0)
{memcpy(resultsPt, str + start_offset, pos - start_offset); resultsPt+=pos-start_offset;}
memcpy(resultsPt, "\0", 1);
dprintf("results:%s\n",results);
return results;
return 0;
}



#define APSHEAD "{\"aps\":{"
#define APSTAIL "}"


void genPayloadData(PayloadData myPaylodData, char *msgbuf) {
char *alert = (char*)malloc(200);
char *message = (char*)malloc(200);
char *sound = (char*)malloc(20);
char *badge = (char *)malloc(20);
char *msgbufPt = msgbuf;
bool isAlert=false, isBadge=false, isSound=false;
bool isToken=false;
int len;
int i;

if (myPaylodData.alert) {
if (myPaylodData.action_loc_key) {
sprintf(alert, "\"alert\":{\"body\":\"%s\",\"action-loc-key\":\"%s\"}",json_escape_str(myPaylodData.alert),json_escape_str(myPaylodData.action_loc_key));
}
else {
sprintf(alert, "\"alert\":\"%s\"",json_escape_str(myPaylodData.alert));
}
isAlert = true;
} else if (myPaylodData.loc_key) {
isToken=false;
sprintf(alert, "\"alert\":{\"loc-key\":\"%s\"",json_escape_str(myPaylodData.loc_key));
for (i=0 ; i < 4; i++) {
if (myPaylodData.loc_args[i]) {
if (isToken) {
sprintf(alert, "%s,\"%s\"",alert,json_escape_str(myPaylodData.loc_args[i]));
}
else {
sprintf(alert, "%s,\"loc-args\":[\"%s\"",alert,json_escape_str(myPaylodData.loc_args[i]));
}
isToken=true;
}
}
if (isToken) {
sprintf(alert, "%s]}",alert);
}
isAlert = true;
}
if (myPaylodData.badge > 0) {
sprintf(badge, "\"badge\":%d",myPaylodData.badge);
isBadge = true;
}
if (myPaylodData.sound) {
sprintf(sound, "\"sound\":\"%s\"",myPaylodData.sound);
isSound = true;
}
if (isAlert | isBadge | isSound) {
len = strlen(APSHEAD);
memcpy(msgbufPt, APSHEAD, len);
msgbufPt += len;
}
else {
memcpy(msgbufPt, "{", 1);
msgbufPt += 1;
}
if (isAlert) {
len = strlen(alert);
memcpy(msgbufPt, alert, len);
msgbufPt += len;
}
if (isBadge) {
if (isAlert) {
memcpy(msgbufPt++, ",", 1);
}
len = strlen(badge);
memcpy(msgbufPt, badge, len);
msgbufPt += len;
}
if (isSound) {
if (isAlert | isBadge) {
memcpy(msgbufPt++, ",", 1);
}
len = strlen(sound);
memcpy(msgbufPt, sound, len);
msgbufPt += len;
}
if (isAlert | isBadge | isSound) {
len = strlen(APSTAIL);
memcpy(msgbufPt, APSTAIL, len);
msgbufPt += len;
isToken = true;
}
isToken=false;
if (msgbufPt-msgbuf < MAXPAYLOAD_SIZE) {
for (i=0; i < 4; i++) {
if (myPaylodData.name_key[i]) {
if (myPaylodData.str_value[i]) {
sprintf(message, "%s\"%s\":\"%s\"",(isToken?",":""),json_escape_str(myPaylodData.name_key[i]),json_escape_str(myPaylodData.str_value[i]));
len = strlen(message);
if (msgbufPt-msgbuf+len<MAXPAYLOAD_SIZE) {
memcpy(msgbufPt, message, len);
msgbufPt += len;
isToken = true;
}
else {
dprintf("\n!!!!Warnings: Total Payload message overlimit (>%d) when processing %s",MAXPAYLOAD_SIZE,message);
}
}
if (myPaylodData.int_value[i]) {
sprintf(message, "%s\"%s\":%d",(isToken?",":""),json_escape_str(myPaylodData.name_key[i]),myPaylodData.int_value[i]);
len = strlen(message);
if (msgbufPt-msgbuf+len<MAXPAYLOAD_SIZE) {
memcpy(msgbufPt, message, len);
msgbufPt += len;
isToken = true;
}
else {
dprintf("\n!!!!Warnings: Total Payload message overlimit (>%d) when processing %s",MAXPAYLOAD_SIZE,message);
}
}
}
}
}
len = strlen(APSTAIL);
memcpy(msgbufPt, APSTAIL, len);
msgbufPt += len;
memcpy(msgbufPt, "\0", 1);
dprintf("\nconstructed message:%s\n",msgbuf);
if (strlen(msgbuf) > MAXPAYLOAD_SIZE) {
dprintf("\n!!!!Warnings: Payload (>%d) : %d\n",MAXPAYLOAD_SIZE,(unsigned)strlen(msgbuf));
}
else {
dprintf("\nPayload (<=%d) size : %d\n",MAXPAYLOAD_SIZE,(unsigned)strlen(msgbuf));
}
free(alert);
free(badge);
free(sound);
free(message);
}

int main(){
char msgbuf[512]; /* payload messages */

PayloadData myPaylodData = {0};
myPaylodData.badge = 3;
myPaylodData.alert = "Message from javacom";
// myPaylodData.action_loc_key = "";
// myPaylodData.loc_args[0] = "";
// myPaylodData.loc_args[1] = "";
// myPaylodData.loc_args[2] = "";
myPaylodData.sound = "received3.caf";
myPaylodData.name_key[0] = "test1";
myPaylodData.str_value[0] = "Hello iPhone";
genPayloadData(myPaylodData,msgbuf);
printf("%s\n",msgbuf);
return 0;

}


 
 
 

No comments: