Ady Wicaksono Daily Activities

Archive for the ‘Programming’ Category

function convert8bitTo7bit with PHP

with one comment

As per GSM 03.40 we can send up to 140 octets (8-bit data), so if we send 7-bit data (septet) we can send up to 160 7-bit ASCII characters. Few days ago I need a function to convert a string to septet hexadecimal representation with PHP without any luck. I tried googling with keyword “convert 8bit to 7bit PHP”, “convert octet to septet PHP”, and similar keyword with no luck.

So I managed to create my own function that works fine so far (hopefully) (but I don’t care if my algo is not optimal nor bad:)) below:


<?
function strToHex($string)
{
    $hex='';
    for ($i=0; $i < strlen($string); $i++)
    {
        $hex .= dechex(ord($string[$i]));
    }
    return $hex;
}
function hexToStr($hex)
{
    $string='';
    for ($i=0; $i < strlen($hex)-1; $i+=2)
    {
        $string .= chr(hexdec($hex[$i].$hex[$i+1]));
    }
    return $string;
}

function hexbin($hex){
    $bin='';
    for($i=0;$i<strlen($hex);$i++)
        $bin.=str_pad(decbin(hexdec($hex{$i})),4,'0',STR_PAD_LEFT);
       return $bin;
} 

function binhex($bin){
    $hex='';
    for($i=strlen($bin)-4;$i>=0;$i-=4)
        $hex.=dechex(bindec(substr($bin,$i,4)));
   return strrev($hex);
}

function Convert8BitTo7Bit($string){
	// Convert String to Hex first
	// E.g *135# will be converted to 2A 31 33 35 23
	$string = strToHex($string);
	// print   "STR = $string\n";
	$total = "";
	for($i = 0; $i < strlen($string); ){
		// Get 1st character string, it's 2 character hex
		$X = $string[$i++].$string[$i++];
		// Convert it to binary
		$my8bit = hexbin($X);
		//print "(8bit) ==> $my8bit\n";
		// remove left side of octet, it shall be septet
		// e.g 2A in octet is 00101010 (8 bit), remove most left 0 --> 0101010 (7 bit)
		$my7bit = substr($my8bit,1,7);
		//print "(7bit) ==>  $my7bit\n";
        // Concatenate it
		$total = $my7bit.$total;
	}
	// Padding the string
	if(strlen($total) % 8 != 0){
		$p1     = (intval((strlen($total) / 8)) + 1) *  8;
		$total  = str_pad($total,$p1,'0',STR_PAD_LEFT);
	}
	$pad   = 7;
	// Conversion begin
	for($i = strlen($total) - 1; $i >= 0 ; $i--){
		$mypad[$pad--] = $total[$i];
		if($pad < 0 || $i <= 0){
			$pad  = 7;
			$tmp1 = array_reverse($mypad);
			//print_r($tmp1);
			$tmp2 = implode($tmp1);
			$res = binhex($tmp2);
			$result .= "$res";
		}
	}
	return $result;
}


?>


To use that code, we simply call the function like this

print Convert8BitTo7Bit("*135#")."\n";

It will print “AAD8AC3602” which represent hexcode to be send on top of TP-UD GSM 03.40

Another wxample: we have 160 characters to send like below
“Test SMS content 160 characters will be displayed as 140 octets. Test SMS content 160 characters will be displayed as 140 octets.Test SMS content 160 characters”

With calling

print Convert8BitTo7Bit("Test SMS content 160 characters will be displayed as 140 octets. Test SMS content 160 characters will be displayed as 140 octets.Test SMS content 160 characters");

We get 140 octets/bytes hexadecimal code for this:


d4f29c0e9a36a7a0f1db4d2fbbe9a0980d061aa3c3f2f0985e96cf41f7349b0d129741e4f41cce0ee7cb6450780e8ad160a0f7985ea6cf5d206a794e074d9b53d0f8eda697dd7450cc06038dd16179784c2fcbe7a07b9acd0689cb20727a0e6787f36532283c07c56830d07b4c2fd3e72e6a794e074d9b53d0f8eda697dd7450cc06038dd16179784c2fcbe7

Anyway, there’s 1 thing missing here:
as per GSM 03.38

If the total number of characters to be sent equals (8n 1) where n=1,2,3 etc. then there are 7 spare bits at the end of the message. To avoid the situation where the receiving entity confuses 7 binary zero pad bits as the @ character, the carriage return or character (defined in clause 6.1.1) shall be used for padding in this situation, just as for Cell Broadcast.
If is intended to be the last character and the message (including the wanted ) ends on an octet boundary, then another must be added together with a padding bit 0. The receiving entity will perform the carriage return function twice, but this will not result in misoperation as the definition of in clause 6.1.1 is identical to the definition of .
The receiving entity shall remove the final character where the message ends on an octet boundary with as the last character.

So please fix this function and if the latest one is 0x00, replace it with 0X1A (Carriage Return) 😉

