/* * This file is part of Smsq. * * Smsq is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Smsq is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Smsq; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Peter Harper * * $Id: smsq.c,v 1.1 2003/12/02 06:17:38 rmello Exp $ */ static char rcsid[] = "@(#) $Id: smsq.c,v 1.1 2003/12/02 06:17:38 rmello Exp $"; #include "ns.h" #include "../qcluster/message.h" #include "../qcluster/cluster.h" #include "../qcluster/locks.h" #include "smsq.h" #include "gsm0338.h" int Ns_ModuleVersion = 1; static struct q_message_type *g_sms_submit_type; static char g_aolserver_name[128]; #define SMSQ_ALIGNED_SIZE(s) (s + (sizeof(int) - (s % sizeof(int)))) /* * Static functions */ static int smsq_construct_sms_in(void *in_buf, int in_size, void *out_buf, int *out_size); static int smsq_construct_sms_out(void *in_buf, int in_size, void *out_buf, int *out_size); static int smsq_tcl_submit_ascii(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int smsq_tcl_wait_for_group_activity(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int smsq_tcl_get_next_group_msg(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int smsq_tcl_get_group_names(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int smsq_tcl_get_server_name(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int smsq_tcl_set_group_process_flag(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int smsq_tcl_set_msg_status(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int smsq_tcl_retrieve_ascii(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int smsq_tcl_get_queue_info(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int smsq_add_commands(Tcl_Interp *interp, ClientData data); static int smsq_execute_tcl_response(struct q_message *message_ptr, int success_p); /* *---------------------------------------------------------------------- * * Ns_ModuleInit -- * * Register the SMS message type with the qcluster module. * * Results: * None. * * Side effects: * *---------------------------------------------------------------------- */ NS_EXPORT int Ns_ModuleInit(char *server, char *module) { /* * Wait for the qcluster module to start. */ while (1) { if (g_qcluster_started) { break; } sleep(1); } Ns_Log(Notice, "smsq: initialising"); (void) gsm0338_init(); g_sms_submit_type = q_register_msg_type(SMSQ_SUBMIT_TYPE_ID, SMSQ_SUBMIT_TYPE_NAME, smsq_construct_sms_in, smsq_construct_sms_out); strcpy(g_aolserver_name, server); Ns_Log(Notice, "smsq: initialisation complete"); return Ns_TclInitInterps(server, smsq_add_commands, NULL); } /* *---------------------------------------------------------------------- * * smsq_construct_sms_out -- * Convert sms message payload for transmission on a network. * * Results: * NS_TRUE - Conversion okay. * NS_FALSE - Conversion failed * *---------------------------------------------------------------------- */ int smsq_construct_sms_out(void *in_buf, int in_size, void *out_buf, int *out_size) { struct smsq_sms_submit *in_ptr; unsigned char *msg_buf; int msg_size; in_ptr = (struct smsq_sms_submit *) in_buf; msg_buf = (unsigned char *)out_buf; msg_size = 0; /* type_of_number */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->type_of_number); msg_size+= sizeof(int); /* numbering_plan_id */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->numbering_plan_id); msg_size+= sizeof(int); /* dest_address */ *((int *)&msg_buf[msg_size]) = htonl(strlen(in_ptr->dest_address)); msg_size+= sizeof(int); strcpy(&msg_buf[msg_size], in_ptr->dest_address); msg_size+= SMSQ_ALIGNED_SIZE(strlen(in_ptr->dest_address)); /* protocol_id */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->protocol_id); msg_size+= sizeof(int); /* data_coding_scheme */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->data_coding_scheme); msg_size+= sizeof(int); /* validity_period / tp_mask */ *((char *)&msg_buf[msg_size ]) = in_ptr->validity_period; *((char *)&msg_buf[msg_size+1]) = in_ptr->tp_mask; msg_size+= sizeof(int); /* safe_stored_p */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->safe_stored_p); msg_size+= sizeof(int); /* user_data_len */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->user_data_len); msg_size+= sizeof(int); /* user_data */ memcpy(&msg_buf[msg_size], in_ptr->user_data, in_ptr->user_data_len); msg_size+= SMSQ_ALIGNED_SIZE(in_ptr->user_data_len); /* unpacked_data_len */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->unpacked_data_len); msg_size+= sizeof(int); /* timestamp.tm_sec */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->timestamp.tm_sec); msg_size+= sizeof(int); /* timestamp.tm_min */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->timestamp.tm_min); msg_size+= sizeof(int); /* timestamp.tm_hour */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->timestamp.tm_hour); msg_size+= sizeof(int); /* timestamp.tm_mday */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->timestamp.tm_mday); msg_size+= sizeof(int); /* timestamp.tm_mon */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->timestamp.tm_mon); msg_size+= sizeof(int); /* timestamp.tm_year */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->timestamp.tm_year); msg_size+= sizeof(int); /* smsc_timestamp.tm_sec */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_sec); msg_size+= sizeof(int); /* smsc_timestamp.tm_min */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_min); msg_size+= sizeof(int); /* smsc_timestamp.tm_hour */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_hour); msg_size+= sizeof(int); /* smsc_timestamp.tm_mday */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_mday); msg_size+= sizeof(int); /* smsc_timestamp.tm_mon */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_mon); msg_size+= sizeof(int); /* smsc_timestamp.tm_year */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_year); msg_size+= sizeof(int); /* smsc_timestamp.tm_tz */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_tz); msg_size+= sizeof(int); /* success_callback_p */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->success_callback_p); msg_size+= sizeof(int); if (in_ptr->success_callback_p) { /* success_callback */ *((int *)&msg_buf[msg_size]) = htonl(strlen(in_ptr->success_callback)); msg_size+= sizeof(int); strcpy(&msg_buf[msg_size], in_ptr->success_callback); msg_size+= SMSQ_ALIGNED_SIZE(strlen(in_ptr->success_callback)); } /* fail_callback_p */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->fail_callback_p); msg_size+= sizeof(int); if (in_ptr->fail_callback_p) { /* fail_callback */ *((int *)&msg_buf[msg_size]) = htonl(strlen(in_ptr->fail_callback)); msg_size+= sizeof(int); strcpy(&msg_buf[msg_size], in_ptr->fail_callback); msg_size+= SMSQ_ALIGNED_SIZE(strlen(in_ptr->fail_callback)); } /* del_attempt_count */ *((int *)&msg_buf[msg_size]) = htonl(in_ptr->del_attempt_count); msg_size+= sizeof(int); *out_size = msg_size; return NS_TRUE; } /* *---------------------------------------------------------------------- * * smsq_construct_sms_in -- * * Convert a network encoded sms message payload back to host encoded. * Note that this is a bodge, for use whilst testing. This function * is platform dependent, and assumes that all remote servers are running * the same architecture. (PH 17/05/2001) * * Results: * NS_TRUE - Conversion okay. * NS_FALSE - Conversion failed * *---------------------------------------------------------------------- */ int smsq_construct_sms_in(void *in_buf, int in_size, void *out_buf, int *out_size) { int msg_pos; unsigned char *msg_buf; struct smsq_sms_submit *out_ptr; int str_len; msg_pos = 0; msg_buf = (unsigned char *)in_buf; out_ptr = (struct smsq_sms_submit *)out_buf; /* type_of_number */ out_ptr->type_of_number = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* numbering_plan_id */ out_ptr->numbering_plan_id = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* dest_address */ str_len = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); memcpy(out_ptr->dest_address, &msg_buf[msg_pos], str_len); out_ptr->dest_address[str_len] = '\0'; msg_pos += SMSQ_ALIGNED_SIZE(str_len); /* protocol_id */ out_ptr->protocol_id = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* data_coding_scheme */ out_ptr->data_coding_scheme = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* validity_period / tp_mask */ out_ptr->validity_period = msg_buf[msg_pos]; out_ptr->tp_mask = msg_buf[msg_pos + 1]; msg_pos+= sizeof(int); /* safe_stored_p */ out_ptr->safe_stored_p = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* user_data_len */ out_ptr->user_data_len = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* user_data */ memcpy(out_ptr->user_data, &msg_buf[msg_pos], out_ptr->user_data_len); msg_pos += SMSQ_ALIGNED_SIZE(out_ptr->user_data_len); /* unpacked_data_len */ out_ptr->unpacked_data_len = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* timestamp.tm_sec */ out_ptr->timestamp.tm_sec = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* timestamp.tm_min */ out_ptr->timestamp.tm_min = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* timestamp.tm_hour */ out_ptr->timestamp.tm_hour = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* timestamp.tm_mday */ out_ptr->timestamp.tm_mday = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* timestamp.tm_mon */ out_ptr->timestamp.tm_mon = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* timestamp.tm_year */ out_ptr->timestamp.tm_year = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* smsc_timestamp.tm_sec */ out_ptr->smsc_timestamp.tm_sec = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* smsc_timestamp.tm_min */ out_ptr->smsc_timestamp.tm_min = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* smsc_timestamp.tm_hour */ out_ptr->smsc_timestamp.tm_hour = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* smsc_timestamp.tm_mday */ out_ptr->smsc_timestamp.tm_mday = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* smsc_timestamp.tm_mon */ out_ptr->smsc_timestamp.tm_mon = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* smsc_timestamp.tm_year */ out_ptr->smsc_timestamp.tm_year = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* smsc_timestamp.tm_tz */ out_ptr->smsc_timestamp.tm_tz = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); /* success_callback_p */ out_ptr->success_callback_p = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); if (out_ptr->success_callback_p) { /* success_callback */ str_len = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); memcpy(out_ptr->success_callback, &msg_buf[msg_pos], str_len); out_ptr->success_callback[str_len] = '\0'; msg_pos += SMSQ_ALIGNED_SIZE(str_len); } /* fail_callback_p */ out_ptr->fail_callback_p = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); if (out_ptr->fail_callback_p) { /* fail_callback */ str_len = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); memcpy(out_ptr->fail_callback, &msg_buf[msg_pos], str_len); out_ptr->fail_callback[str_len] = '\0'; msg_pos += SMSQ_ALIGNED_SIZE(str_len); } /* del_attempt_count */ out_ptr->del_attempt_count = ntohl(*((int *)&msg_buf[msg_pos])); msg_pos += sizeof(int); *out_size = sizeof(struct smsq_sms_submit); return NS_TRUE; } /* *---------------------------------------------------------------------- * * smsq_group_empty_p -- * * Checks if a group's message queue is empty. * * Results: * *---------------------------------------------------------------------- */ int smsq_group_empty_p(struct q_group *group_ptr) { int ret; q_get_rlock(group_ptr); if (group_ptr->queue_by_stat_ll[Q_MSG_STATUS_READY] == NULL) { ret = NS_TRUE; } else { ret = NS_FALSE; } q_release_lock(group_ptr); return ret; } /* *---------------------------------------------------------------------- * * smsq_set_group_process_flag -- * * Adjusts the counter indicating to the underlying qcluster layer whether * this server will process messages for this group. The qcluster layer * may reassign any pending/future messages for the specified group to * another server. * * Results: * * Side effects: * *---------------------------------------------------------------------- */ int smsq_set_group_process_p(struct q_group *group_ptr, int can_process_p) { if (can_process_p == 1) { q_set_can_process_p(group_ptr, 1); } else { q_set_can_process_p(group_ptr, -1); } return NS_TRUE; } /* *---------------------------------------------------------------------- * * smsq_submit_sms -- * * Submits an sms to the specified group name. * * Results: * *---------------------------------------------------------------------- */ int smsq_submit_sms(struct q_group *group_ptr, char *msg_id_ptr, int type_of_number, int numbering_plan_id, char *dest_address, char *src_address, int protocol_id, int data_coding_scheme, unsigned char validity_period, unsigned char tp_mask, int message_ref, int safe_stored_p, unsigned char *user_data, int user_data_len, int unpacked_data_len, struct smsc_tm *smsc_timestamp_ptr, char *fail_callback, char *success_callback) { struct timeval tv; struct timezone tz; time_t timep; struct tm *datetime; struct smsq_sms_submit payload; timep = time(NULL); datetime = localtime(&timep); gettimeofday(&tv, &tz); payload.type_of_number = type_of_number; payload.numbering_plan_id = numbering_plan_id; strcpy(payload.dest_address, dest_address); if (src_address != NULL) { strcpy(payload.src_address, src_address); } else { strcpy(payload.src_address, ""); } payload.protocol_id = protocol_id; payload.data_coding_scheme = data_coding_scheme; payload.validity_period = validity_period; payload.tp_mask = tp_mask; payload.message_ref = message_ref; payload.safe_stored_p = safe_stored_p; payload.user_data_len = user_data_len; memcpy(payload.user_data, user_data, user_data_len); payload.unpacked_data_len = unpacked_data_len; memcpy(&payload.timestamp, datetime, sizeof(struct tm)); if (smsc_timestamp_ptr != NULL) { memcpy(&payload.smsc_timestamp, smsc_timestamp_ptr, sizeof(struct smsc_tm)); } else { memset(&payload.smsc_timestamp, 0, sizeof(struct smsc_tm)); } payload.fail_callback_p = 0; payload.success_callback_p = 0; if (fail_callback != NULL) { payload.fail_callback_p = 1; strcpy(payload.fail_callback, fail_callback); } if (success_callback != NULL) { payload.success_callback_p = 1; strcpy(payload.success_callback, success_callback); } payload.del_attempt_count = 0; sprintf(msg_id_ptr, "%s.%02d.%02d.%02d.%06d.%06d", g_server->svr_name, datetime->tm_year, datetime->tm_mon, datetime->tm_mday, (int) tv.tv_sec, (int) tv.tv_usec); q_queue_msg(g_sms_submit_type, group_ptr, msg_id_ptr, Q_MSG_STATUS_READY, &payload, sizeof(struct smsq_sms_submit), g_cluster_size); return NS_TRUE; } /* *---------------------------------------------------------------------- * * smsq_submit_ascii -- * * Submits an ascii sms onto the queue by using the smsq_submit_sms * function api. * * Results: * *---------------------------------------------------------------------- */ int smsq_submit_ascii(struct q_group *group_ptr, char *msg_id_ptr, char *msisdn_ptr, char *src_msisdn_ptr, char *message_ptr, struct smsc_tm *smsc_timestamp_ptr, char *fail_callback, char *success_callback) { unsigned char validity_period; unsigned char bits7_msg[145]; // Should be 140, but just to be sure. int bits7_len; /* * Forcibly prevent messages greater than 160 characters. */ if (strlen(message_ptr) > 160) { message_ptr[160] = '\0'; } gsm0338_pack_7bit(bits7_msg, &bits7_len, message_ptr); validity_period = (unsigned char) 0xaa; smsq_submit_sms(group_ptr, msg_id_ptr, 1, /* type of number */ 1, /* numbering plan_id */ msisdn_ptr, /* dest_address */ src_msisdn_ptr, /* src_address */ 0, /* protocol_id */ DCS_DEFAULT, /* data_coding_scheme */ 0xaa, /* validity_period */ TP_TYPE_SUBMIT | TP_STATUS_REP | TP_VAL_PER_REL, /* tp_mask */ 0, /* message_ref */ 0, /* safe_stored_p */ bits7_msg, bits7_len, strlen(message_ptr), /* unpacked_data_len */ smsc_timestamp_ptr, fail_callback, success_callback); return NS_TRUE; } /* *---------------------------------------------------------------------- * * smsq_submit_8bit -- * * Submits an ascii sms onto the queue by using the smsq_submit_sms * function api. * * Results: * *---------------------------------------------------------------------- */ int smsq_submit_8bit(struct q_group *group_ptr, char *msg_id_ptr, char *msisdn_ptr, char *src_msisdn_ptr, unsigned char *message_ptr, int message_size, int unpacked_size, int tp_flags, int data_coding_scheme, struct smsc_tm *smsc_timestamp_ptr, char *fail_callback, char *success_callback) { smsq_submit_sms(group_ptr, msg_id_ptr, 1, /* type of number */ 1, /* numbering plan_id */ msisdn_ptr, /* dest_address */ src_msisdn_ptr, /* src_address */ 0, /* protocol_id */ data_coding_scheme, /* data_coding_scheme */ 0xaa, /* validity_period */ TP_TYPE_SUBMIT | TP_STATUS_REP | TP_VAL_PER_REL | tp_flags, /* tp_mask */ 0, /* message_ref */ 0, /* safe_stored_p */ message_ptr, message_size, unpacked_size, /* unpacked_data_len */ smsc_timestamp_ptr, fail_callback, success_callback); return NS_TRUE; } /* *---------------------------------------------------------------------- * * smsq_set_msg_status -- * * Results: * *---------------------------------------------------------------------- */ int smsq_set_msg_status(char *msg_id_ptr, struct q_group *group_ptr, int status) { struct smsq_sms_submit *payload_ptr; struct q_message *msg_ptr; if (status == Q_MSG_STATUS_FAILED || status == Q_MSG_STATUS_COMPLETE) { q_get_rlock(group_ptr); msg_ptr = q_data_get_msg_entry(group_ptr, msg_id_ptr); if (msg_ptr != NULL) { payload_ptr = msg_ptr->msg_ptr; if (status == Q_MSG_STATUS_FAILED && payload_ptr->fail_callback_p) { smsq_execute_tcl_response(msg_ptr, 0); } else if (status == Q_MSG_STATUS_COMPLETE && payload_ptr->success_callback_p) { smsq_execute_tcl_response(msg_ptr, 1); } } q_release_lock(group_ptr); } q_msg_status_update(msg_id_ptr, group_ptr, status); return NS_TRUE; } /* *---------------------------------------------------------------------- * * smsq_get_msg_and_set_status -- * * Wrapper around the qcluster function that: * Retrieves a message with the given status from the given group. The * status of the message is then updated to the new status in one, single * atomic operation. * * Results: * Message pointer if a message was found. NULL otherwise. * *---------------------------------------------------------------------- */ struct q_message * smsq_get_msg_and_set_status(struct q_group *group_ptr, int cur_status, int new_status) { return q_get_msg_and_set_status(group_ptr, cur_status, new_status); } /* *---------------------------------------------------------------------- * * smsq_add_commands -- * * Adds the smsq api functions to the Tcl interpreter. * * Results: * *---------------------------------------------------------------------- */ static int smsq_add_commands(Tcl_Interp *interp, ClientData data) { Tcl_CreateCommand(interp, "smsq_submit_ascii", smsq_tcl_submit_ascii, NULL, NULL); Tcl_CreateCommand(interp, "smsq_wait_for_group_activity", smsq_tcl_wait_for_group_activity, NULL, NULL); Tcl_CreateCommand(interp, "smsq_get_next_group_msg", smsq_tcl_get_next_group_msg, NULL, NULL); Tcl_CreateCommand(interp, "smsq_get_group_names", smsq_tcl_get_group_names, NULL, NULL); Tcl_CreateCommand(interp, "smsq_get_server_name", smsq_tcl_get_server_name, NULL, NULL); Tcl_CreateCommand(interp, "smsq_set_group_process_flag", smsq_tcl_set_group_process_flag, NULL, NULL); Tcl_CreateCommand(interp, "smsq_set_msg_status", smsq_tcl_set_msg_status, NULL, NULL); Tcl_CreateCommand(interp, "smsq_retrieve_ascii", smsq_tcl_retrieve_ascii, NULL, NULL); Tcl_CreateCommand(interp, "smsq_get_queue_info", smsq_tcl_get_queue_info, NULL, NULL); return NS_OK; } /* *---------------------------------------------------------------------- * * smsq_tcl_submit_ascii -- * * Tcl API function to allow ascii sms to be added to the message queue * * Results: * *---------------------------------------------------------------------- */ static int smsq_tcl_submit_ascii(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { struct q_group *group_ptr; char msg_id[128]; char *fail_callback_ptr; char *success_callback_ptr; char *src_msisdn = ""; int arg_offset = 0; fail_callback_ptr = NULL; success_callback_ptr = NULL; if (argc > 2 && !strcmp(argv[1],"-src")) { src_msisdn = argv[2]; arg_offset = 2; } if ((argc - arg_offset) < 4 || (argc - arg_offset) > 6) { Tcl_AppendResult(interp, "Usage: ", argv[0], " [-src src_msisdn] group_name msisdn message [failed_callback] [success_callback", NULL); return TCL_ERROR; } group_ptr = q_get_group_ptr(g_server, argv[arg_offset + 1]); if (group_ptr == NULL) { Tcl_AppendResult(interp, "Unknown group name: ", argv[arg_offset + 1], NULL); return TCL_ERROR; } /* * Set the fail_callback parameter if passed in */ if ((argc - arg_offset) > 4) { fail_callback_ptr = argv[arg_offset + 4]; } /* * Set the success_callback parameter if passed in */ if ((argc - arg_offset) > 5) { success_callback_ptr = argv[arg_offset + 5]; } smsq_submit_ascii(group_ptr, msg_id, argv[arg_offset + 2], src_msisdn, argv[arg_offset + 3], NULL, fail_callback_ptr, success_callback_ptr); Tcl_AppendResult(interp, msg_id, NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * smsq_tcl_retrieve_ascii -- * * Tcl API function to retrieve the contents of an ascii message * * Results: * *---------------------------------------------------------------------- */ static int smsq_tcl_retrieve_ascii(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { struct q_group *group_ptr; struct q_message *msg_ptr; struct smsq_sms_submit *payload_ptr; Tcl_Obj *list_ptr, *elem_ptr; char bits8_msg[165]; if (argc != 3) { Tcl_AppendResult(interp, "Usage: ", argv[0], " group_name msg_id", NULL); return TCL_ERROR; } list_ptr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); group_ptr = q_get_group_ptr(g_server, argv[1]); if (group_ptr == NULL) { Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); return TCL_ERROR; } q_get_rlock(group_ptr); msg_ptr = q_data_get_msg_entry(group_ptr, argv[2]); if (msg_ptr != NULL) { payload_ptr = msg_ptr->msg_ptr; elem_ptr = Tcl_NewStringObj("msisdn", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj(payload_ptr->dest_address, -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj("src_msisdn", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj(payload_ptr->src_address, -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj("originating_msisdn", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj(payload_ptr->dest_address, -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj("terminating_msisdn", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj(payload_ptr->src_address, -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj("message", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); gsm0338_unpack_7bit(bits8_msg, payload_ptr->user_data, payload_ptr->user_data_len, payload_ptr->unpacked_data_len); elem_ptr = Tcl_NewStringObj(bits8_msg, -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj("smsc_year", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_year); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj("smsc_month", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_mon); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj("smsc_day", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_mday); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj("smsc_hour", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_hour); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj("smsc_min", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_min); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj("smsc_sec", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_sec); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj("smsc_tz", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_tz); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewStringObj("dcs", -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); elem_ptr = Tcl_NewIntObj(payload_ptr->data_coding_scheme); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); } q_release_lock(group_ptr); Tcl_SetObjResult(interp, list_ptr); return TCL_OK; } /* *---------------------------------------------------------------------- * * smsq_tcl_wait_for_group_activity -- * * Tcl API function to wait for activity within the specified group * for the specified timeout period. * * Results: * *---------------------------------------------------------------------- */ static int smsq_tcl_wait_for_group_activity(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { struct q_group *group_ptr; char msg_id[128]; int timeout; Ns_Time waittime; int result; if (argc < 2 || argc > 3) { Tcl_AppendResult(interp, "Usage: ", argv[0], " group_name ?timeout?", NULL); return TCL_ERROR; } else { if (argc == 3) { if (Tcl_GetInt(interp, argv[2], &timeout) != TCL_OK) { return TCL_ERROR; } } } group_ptr = q_get_group_ptr(g_server, argv[1]); if (group_ptr == NULL) { Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); return TCL_ERROR; } /* * Call the underlying Ns_Cond function for this thread event. */ if (argc == 2) { Ns_CondWait(&group_ptr->event, &group_ptr->event_lock); interp->result = "1"; Ns_MutexUnlock(&group_ptr->event_lock); } else { Ns_GetTime(&waittime); Ns_IncrTime(&waittime, timeout, 0); result = Ns_CondTimedWait(&group_ptr->event, &group_ptr->event_lock, &waittime); Ns_MutexUnlock(&group_ptr->event_lock); switch (result) { case NS_OK: interp->result = "1"; break; case NS_TIMEOUT: interp->result = "0"; break; default: return TCL_ERROR; break; } } return TCL_OK; } /* *---------------------------------------------------------------------- * * smsq_tcl_get_next_group_msg -- * * Tcl API function to retrieve a message within the given group, * returning the message id. Returns an empty string if no message * found. * * Results: * *---------------------------------------------------------------------- */ static int smsq_tcl_get_next_group_msg(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { struct q_message *msg_ptr; struct q_group *group_ptr; if (argc != 2) { Tcl_AppendResult(interp, "Usage: ", argv[0], " group_name", NULL); return TCL_ERROR; } group_ptr = q_get_group_ptr(g_server, argv[1]); if (group_ptr == NULL) { Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); return TCL_ERROR; } /* * Get the next available message */ msg_ptr = smsq_get_msg_and_set_status(group_ptr, Q_MSG_STATUS_READY, Q_MSG_STATUS_PROCESSING); if (msg_ptr == NULL) { Tcl_AppendResult(interp, NULL); } else { Tcl_AppendResult(interp, msg_ptr->msg_id, NULL); } // Ns_Log(Notice, "Exiting smsq_tcl_get_next_group_msg %x", msg_ptr); return TCL_OK; } /* *---------------------------------------------------------------------- * * smsq_tcl_set_msg_status -- * * Set the status of the given message id. If status is "complete", the * message is removed from the given group queue. * * Results: * *---------------------------------------------------------------------- */ static int smsq_tcl_set_msg_status(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { struct q_group *group_ptr; int status; if (argc != 4) { Tcl_AppendResult(interp, "Usage: ", argv[0], " group_name msg_id status", NULL); return TCL_ERROR; } group_ptr = q_get_group_ptr(g_server, argv[1]); if (group_ptr == NULL) { Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); return TCL_ERROR; } /* * Work out the actual C status value */ if (STREQ(argv[3], "ready")) { status = Q_MSG_STATUS_READY; } else if (STREQ(argv[3], "delayed")) { status = Q_MSG_STATUS_DELAYED; } else if (STREQ(argv[3], "processing")) { status = Q_MSG_STATUS_PROCESSING; } else if (STREQ(argv[3], "complete")) { status = Q_MSG_STATUS_COMPLETE; } else if (STREQ(argv[3], "failed")) { status = Q_MSG_STATUS_FAILED; } else { Tcl_AppendResult(interp, "Unknown status: should be ", "ready, ", "delayed, ", "processing, ", "complete or", "failed", NULL); return TCL_ERROR; } /* * Set the status value. */ // q_msg_status_update(argv[2], group_ptr, status); smsq_set_msg_status(argv[2], group_ptr, status); return TCL_OK; } /* *---------------------------------------------------------------------- * * smsq_tcl_set_group_process_flag -- * * Set the group process flag for the specified group. * * Results: * *---------------------------------------------------------------------- */ static int smsq_tcl_set_group_process_flag(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { struct q_group *group_ptr; int process_p; if (argc != 3) { Tcl_AppendResult(interp, "Usage: ", argv[0], " group_name process_flag", NULL); return TCL_ERROR; } group_ptr = q_get_group_ptr(g_server, argv[1]); if (group_ptr == NULL) { Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); return TCL_ERROR; } if (Tcl_GetInt(interp, argv[2], &process_p) != TCL_OK) { return TCL_ERROR; } if (process_p < 0 || process_p > 1) { Tcl_AppendResult(interp, "Invalid process_p flag : ", argv[2], NULL); return TCL_ERROR; } /* * Call the C function to set the process flag. */ smsq_set_group_process_p(group_ptr, process_p); return TCL_OK; } /* *---------------------------------------------------------------------- * * smsq_tcl_get_group_names -- * * Return a list of group names * * Results: * *---------------------------------------------------------------------- */ static int smsq_tcl_get_group_names(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { char *group_name_ptr; int process_p; Tcl_HashEntry *search_ptr; Tcl_HashSearch search; Tcl_Obj *list_ptr, *elem_ptr; if (argc != 1) { Tcl_AppendResult(interp, "Usage: ", argv[0], NULL); return TCL_ERROR; } list_ptr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); q_get_rlock(g_server); search_ptr = Tcl_FirstHashEntry(&g_server->groups_ht, &search); while (search_ptr != NULL) { group_name_ptr = Tcl_GetHashKey(&g_server->groups_ht, search_ptr); elem_ptr = Tcl_NewStringObj(group_name_ptr, -1); Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); search_ptr = Tcl_NextHashEntry(&search); } q_release_lock(g_server); Tcl_SetObjResult(interp, list_ptr); return TCL_OK; } /* *---------------------------------------------------------------------- * * smsq_tcl_get_queue_info -- * * Return a complete snapshop of all queues. * * Results: * *---------------------------------------------------------------------- */ static int smsq_tcl_get_queue_info(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { char *group_name_ptr; int process_p; Tcl_HashEntry *search_ptr; Tcl_HashSearch search; struct q_message_search msg_search; Tcl_Obj *server_list_ptr, *group_list_ptr, *msg_list_ptr, *elem_ptr; struct q_server *server_ptr; struct q_group *group_ptr; struct q_message *message_ptr; int i; if (argc != 1) { Tcl_AppendResult(interp, "Usage: ", argv[0], NULL); return TCL_ERROR; } server_list_ptr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); q_get_rlock(g_server); for (i = 0; i < g_cluster_size; i++) { server_ptr = g_cluster_order[i]; elem_ptr = Tcl_NewStringObj(server_ptr->svr_name, -1); Tcl_ListObjAppendElement(interp, server_list_ptr, elem_ptr); group_list_ptr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); search_ptr = Tcl_FirstHashEntry(&server_ptr->groups_ht, &search); while (search_ptr != NULL) { /* add group name */ group_name_ptr = Tcl_GetHashKey(&server_ptr->groups_ht, search_ptr); group_ptr = Tcl_GetHashValue(search_ptr); elem_ptr = Tcl_NewStringObj(group_name_ptr, -1); Tcl_ListObjAppendElement(interp, group_list_ptr, elem_ptr); msg_list_ptr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); message_ptr = q_data_first_msg_entry(group_ptr, -1, &msg_search); while (message_ptr != NULL) { /* * Add this individual message. */ elem_ptr = Tcl_NewStringObj(message_ptr->msg_id, -1); Tcl_ListObjAppendElement(interp, msg_list_ptr, elem_ptr); message_ptr = q_data_next_msg_entry(&msg_search); } Tcl_ListObjAppendElement(interp, group_list_ptr, msg_list_ptr); search_ptr = Tcl_NextHashEntry(&search); } q_release_lock(server_ptr); Tcl_ListObjAppendElement(interp, server_list_ptr, group_list_ptr); } Tcl_SetObjResult(interp, server_list_ptr); return TCL_OK; } /* *---------------------------------------------------------------------- * * smsq_tcl_get_server_name -- * * Return a list of group names * * Results: * *---------------------------------------------------------------------- */ static int smsq_tcl_get_server_name(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { q_get_rlock(g_server); Tcl_AppendResult(interp, g_server->svr_name, NULL); q_release_lock(g_server); return TCL_OK; } /* *---------------------------------------------------------------------- * * smsq_tcl_execute_tcl -- * * Executes piece of tcl. * * Results: * *---------------------------------------------------------------------- */ static int smsq_execute_tcl_response(struct q_message *message_ptr, int success_p) { struct smsq_sms_submit *payload_ptr; Ns_DString script; Ns_DString result; int status; unsigned char bits8_msg[165]; Ns_DStringInit(&script); Ns_DStringInit(&result); payload_ptr = message_ptr->msg_ptr; if (success_p) { if (!payload_ptr->success_callback_p) { return NS_FALSE; } else { Ns_DStringVarAppend(&script, payload_ptr->success_callback, " 1 ", NULL); } } else { if (!payload_ptr->fail_callback_p) { return NS_FALSE; } else { Ns_DStringVarAppend(&script, payload_ptr->fail_callback, " 0 ", NULL); } } Ns_DStringVarAppend(&script, message_ptr->msg_id, " ", NULL); Ns_DStringVarAppend(&script, "[list ", NULL); Ns_DStringVarAppend(&script, "msisdn", " {", NULL); Ns_DStringVarAppend(&script, payload_ptr->dest_address,"} ", NULL); Ns_DStringVarAppend(&script, "src_msisdn", " {", NULL); Ns_DStringVarAppend(&script, payload_ptr->src_address,"} ", NULL); Ns_DStringVarAppend(&script, "message", " {", NULL); gsm0338_unpack_7bit(bits8_msg, payload_ptr->user_data, payload_ptr->user_data_len, payload_ptr->unpacked_data_len); Ns_DStringVarAppend(&script, bits8_msg, "}", NULL); Ns_DStringVarAppend(&script, "]", NULL); status = Ns_TclEval(&result, g_aolserver_name, script.string); return NS_TRUE; }