Written by adywicaksono

December 24, 2009 at 4:24 pm

How to patch kannel to support additional vendor specific TLV case study Opera SMPP

with 3 comments

Last time I need to patch my kannel, because mblox (www.mblox.com) use additional specific vendor parameters.
This article will tell you how to do it? This article is based on experience and this email archieve:

http://www.mail-archive.com/devel@kannel.org/msg05603.html

Rather than using Mblox, we will now patch kannel to connect to Opera SMPP, it just another SMPP server
which add some additional parameters:


1. TLV_OPERA_CAMPAIGNID with value 0x1400, type octet string
2. TLV_OPERA_REFERENCE with value 0x1401, type octet string
3. TLV_OPERA_CHANNEL with value 0x1402, type octet string

For more information about Opera SMPP visit http://www.oitg.com/

Ok follow these steps
* Change file gw/bb_alog.c
This file purpose is “encapsulate custom access log logic and escape code parsing”
For our case we want to add 3 additional custom parameters as defined above.
Look at function “static Octstr *get_pattern(…)”, you find that some %x notation is already used, e.g


* %l - log message
* %i - smsc-id
* %n - service-name (for MO) or sendsms-user (for MT)
* %A - account

Now we add unused one, which is:


* %z - Opera campaign id
* %y - Opera reference
* %w - Opera channel

Now, there’s a line around line 301:

/* XXX add more here if needed */

then you add these lines

———– cut here ————

// Since Opera Parameter contains NULL char, we don't need to log 0x00/NULL char
case 'z': /* Opera Campaign Id */
     if (octstr_len(msg->sms.opera_campaign_id)) {
            for (i = 0; i < octstr_len(msg->sms.opera_campaign_id); ++i) {
                if (octstr_get_char(msg->sms.opera_campaign_id, i) != 0x00)
                    octstr_append_char(result, octstr_get_char(msg->sms.opera_campaign_id, i));
            }

     }
     break;
case 'y': /* Opera Reference */
     if (octstr_len(msg->sms.opera_reference)) {
            for (i = 0; i < octstr_len(msg->sms.opera_reference); ++i) {
                if (octstr_get_char(msg->sms.opera_reference, i) != 0x00)
                    octstr_append_char(result, octstr_get_char(msg->sms.opera_reference, i));
            }
     }
     break;
case 'w': /* Opera Channel */
     if (octstr_len(msg->sms.opera_channel)) {
            for (i = 0; i < octstr_len(msg->sms.opera_channel); ++i) {
                if (octstr_get_char(msg->sms.opera_channel, i) != 0x00)
                    octstr_append_char(result, octstr_get_char(msg->sms.opera_channel, i));
            }

     }
     break;

———– cut here ————

* Change file gw/msg-decl.h – a message declarations file

You see these lines?


104 OCTSTR(charset);
105 OCTSTR(boxc_id);
106 OCTSTR(binfo);
107 INTEGER(msg_left);

Just add after INTEGER(msg_left); this line:
——— cut here ———-

OCTSTR(opera_campaign_id);
OCTSTR(opera_reference);
OCTSTR(opera_channel);

——— cut here ———-

OCTSTR declare that we have octet string as additional parameter, meanwhile if you have
additional parameter which is integer value use INTEGER() instead of OCTSTR().

* Change file gw/smsc/smpp_pdu_opt.def, which contains PDU definition

You add these lines before the latest #endif

—- cut here —

#define SMPP_opera_campaign_id 0x1400
#define SMPP_opera_reference 0x1401
#define SMPP_opera_channel 0x1402

—- cut here —

* Change file gw/smsc/smsc_smpp.c (SMPP v3.3 and v3.4 implementation file)

Find function “static Msg *pdu_to_msg()” and find these linese (should be around line no. 458):


458 msg->sms.receiver = pdu->u.deliver_sm.destination_addr;
459 pdu->u.deliver_sm.destination_addr = NULL;
460
461
462 /* SMSCs use service_type for billing information */
463 msg->sms.binfo = pdu->u.deliver_sm.service_type;
464 pdu->u.deliver_sm.service_type = NULL;

You add on lines between 459 and 462 these lines:

—- cut here —-


/* Check for Opera Campaign ID */
if (smpp->version > 0x33 && octstr_len(pdu->u.deliver_sm.opera_campaign_id)) {
    msg->sms.opera_campaign_id = pdu->u.deliver_sm.opera_campaign_id;
    pdu->u.deliver_sm.opera_campaign_id = NULL;
}

/* Check for Opera Reference */
if (smpp->version > 0x33 && octstr_len(pdu->u.deliver_sm.opera_reference)) {
    msg->sms.opera_reference = pdu->u.deliver_sm.opera_reference;
    pdu->u.deliver_sm.opera_reference = NULL;
}

/* Check for Opera Channel */
if (smpp->version > 0x33 && octstr_len(pdu->u.deliver_sm.opera_channel)) {
    msg->sms.opera_channel = pdu->u.deliver_sm.opera_channel;
    pdu->u.deliver_sm.opera_channel = NULL;
}

—- cut here —-

Find also function “static Msg *data_sm_to_msg()” and find these lines inside this function:


637 msg->sms.receiver = pdu->u.data_sm.destination_addr;
638 pdu->u.data_sm.destination_addr = NULL;
639
640 /* SMSCs use service_type for billing information */
641 msg->sms.binfo = pdu->u.data_sm.service_type;
642 pdu->u.data_sm.service_type = NULL;

Should be around lines 637-642, you put the similar lines between 638 – 640 with these lines:

—- cut here —-

/* Check for Opera Campaign ID */
if (smpp->version > 0x33 && octstr_len(pdu->u.data_sm.opera_campaign_id)) {
   msg->sms.opera_campaign_id = pdu->u.data_sm.opera_campaign_id;
   pdu->u.data_sm.opera_campaign_id = NULL;
}

/* Check for Opera Reference */
if (smpp->version > 0x33 && octstr_len(pdu->u.data_sm.opera_reference)) {
   msg->sms.opera_reference = pdu->u.data_sm.opera_reference;
   pdu->u.data_sm.opera_reference = NULL;
}

/* Check for Opera Channel */
if (smpp->version > 0x33 && octstr_len(pdu->u.data_sm.opera_channel)) {
   msg->sms.opera_channel = pdu->u.data_sm.opera_channel;
   pdu->u.data_sm.opera_channel = NULL;
}

—- cut here —-

Find this function “static SMPP_PDU *msg_to_pdu(…)”, and you should see
these lines

    966     /* set more messages to send */
    967     if (smpp->version > 0x33 && msg->sms.msg_left > 0)
    968         pdu->u.submit_sm.more_messages_to_send = 1;

Just add after that part, these lines:
—— cut here —–

    // Each Opera Param STring must be added 0x00 or NULL chars
    /* set Opera campaign id */
    if (smpp->version > 0x33 && octstr_len(msg->sms.opera_campaign_id)) {
        pdu->u.submit_sm.opera_campaign_id = octstr_duplicate(msg->sms.opera_campaign_id);
        // Append NULL
        octstr_append_char(pdu->u.submit_sm.opera_campaign_id,0x00);
    }
    /* set Opera reference */
    if (smpp->version > 0x33 && octstr_len(msg->sms.opera_reference)) {
        pdu->u.submit_sm.opera_reference = octstr_duplicate(msg->sms.opera_reference);
        octstr_append_char(pdu->u.submit_sm.opera_reference,0x00);
    }
    /* set Opera channel */
    if (smpp->version > 0x33 && octstr_len(msg->sms.opera_channel)) {
        pdu->u.submit_sm.opera_channel = octstr_duplicate(msg->sms.opera_channel);
        octstr_append_char(pdu->u.submit_sm.opera_channel,0x00);
    }

—— cut here —–

* Change file gw/urltrans.c (URL translations file)
Find function name: “Octstr *urltrans_fill_escape_codes(Octstr *pattern, Msg *request)”
and find this lines:

    600     case 'B':  /* billing identifier/information */
    601         if (octstr_len(request->sms.binfo)) {
    602             enc = octstr_duplicate(request->sms.binfo);
    603             octstr_url_encode(enc);
    604             octstr_append(result, enc);
    605             octstr_destroy(enc);
    606         }
    607         break;
    608

Just add these lines on those “switch() case:”
—- cut here —-


       case 'z':  /* Opera Campaign Id */
           if (octstr_len(request->sms.opera_campaign_id)) {
               enc = octstr_duplicate(request->sms.opera_campaign_id);
               octstr_url_encode(enc);
               octstr_append(result, enc);
               octstr_destroy(enc);
           }
           break;
        case 'y':  /* Opera Reference */
           if (octstr_len(request->sms.opera_reference)) {
               enc = octstr_duplicate(request->sms.opera_reference);
               octstr_url_encode(enc);
               octstr_append(result, enc);
               octstr_destroy(enc);
           }
           break;
        case 'w':  /* Opera Channel */
           if (octstr_len(request->sms.opera_channel)) {
               enc = octstr_duplicate(request->sms.opera_channel);
               octstr_url_encode(enc);
               octstr_append(result, enc);
               octstr_destroy(enc);
           }
           break;

—- cut here —-
* Change file gw/smsc/smpp_pdu.def (definitions of SMPP PDU structure)

Find this line:

     PDU(submit_sm,
         0x00000004,
         HEADER

After you find that line… scroll down and find first appearance of these lines
after that line above:

         TLV_OCTETS(its_session_info, 2, 2)
         TLV_OCTETS(ussd_service_op, 1, 1)
         OPTIONAL_END

Change those lines become

         TLV_OCTETS(its_session_info, 2, 2)
         TLV_OCTETS(ussd_service_op, 1, 1)
         TLV_OCTETS(opera_campaign_id, 0, 128)
         TLV_OCTETS(opera_reference, 0, 128)
         TLV_OCTETS(opera_channel, 0, 128)
	 OPTIONAL_END

We assume each optional parameters has length between 0-128 octet.

Now continue by finding this line:

     PDU(deliver_sm,
         0x00000005,
         HEADER

After you find that line… scroll down and find first appearance of these lines
after that line:

     TLV_INTEGER(message_state, 1)
     TLV_NULTERMINATED(receipted_message_id, 65)
     OPTIONAL_END

Change it to:

     TLV_INTEGER(message_state, 1)
     TLV_NULTERMINATED(receipted_message_id, 65)
     TLV_OCTETS(opera_campaign_id, 0, 128)
     TLV_OCTETS(opera_reference, 0, 128)
     TLV_OCTETS(opera_channel, 0, 128)
     OPTIONAL_END

Now continue by finding this line:

	PDU(data_sm,
	    0x00000103,
	    HEADER

After you find that line… scroll down and find first appearance of these lines
after that line:

    TLV_INTEGER(language_indicator, 1)
    TLV_INTEGER(its_reply_type, 1)
    TLV_OCTETS(its_session_info, 2, 2)
    OPTIONAL_END

Change it to:

    TLV_INTEGER(language_indicator, 1)
    TLV_INTEGER(its_reply_type, 1)
    TLV_OCTETS(its_session_info, 2, 2)
     TLV_OCTETS(opera_campaign_id, 0, 128)
     TLV_OCTETS(opera_reference, 0, 128)
     TLV_OCTETS(opera_channel, 0, 128)
     OPTIONAL_END

* Change gw/smsbox.c (main program of the smsbox)

We’ll change a lot here :), so be patient
1. Modify function

static void get_x_kannel_from_headers(List *headers, Octstr **from,
                                      Octstr **to, Octstr **udh,
                                      Octstr **user, Octstr **pass,
                                      Octstr **smsc, int *mclass, int *mwi,
                                      int *coding, int *compress,
                                      int *validity, int *deferred,
                                      int *dlr_mask, Octstr **dlr_url,
                                      Octstr **account, int *pid, int *alt_dcs,
                                      int *rpi, Octstr **binfo, int *priority)

Add this additional 3 parameters:
1. Octstr **opera_campaign_id,
2. Octstr **opera_reference,
3. Octstr **opera_channel,

so the latest line changed from

     int *rpi, Octstr **binfo, int *priority)

become

     int *rpi, Octstr **binfo,
     Octstr **opera_campaign_id,
     Octstr **opera_reference,
     Octstr **opera_channel,
     int *priority)

Also still on the same function, inside this function find:


        else if (octstr_case_compare(name, octstr_imm("X-Kannel-BInfo")) == 0) {
            *binfo = octstr_duplicate(val);
            octstr_strip_blanks(*binfo);
        }

Add to the same if-else block these lines:


        else if (octstr_case_compare(name, octstr_imm("X-Kannel-Opera_Campaign_Id")) == 0) {
            *opera_campaign_id = octstr_duplicate(val);
            octstr_strip_blanks(*opera_campaign_id);
        }else if (octstr_case_compare(name, octstr_imm("X-Kannel-Opera_Reference")) == 0) {
            *opera_reference = octstr_duplicate(val);
            octstr_strip_blanks(*opera_reference);
        }else if (octstr_case_compare(name, octstr_imm("X-Kannel-Opera_Channel")) == 0) {
            *opera_channel = octstr_duplicate(val);
            octstr_strip_blanks(*opera_channel);
        }

Scroll down and find this function:

static void get_x_kannel_from_xml(int requesttype , Octstr **type, Octstr **body,
                                  List *headers, Octstr **from,
                                  Octstr **to, Octstr **udh,
                                  Octstr **user, Octstr **pass,
                                  Octstr **smsc, int *mclass, int *mwi,
                                  int *coding, int *compress,
                                  int *validity, int *deferred,
                                  int *dlr_mask, Octstr **dlr_url,
                                  Octstr **account, int *pid, int *alt_dcs,
                                  int *rpi, List **tolist, Octstr **charset,
                                  Octstr **binfo, int *priority)

do the same thing, so

     Octstr **binfo, int *priority)

become

     Octstr **binfo,
     Octstr **opera_campaign_id,
     Octstr **opera_reference,
     Octstr **opera_channel,
     int *priority)

Still on the same function, modify the body of function by finding this line inside
this function:

        /* binfo */
        get_tag(tmp, octstr_imm("binfo"), binfo, 0, 0);

Modify to:

        /* binfo */
        get_tag(tmp, octstr_imm("binfo"), binfo, 0, 0);
        get_tag(tmp, octstr_imm("opera_campaign_id"), opera_campaign_id, 0, 0);
        get_tag(tmp, octstr_imm("opera_reference"), opera_reference, 0, 0);
        get_tag(tmp, octstr_imm("opera_reference"), opera_reference, 0, 0);

Scrolldown and find this function

static void fill_message(Msg *msg, URLTranslation *trans,
                         Octstr *replytext, int octet_stream,
                         Octstr *from, Octstr *to, Octstr *udh,
                         int mclass, int mwi, int coding, int compress,
                         int validity, int deferred,
                         Octstr *dlr_url, int dlr_mask, int pid, int alt_dcs,
                         int rpi, Octstr *smsc, Octstr *account,
                         Octstr *charset, Octstr *binfo, int priority)

do the similar thing, so

     Octstr *binfo, int priority)

become

     Octstr *binfo,
     Octstr *opera_campaign_id,
     Octstr *opera_reference,
     Octstr *opera_channel,
     int priority)

Still on the same function, find these lines:

    if (priority != SMS_PARAM_UNDEFINED) {
        if (urltrans_accept_x_kannel_headers(trans))
            msg->sms.priority = priority;
        else
            warning(0, "Tried to change priority to '%d', denied.", priority);
    }

Add after this “if(…){…}” block these lines:

    if (opera_campaign_id) {
        if (urltrans_accept_x_kannel_headers(trans)) {
            msg->sms.opera_campaign_id = opera_campaign_id;
        } else {
            warning(0, "Tried to change opera_campaign_id to '%s', denied.",
                    octstr_get_cstr(opera_campaign_id));
            octstr_destroy(opera_campaign_id);
        }
    }
    if (opera_reference) {
        if (urltrans_accept_x_kannel_headers(trans)) {
            msg->sms.opera_reference = opera_reference;
        } else {
            warning(0, "Tried to change opera_reference to '%s', denied.",
                    octstr_get_cstr(opera_reference));
            octstr_destroy(opera_reference);
        }
    }
    if (opera_channel) {
        if (urltrans_accept_x_kannel_headers(trans)) {
            msg->sms.opera_channel = opera_channel;
        } else {
            warning(0, "Tried to change opera_channel to '%s', denied.",
                    octstr_get_cstr(opera_channel));
            octstr_destroy(opera_channel);
        }
    }

Scroll down find this function: “static void url_result_thread(void *arg)”
On this function, after this variable declaration:

unsigned int queued; /* indicate if processes reply is requeued */

Add this variable declaration:

Octstr *opera_campaign_id, *opera_reference, *opera_channel;

Add after

from = to = udh = smsc = dlr_url = account = binfo = charset = NULL;

this line:

opera_campaign_id = opera_reference = opera_channel = NULL;

Still in this function, modify:

get_x_kannel_from_headers(reply_headers, &from, &to, &udh,
NULL, NULL, &smsc, &mclass, &mwi,
&coding, &compress, &validity,
&deferred, &dlr_mask, &dlr_url,
&account, &pid, &alt_dcs, &rpi,
&binfo, &priority);

to

get_x_kannel_from_headers(reply_headers, &from, &to, &udh,
NULL, NULL, &smsc, &mclass, &mwi,
&coding, &compress, &validity,
&deferred, &dlr_mask, &dlr_url,
&account, &pid, &alt_dcs, &rpi,
&binfo,&opera_campaign_id,&opera_reference,&opera_channel, &priority);

also

get_x_kannel_from_xml(mt_reply, &type, &replytext, reply_headers,
&from, &to, &udh, NULL, NULL, &smsc, &mclass, &mwi,
&coding, &compress, &validity, &deferred, &dlr_mask,
&dlr_url, &account, &pid, &alt_dcs, &rpi, NULL, &charset,
&binfo, &priority);

to

get_x_kannel_from_xml(mt_reply, &type, &replytext, reply_headers,
&from, &to, &udh, NULL, NULL, &smsc, &mclass, &mwi,
&coding, &compress, &validity, &deferred, &dlr_mask,
&dlr_url, &account, &pid, &alt_dcs, &rpi, NULL, &charset,
&binfo, &opera_campaign_id,&opera_reference,&opera_channel,&priority);

Remember we previously change number of parameter for those function.

Scroll down find this function call

fill_message(msg, trans, replytext, octets, from, to, udh, mclass,
mwi, coding, compress, validity, deferred, dlr_url,
dlr_mask, pid, alt_dcs, rpi, smsc, account, charset,
binfo, priority);

change to

fill_message(msg, trans, replytext, octets, from, to, udh, mclass,
mwi, coding, compress, validity, deferred, dlr_url,
dlr_mask, pid, alt_dcs, rpi, smsc, account, charset,
binfo,opera_campaign_id,opera_reference,opera_channel, priority);

Scroll down find this function:
“static int obey_request(Octstr **result, URLTranslation *trans, Msg *msg)”
Inside this function find these lines:

if (octstr_len(msg->sms.binfo)) {
Octstr *os;
os = octstr_duplicate(msg->sms.binfo);
http_header_add(request_headers, "X-Kannel-BInfo",
octstr_get_cstr(os));
octstr_destroy(os);
}

Add after those if(){…} block:


       if (octstr_len(msg->sms.opera_campaign_id)) {
           Octstr *os;
           os = octstr_duplicate(msg->sms.opera_campaign_id);
           http_header_add(request_headers, "X-Kannel-Opera_Campaign_Id",
               octstr_get_cstr(os));
           octstr_destroy(os);
       }
       if (octstr_len(msg->sms.opera_reference)) {
           Octstr *os;
           os = octstr_duplicate(msg->sms.opera_reference);
           http_header_add(request_headers, "X-Kannel-Opera_Reference",
               octstr_get_cstr(os));
           octstr_destroy(os);
       }
       if (octstr_len(msg->sms.opera_channel)) {
           Octstr *os;
           os = octstr_duplicate(msg->sms.opera_channel);
           http_header_add(request_headers, "X-Kannel-Opera_Channel",
               octstr_get_cstr(os));
           octstr_destroy(os);
       }

Scroll down, find this function:

static Octstr *smsbox_req_handle(URLTranslation *t, Octstr *client_ip,
                                 HTTPClient *client,
                                 Octstr *from, Octstr *to, Octstr *text,
                                 Octstr *charset, Octstr *udh, Octstr *smsc,
                                 int mclass, int mwi, int coding, int compress,
                                 int validity, int deferred,
                                 int *status, int dlr_mask, Octstr *dlr_url,
                                 Octstr *account, int pid, int alt_dcs, int rpi,
                                 List *receiver, Octstr *binfo, int priority)

Change it to

static Octstr *smsbox_req_handle(URLTranslation *t, Octstr *client_ip,
                                 HTTPClient *client,
                                 Octstr *from, Octstr *to, Octstr *text,
                                 Octstr *charset, Octstr *udh, Octstr *smsc,
                                 int mclass, int mwi, int coding, int compress,
                                 int validity, int deferred,
                                 int *status, int dlr_mask, Octstr *dlr_url,
                                 Octstr *account, int pid, int alt_dcs, int rpi,
                                 List *receiver, Octstr *binfo,
				      Octstr *opera_campaign_id,
     Octstr *opera_reference,
     Octstr *opera_channel,
int priority)

Still in the same function
Change these lines:

    msg->sms.msgdata = text ? octstr_duplicate(text) : octstr_create("");
    msg->sms.udhdata = udh ? octstr_duplicate(udh) : octstr_create("");

    if (octstr_len(binfo))
        msg->sms.binfo = octstr_duplicate(binfo);

to

    msg->sms.msgdata = text ? octstr_duplicate(text) : octstr_create("");
    msg->sms.udhdata = udh ? octstr_duplicate(udh) : octstr_create("");

    if (octstr_len(binfo))
        msg->sms.binfo = octstr_duplicate(binfo);
    if (octstr_len(opera_campaign_id))
        msg->sms.opera_campaign_id = octstr_duplicate(opera_campaign_id);
    if (octstr_len(opera_reference))
        msg->sms.opera_reference = octstr_duplicate(opera_reference);
    if (octstr_len(opera_channel))
        msg->sms.opera_channel = octstr_duplicate(opera_channel);

Scroll down and find this code:

static Octstr *smsbox_req_sendsms(List *args, Octstr *client_ip, int *status,
                                  HTTPClient *client)

Inside this function Add this variable declaration and set it’s initial value to NULL:

Octstr *opera_campaign_id, *opera_reference, *opera_channel;
 opera_campaign_id = opera_reference = opera_channel = NULL;

Still in the same function, find this code:

binfo = http_cgi_variable(args, "binfo");

Change to

binfo = http_cgi_variable(args, "binfo");
opera_campaign_id = http_cgi_variable(args, "opera_campaign_id");
opera_reference   = http_cgi_variable(args, "opera_reference");
opera_channel     = http_cgi_variable(args, "opera_channel");

Scroll down, find this code:

    return smsbox_req_handle(t, client_ip, client, from, to, text, charset, udh,
                             smsc, mclass, mwi, coding, compress, validity,
                             deferred, status, dlr_mask, dlr_url, account,
                             pid, alt_dcs, rpi, NULL, binfo, priority);

And change to

    return smsbox_req_handle(t, client_ip, client, from, to, text, charset, udh,
                             smsc, mclass, mwi, coding, compress, validity,
                             deferred, status, dlr_mask, dlr_url, account,
                             pid, alt_dcs, rpi, NULL, binfo,opera_campaign_id,opera_reference,opera_channel, priority);

Scroll down, find


static Octstr *smsbox_sendsms_post(List *headers, Octstr *body,
                                   Octstr *client_ip, int *status,
                                   HTTPClient *client)

Inside this function Add this variable declaration and set it’s initial value to NULL:

Octstr *opera_campaign_id, *opera_reference, *opera_channel;
 opera_campaign_id = opera_reference = opera_channel = NULL;

Still in this function, modify:

                get_x_kannel_from_headers(reply_headers, &from, &to, &udh,
                                          NULL, NULL, &smsc, &mclass, &mwi,
                                          &coding, &compress, &validity,
                                          &deferred, &dlr_mask, &dlr_url,
                                          &account, &pid, &alt_dcs, &rpi,
                                          &binfo, &priority);

to

                get_x_kannel_from_headers(reply_headers, &from, &to, &udh,
                                          NULL, NULL, &smsc, &mclass, &mwi,
                                          &coding, &compress, &validity,
                                          &deferred, &dlr_mask, &dlr_url,
                                          &account, &pid, &alt_dcs, &rpi,
                                          &binfo,&opera_campaign_id,&opera_reference,&opera_channel, &priority);

also

                get_x_kannel_from_xml(mt_reply, &type, &replytext, reply_headers,
                        &from, &to, &udh, NULL, NULL, &smsc, &mclass, &mwi,
                        &coding, &compress, &validity, &deferred, &dlr_mask,
                        &dlr_url, &account, &pid, &alt_dcs, &rpi, NULL, &charset,
                        &binfo, &priority);

to

                get_x_kannel_from_xml(mt_reply, &type, &replytext, reply_headers,
                        &from, &to, &udh, NULL, NULL, &smsc, &mclass, &mwi,
                        &coding, &compress, &validity, &deferred, &dlr_mask,
                        &dlr_url, &account, &pid, &alt_dcs, &rpi, NULL, &charset,
                        &binfo, &opera_campaign_id,&opera_reference,&opera_channel,&priority);

Scroll down, find this code:

            ret = smsbox_req_handle(t, client_ip, client, from, to, body, charset,
                                    udh, smsc, mclass, mwi, coding, compress,
                                    validity, deferred, status, dlr_mask,
                                    dlr_url, account, pid, alt_dcs, rpi, tolist,
                                    binfo, priority);

Change to

    return smsbox_req_handle(t, client_ip, client, from, to, text, charset, udh,
                             smsc, mclass, mwi, coding, compress, validity,
                             deferred, status, dlr_mask, dlr_url, account,
                             pid, alt_dcs, rpi, NULL, binfo,opera_campaign_id,opera_reference,opera_channel, priority);

Scroll down, find this code:

    octstr_destroy(account);
    octstr_destroy(binfo);

Change to:

    octstr_destroy(account);
    octstr_destroy(binfo);
    octstr_destroy(opera_campaign_id);
    octstr_destroy(opera_reference);
    octstr_destroy(opera_channel);

Scroll down, find this code:

static Octstr *smsbox_xmlrpc_post(List *headers, Octstr *body,
                                  Octstr *client_ip, int *status)

Inside this function Add this variable declaration and set it’s initial value to NULL:

Octstr *opera_campaign_id, *opera_reference, *opera_channel;
 opera_campaign_id = opera_reference = opera_channel = NULL;

Now, compile:

sh configure --disable-ssl --with-mysql

Good luck 🙂

Written by adywicaksono

September 30, 2007 at 8:26 am

Posted in kannel, Linux, Programming

SSL Connection with Java

with one comment

Here is an example Java code to create SSL connection between you and HTTPS
(taken from http://www.cafeaulait.org/slides/iw2000/whatsnew/04.html)

import java.net.*;
import java.io.*;
import java.security.*;
import javax.net.ssl.*;

public class HTTPSClient {
	public static void main(String[] args) {
		if (args.length == 0) {
			System.out.println("Usage: java HTTPSClient host");
			return;
		}

		int port = 443; // default https port
		String host = args[0];

		try{
			Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
			SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();

			SSLSocket socket = (SSLSocket) factory.createSocket(host, port);

			Writer out = new OutputStreamWriter(socket.getOutputStream());
			// https requires the full URL in the GET line
			out.write("GET / HTTP/1.0\\r\\\n");
			out.write("\\r\\n");
			out.flush();

			// read response
			BufferedReader in = new BufferedReader(
						new InputStreamReader(socket.getInputStream()));
			int c;
			while ((c = in.read()) != -1) {
				System.out.write(c);
			}

			out.close();
			in.close();
			socket.close();
		}catch (IOException e) {
			System.err.println(e);
		}
	}
}

You can compile & run it:

$ javac HTTPSClient.java
$ java HTTPSClient login.yahoo.com

But wait, if HTTPS server give you certificate which is signed by “unknown” Certificate Authority ( I mean not signed by approved CA like Thawte, Verisign) then you will get this error

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

or something like it

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found

However, I found the fast solutions (I forget where was it, but I started from Google)
so all certificates (signed and unsigned) become accepted and the exception disappears.
Of course this is not recommended for a production system but quite useful for testing :).

import java.net.*;
import java.io.*;
import java.security.*;
import javax.net.ssl.*;

public class HTTPSClient {
	public static void main(String[] args) {
		if (args.length == 0) {
		   System.out.println("Usage: java HTTPSClient host");
		   return;
		}

		int port = 443; // default https port
		String host = args[0];

		
		TrustManager[] trustAll = new javax.net.ssl.TrustManager[]{
		  new javax.net.ssl.X509TrustManager(){
			  public java.security.cert.X509Certificate[] getAcceptedIssuers(){
				return null;
			  }
			  public void checkClientTrusted(java.security.cert.X509Certificate[] certs,String authType){}
			  public void checkServerTrusted(java.security.cert.X509Certificate[] certs,String authType){}
		  }
		};
		
		try {
		
			javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL");
			sc.init(null, trustAll, new java.security.SecureRandom());
		
			Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
			SSLSocketFactory factory = (SSLSocketFactory) sc.getSocketFactory();
			SSLSocket socket = (SSLSocket) factory.createSocket(host, port);

			Writer out = new OutputStreamWriter(socket.getOutputStream());
			out.write("GET / HTTP/1.0\\r\\n");
			out.write("\\r\\n");
			out.flush();

			// read response
			BufferedReader in = new BufferedReader(
			new InputStreamReader(socket.getInputStream()));
			int c;
			while ((c = in.read()) != -1) {
				System.out.write(c);
			}
			out.close();
			in.close();
			socket.close();
		}catch (Exception e) {
			System.err.println(e);
		}
	}
}

Written by adywicaksono

July 15, 2007 at 4:20 pm

C code to test thread limitation on Linux

with 5 comments

Still remember with posting about thread limitation on Linux?

Here’s a C code to test it..

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#define MAX_THREADS 10000
int i;

void run(void) {
   char c;
   if (i < 10)
      printf("Address of c = %u KB\n", (unsigned int) &c / 1024);
   sleep(60 * 60);
}

int main(int argc, char *argv[]) {
   int rc = 0;
   pthread_t thread[MAX_THREADS];
   printf("Creating threads ...\n");
   for (i = 0; i < MAX_THREADS && rc == 0; i++) {
     rc = pthread_create(&(thread[i]), NULL, (void *) &run, NULL);
     if (rc == 0) {
       pthread_detach(thread[i]);
       if ((i + 1) % 100 == 0)
         printf("%i threads so far ...\n", i + 1);
     }
     else
        printf("Failed with return code %i creating thread %i.\n",rc, i + 1);
   }
   exit(0);
}

Save it as threadlimit.c and compile it with

gcc -lpthread -o resulttest threadlimit.c

You have an exe file => “resulttest” so execute it now

./resulttest

Here is an example as result of this C code execution on my Fedora 🙂
Linux 2.6.17-1.2142_FC4smp #1 SMP Tue Jul 11 22:57:02 EDT 2006 i686 i686 i386 GNU/Linux

# ulimit -s
10240

# ./resulttest
Creating threads ...
100 threads so far ...
200 threads so far ...
300 threads so far ...
Failed with return code 12 creating thread 304.

Boom!!!, I only create 303 threads. No try decrease thread stack size to 100

# ulimit -s 100
100

# ./resulttest
Creating threads ...
100 threads so far ...
200 threads so far ...
300 threads so far ...
400 threads so far ...
500 threads so far ...
600 threads so far ...
700 threads so far ...
800 threads so far ...
900 threads so far ...
1000 threads so far ...
1100 threads so far ...
1200 threads so far ...
1300 threads so far ...
1400 threads so far ...
1500 threads so far ...
....
10000 threads so far...
... [CTRL-C]

Wow, I could create a lot of threads :D….

Written by adywicaksono

July 15, 2007 at 3:35 pm

Posted in Linux, Programming

Length of Array in C

with one comment

Q: How to count length of array in C/C++?

A:

———- cut here ———-
typedef struct {
int x;
int y;
}ok ;

int main(){

ok i [] = {
{1,2},
{3,4}
};
printf(“SIZE: %d\n”,sizeof (i) / sizeof (*i));
return 0;
}

==> Size = sizeof(i)/sizeof(*i)

Written by adywicaksono

July 14, 2007 at 11:51 am

Posted in Programming

Swap 2 variables without temporary variables?

with 3 comments

This is the solution:

#include <stdio.h>

void swap(int *x, int *y){
  *x -= *y;
  *y += *x;
  *x  = (*y - *x);
}

int main(){
   int a = 100;
   int b = 200;
   printf("a = %dnb = %dn",a,b);
   swap(&a,&b);
   printf("a = %dnb = %dn",a,b);
   return 0;
}

Written by adywicaksono

July 10, 2007 at 4:55 pm

Posted in Programming