[PATCH] meta-data branch vs. CVS HEAD
Stipe Tolj <[email protected]>
| Newsgroups | gmane.comp.mobile.kannel.devel |
|---|---|
| Organization | tolj.org system architecture |
| Message-ID | <[email protected]> |
Hi list,
attached is a revised version of Alex's meta-data support, i.e. for vendor
specific optional TLVs in SMPP v3.4.
This patch is against CVS HEAD, which makes it more current then the version we
used when we spin-off the 'meta-data' branch in CVS, also fixing minor segfault
possibilities.
Please review and vote for committing to mainline HEAD tree.
Stipe
-------------------------------------------------------------------
Kölner Landstrasse 419
40589 DÃŒsseldorf, NRW, Germany
tolj.org system architecture Kannel Software Foundation (KSF)
http://www.tolj.org/ http://www.kannel.org/
mailto:st_{at}_tolj.org mailto:stolj_{at}_kannel.org
-------------------------------------------------------------------
meta_data_cvs_head.diff
(text/plain, 45.1 KB)
### Eclipse Workspace Patch 1.0
#P gateway-cvs-head
Index: gw/urltrans.c
===================================================================
RCS file: /home/cvs/gateway/gw/urltrans.c,v
retrieving revision 1.108
diff -u -r1.108 urltrans.c
--- gw/urltrans.c 24 Jun 2008 11:28:41 -0000 1.108
+++ gw/urltrans.c 8 Sep 2008 17:19:48 -0000
@@ -367,6 +367,15 @@
}
break;
+ case 'D': /* meta_data */
+ if (octstr_len(request->sms.meta_data)) {
+ enc = octstr_duplicate(request->sms.meta_data);
+ octstr_url_encode(enc);
+ octstr_append(result, enc);
+ octstr_destroy(enc);
+ }
+ break;
+
case 'A':
if (request->sms.msgdata) {
enc = octstr_duplicate(request->sms.msgdata);
Index: gw/bb_smscconn.c
===================================================================
RCS file: /home/cvs/gateway/gw/bb_smscconn.c,v
retrieving revision 1.96
diff -u -r1.96 bb_smscconn.c
--- gw/bb_smscconn.c 9 Jan 2008 20:06:56 -0000 1.96
+++ gw/bb_smscconn.c 8 Sep 2008 17:19:44 -0000
@@ -91,6 +91,8 @@
#include "bb_smscconn_cb.h" /* callback functions for connections */
#include "smscconn_p.h" /* to access counters */
+#include "smsc/smpp_pdu.h" /* access smpp_pdu_init/smpp_pdu_shutdown */
+
/* passed from bearerbox core */
extern volatile sig_atomic_t bb_status;
@@ -639,6 +641,10 @@
if (handle_concatenated_mo)
init_concat_handler();
+ /* initialize low level PDUs */
+ if (smpp_pdu_init(cfg) == -1)
+ panic(0, "Connot start with PDU init failed.");
+
smsc_groups = cfg_get_multi_group(cfg, octstr_imm("smsc"));
gwlist_add_producer(smsc_list);
for (i = 0; i < gwlist_len(smsc_groups) &&
@@ -839,6 +845,10 @@
* to shutdown before calling these?
*/
gwlist_remove_producer(incoming_sms);
+
+ /* shutdown low levele PDU things */
+ smpp_pdu_shutdown();
+
return 0;
}
Index: gw/msg-decl.h
===================================================================
RCS file: /home/cvs/gateway/gw/msg-decl.h,v
retrieving revision 1.34
diff -u -r1.34 msg-decl.h
--- gw/msg-decl.h 9 Jan 2008 20:06:57 -0000 1.34
+++ gw/msg-decl.h 8 Sep 2008 17:19:44 -0000
@@ -109,6 +109,7 @@
INTEGER(priority);
INTEGER(resend_try);
INTEGER(resend_time);
+ OCTSTR(meta_data);
})
MSG(ack,
Index: gw/smsbox.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsbox.c,v
retrieving revision 1.278
diff -u -r1.278 smsbox.c
--- gw/smsbox.c 24 Jun 2008 11:28:41 -0000 1.278
+++ gw/smsbox.c 8 Sep 2008 17:19:47 -0000
@@ -506,7 +506,7 @@
int *validity, int *deferred,
int *dlr_mask, Octstr **dlr_url,
Octstr **account, int *pid, int *alt_dcs,
- int *rpi, Octstr **binfo, int *priority)
+ int *rpi, Octstr **binfo, int *priority, Octstr **meta_data)
{
Octstr *name, *val;
long l;
@@ -596,6 +596,10 @@
else if (octstr_case_compare(name, octstr_imm("X-Kannel-Priority")) == 0) {
sscanf(octstr_get_cstr(val),"%d", priority);
}
+ else if (octstr_case_compare(name, octstr_imm("X-Kannel-Meta-Data")) == 0) {
+ *meta_data = octstr_duplicate(val);
+ octstr_strip_blanks(*meta_data);
+ }
octstr_destroy(name);
octstr_destroy(val);
}
@@ -652,7 +656,7 @@
int *dlr_mask, Octstr **dlr_url,
Octstr **account, int *pid, int *alt_dcs,
int *rpi, List **tolist, Octstr **charset,
- Octstr **binfo, int *priority)
+ Octstr **binfo, int *priority, Octstr **meta_data)
{
Octstr *text, *tmp, *tmp2;
@@ -834,6 +838,13 @@
*priority = tmplong;
O_DESTROY(tmp);
}
+
+ /* meta_data */
+ get_tag(*body, octstr_imm("meta-data"), &tmp, 0, 0);
+ if (tmp) {
+ *meta_data = octstr_duplicate(tmp);
+ O_DESTROY(tmp);
+ }
/* charset from <?xml...encoding=?> */
tmp = find_charset_encoding(*body);
@@ -873,7 +884,7 @@
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)
+ Octstr *charset, Octstr *binfo, int priority, Octstr *meta_data)
{
msg->sms.msgdata = replytext;
msg->sms.time = time(NULL);
@@ -1031,6 +1042,16 @@
else
warning(0, "Tried to change priority to '%d', denied.", priority);
}
+
+ if (meta_data != NULL) {
+ if (urltrans_accept_x_kannel_headers(trans)) {
+ octstr_destroy(msg->sms.meta_data);
+ msg->sms.meta_data = meta_data;
+ } else {
+ warning(0, "Tried to set Meta-Data field, denied.");
+ O_DESTROY(meta_data);
+ }
+ }
}
@@ -1106,7 +1127,7 @@
unsigned int queued; /* indicate if processes reply is requeued */
Octstr *reply_body, *charset;
- Octstr *udh, *from, *to, *dlr_url, *account, *smsc, *binfo;
+ Octstr *udh, *from, *to, *dlr_url, *account, *smsc, *binfo, *meta_data;
int dlr_mask, mclass, mwi, coding, compress, pid, alt_dcs, rpi;
int validity, deferred, priority;
@@ -1125,7 +1146,7 @@
break;
octets = 0;
- from = to = udh = smsc = dlr_url = account = binfo = charset = NULL;
+ from = to = udh = smsc = dlr_url = account = binfo = charset = meta_data = NULL;
mclass = mwi = coding = compress = pid = alt_dcs = rpi = dlr_mask
= validity = deferred = priority = SMS_PARAM_UNDEFINED;
@@ -1145,7 +1166,7 @@
&coding, &compress, &validity,
&deferred, &dlr_mask, &dlr_url,
&account, &pid, &alt_dcs, &rpi,
- &binfo, &priority);
+ &binfo, &priority, &meta_data);
} else if (octstr_case_compare(type, text_plain) == 0) {
replytext = octstr_duplicate(reply_body);
octstr_destroy(reply_body);
@@ -1155,7 +1176,7 @@
&coding, &compress, &validity,
&deferred, &dlr_mask, &dlr_url,
&account, &pid, &alt_dcs, &rpi,
- &binfo, &priority);
+ &binfo, &priority, &meta_data);
} else if (octstr_case_compare(type, text_xml) == 0) {
replytext = octstr_duplicate(reply_body);
octstr_destroy(reply_body);
@@ -1164,7 +1185,7 @@
&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);
+ &binfo, &priority, &meta_data);
} else if (octstr_case_compare(type, octet_stream) == 0) {
replytext = octstr_duplicate(reply_body);
octstr_destroy(reply_body);
@@ -1175,7 +1196,7 @@
&coding, &compress, &validity,
&deferred, &dlr_mask, &dlr_url,
&account, &pid, &alt_dcs, &rpi,
- &binfo, &priority);
+ &binfo, &priority, &meta_data);
} else {
replytext = octstr_duplicate(reply_couldnotrepresent);
}
@@ -1195,7 +1216,7 @@
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);
+ binfo, priority, meta_data);
if (final_url == NULL)
final_url = octstr_imm("");
@@ -1458,6 +1479,8 @@
octstr_get_cstr(os));
octstr_destroy(os);
}
+ if (octstr_len(msg->sms.meta_data))
+ http_header_add(request_headers, "X-Kannel-Meta-Data", octstr_get_cstr(msg->sms.meta_data));
id = remember_receiver(msg, trans, HTTP_METHOD_POST, pattern, request_headers, msg->sms.msgdata, 0);
semaphore_down(max_pending_requests);
http_start_request(caller, HTTP_METHOD_POST, pattern, request_headers,
@@ -1615,6 +1638,11 @@
O_DESTROY(tmp);
}
+ /* meta_data */
+ if (octstr_len(msg->sms.meta_data)) {
+ OCTSTR_APPEND_XML(xml, "meta-data", msg->sms.meta_data);
+ }
+
/* End XML */
octstr_append(xml, octstr_imm("\t</submit>\n"));
octstr_append(xml, octstr_imm("</message>\n"));
@@ -1955,7 +1983,7 @@
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)
+ List *receiver, Octstr *binfo, int priority, Octstr *meta_data)
{
Msg *msg = NULL;
Octstr *newfrom = NULL;
@@ -2283,6 +2311,8 @@
goto field_error;
}
+ msg->sms.meta_data = meta_data;
+
msg->sms.receiver = NULL;
/*
@@ -2465,11 +2495,11 @@
URLTranslation *t = NULL;
Octstr *tmp_string;
Octstr *from, *to, *charset, *text, *udh, *smsc, *dlr_url, *account;
- Octstr *binfo;
+ Octstr *binfo, *meta_data;
int dlr_mask, mclass, mwi, coding, compress, validity, deferred, pid;
int alt_dcs, rpi, priority;
- from = to = udh = text = smsc = account = dlr_url = charset = binfo = NULL;
+ from = to = udh = text = smsc = account = dlr_url = charset = binfo = meta_data = NULL;
mclass = mwi = coding = compress = validity = deferred = dlr_mask =
pid = alt_dcs = rpi = priority = SMS_PARAM_UNDEFINED;
@@ -2543,6 +2573,8 @@
if(tmp_string != NULL)
sscanf(octstr_get_cstr(tmp_string),"%d", &priority);
+ meta_data = octstr_duplicate(http_cgi_variable(args, "meta-data"));
+
/*
* we required "to" to be defined
*/
@@ -2561,7 +2593,7 @@
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);
+ pid, alt_dcs, rpi, NULL, binfo, priority, meta_data);
}
@@ -2579,7 +2611,7 @@
List *tolist;
Octstr *text_html, *text_plain, *text_wml, *text_xml, *octet_stream;
Octstr *text;
- Octstr *from, *to, *udh, *smsc, *charset, *dlr_url, *account, *binfo;
+ Octstr *from, *to, *udh, *smsc, *charset, *dlr_url, *account, *binfo, *meta_data;
int dlr_mask, mclass, mwi, coding, compress, validity, deferred;
int pid, alt_dcs, rpi, priority;
@@ -2591,7 +2623,7 @@
user = pass = ret = type = NULL;
tolist = NULL;
- from = to = udh = smsc = account = dlr_url = charset = binfo = NULL;
+ from = to = udh = smsc = account = dlr_url = charset = binfo = meta_data = NULL;
mclass = mwi = coding = compress = validity = deferred = dlr_mask =
pid = alt_dcs = rpi = priority = SMS_PARAM_UNDEFINED;
@@ -2607,7 +2639,7 @@
&coding, &compress, &validity,
&deferred, &dlr_mask, &dlr_url,
&account, &pid, &alt_dcs, &rpi,
- &binfo, &priority);
+ &binfo, &priority, &meta_data);
} else if (octstr_case_compare(type, text_plain) == 0 ||
octstr_case_compare(type, octet_stream) == 0) {
get_x_kannel_from_headers(headers, &from, &to, &udh,
@@ -2615,13 +2647,13 @@
&coding, &compress, &validity,
&deferred, &dlr_mask, &dlr_url,
&account, &pid, &alt_dcs, &rpi,
- &binfo, &priority);
+ &binfo, &priority, &meta_data);
} else if (octstr_case_compare(type, text_xml) == 0) {
get_x_kannel_from_xml(mt_push, &type, &body, headers,
&from, &to, &udh, &user, &pass, &smsc, &mclass,
&mwi, &coding, &compress, &validity, &deferred,
&dlr_mask, &dlr_url, &account, &pid, &alt_dcs,
- &rpi, &tolist, &charset, &binfo, &priority);
+ &rpi, &tolist, &charset, &binfo, &priority, &meta_data);
} else {
*status = HTTP_BAD_REQUEST;
ret = octstr_create("Invalid content-type");
@@ -2672,7 +2704,7 @@
udh, smsc, mclass, mwi, coding, compress,
validity, deferred, status, dlr_mask,
dlr_url, account, pid, alt_dcs, rpi, tolist,
- binfo, priority);
+ binfo, priority, meta_data);
}
error2:
@@ -2685,6 +2717,7 @@
octstr_destroy(dlr_url);
octstr_destroy(account);
octstr_destroy(binfo);
+ octstr_destroy(meta_data);
error:
octstr_destroy(type);
octstr_destroy(charset);
@@ -2707,7 +2740,7 @@
Octstr *client_ip, int *status)
{
Octstr *ret, *type, *user, *pass;
- Octstr *from, *to, *udh, *smsc, *charset, *dlr_url, *account, *binfo;
+ Octstr *from, *to, *udh, *smsc, *charset, *dlr_url, *account, *binfo, *meta_data;
Octstr *output;
Octstr *method_name;
XMLRPCDocument *msg;
@@ -2715,7 +2748,7 @@
int dlr_mask, mclass, mwi, coding, compress, validity,
deferred, pid, alt_dcs, rpi;
- from = to = udh = smsc = account = dlr_url = charset = binfo = NULL;
+ from = to = udh = smsc = account = dlr_url = charset = binfo = meta_data = NULL;
mclass = mwi = coding = compress = validity = deferred = dlr_mask =
pid = alt_dcs = rpi = -1;
Index: gw/smsc/smpp_pdu.h
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smpp_pdu.h,v
retrieving revision 1.14
diff -u -r1.14 smpp_pdu.h
--- gw/smsc/smpp_pdu.h 9 Jan 2008 20:06:53 -0000 1.14
+++ gw/smsc/smpp_pdu.h 8 Sep 2008 17:19:49 -0000
@@ -95,7 +95,7 @@
#define TLV_INTEGER(name, octets) unsigned long name;
#define TLV_NULTERMINATED(name, max_len) Octstr *name;
#define TLV_OCTETS(name, min_len, max_len) Octstr *name;
- #define OPTIONAL_END
+ #define OPTIONAL_END Dict *tlv;
#define INTEGER(name, octets) unsigned long name;
#define NULTERMINATED(name, max_octets) Octstr *name;
#define OCTETS(name, field_giving_octets) Octstr *name;
@@ -224,6 +224,11 @@
SMPP_ESME_RINVBCASTCHANIND = 0x00000112,
};
+/* initialize SMPP PDU */
+int smpp_pdu_init(Cfg *cfg);
+/* shutdown SMPP PDU */
+int smpp_pdu_shutdown(void);
+
SMPP_PDU *smpp_pdu_create(unsigned long type, unsigned long seq_no);
void smpp_pdu_destroy(SMPP_PDU *pdu);
int smpp_pdu_is_valid(SMPP_PDU *pdu); /* XXX */
Index: gw/smsc/smpp_pdu.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smpp_pdu.c,v
retrieving revision 1.29
diff -u -r1.29 smpp_pdu.c
--- gw/smsc/smpp_pdu.c 9 Jan 2008 20:06:52 -0000 1.29
+++ gw/smsc/smpp_pdu.c 8 Sep 2008 17:19:49 -0000
@@ -71,6 +71,118 @@
#define MAX_SMPP_PDU_LEN (7424)
+struct smpp_tlv {
+ Octstr *name;
+ long tag;
+ long length;
+ enum { SMPP_TLV_OCTETS = 0, SMPP_TLV_NULTERMINATED = 1, SMPP_TLV_INTEGER = 2 } type;
+};
+
+
+static Dict *tlv_by_tag;
+static Dict *tlv_by_name;
+static int initialized;
+
+
+static void smpp_tlv_destroy(struct smpp_tlv *tlv)
+{
+ if (tlv == NULL)
+ return;
+ octstr_destroy(tlv->name);
+ gw_free(tlv);
+}
+
+
+int smpp_pdu_init(Cfg *cfg)
+{
+ CfgGroup *grp;
+ List *l;
+
+ if (initialized)
+ return 0;
+
+
+ l = cfg_get_multi_group(cfg, octstr_imm("smpp-tlv"));
+ tlv_by_tag = dict_create(gwlist_len(l) > 0 ? gwlist_len(l) : 1, (void(*)(void*))smpp_tlv_destroy);
+ tlv_by_name = dict_create(gwlist_len(l) > 0 ? gwlist_len(l) : 1, NULL);
+ while (l != NULL && (grp = gwlist_extract_first(l)) != NULL) {
+ struct smpp_tlv *tlv;
+ Octstr *tmp;
+
+ tlv = gw_malloc(sizeof(*tlv));
+ if ((tlv->name = cfg_get(grp, octstr_imm("name"))) == NULL) {
+ error(0, "SMPP: Unable to get name for smpp-tlv.");
+ smpp_tlv_destroy(tlv);
+ goto failed;
+ }
+ if (cfg_get_integer(&tlv->tag, grp, octstr_imm("tag")) == -1) {
+ error(0, "SMPP: Unable to get tag for smpp-tlv.");
+ smpp_tlv_destroy(tlv);
+ goto failed;
+ }
+ if (cfg_get_integer(&tlv->length, grp, octstr_imm("length")) == -1) {
+ error(0, "SMPP: Unable to get length for smpp-tlv.");
+ smpp_tlv_destroy(tlv);
+ goto failed;
+ }
+ if ((tmp = cfg_get(grp, octstr_imm("type"))) == NULL) {
+ error(0, "SMPP: Unable to get type for smpp-tlv.");
+ smpp_tlv_destroy(tlv);
+ goto failed;
+ }
+ if (octstr_str_case_compare(tmp, "octetstring") == 0)
+ tlv->type = SMPP_TLV_OCTETS;
+ else if (octstr_str_case_compare(tmp, "nulterminated") == 0)
+ tlv->type = SMPP_TLV_NULTERMINATED;
+ else if (octstr_str_case_compare(tmp, "integer") == 0)
+ tlv->type = SMPP_TLV_INTEGER;
+ else {
+ error(0, "SMPP: Unknown type for smpp-tlv: `%s'", octstr_get_cstr(tmp));
+ octstr_destroy(tmp);
+ smpp_tlv_destroy(tlv);
+ goto failed;
+ }
+ octstr_destroy(tmp);
+ /* put into dict */
+ if (!dict_put_once(tlv_by_name, tlv->name, tlv)) {
+ error(0, "SMPP: Double TLV name %s found.", octstr_get_cstr(tlv->name));
+ smpp_tlv_destroy(tlv);
+ goto failed;
+ }
+ tmp = octstr_format("%ld", tlv->tag);
+ if (!dict_put_once(tlv_by_tag, tmp, tlv)) {
+ error(0, "SMPP: Double TLV tag %s found.", octstr_get_cstr(tmp));
+ octstr_destroy(tmp);
+ goto failed;
+ }
+ octstr_destroy(tmp);
+ }
+ gwlist_destroy(l, NULL);
+
+ initialized = 1;
+ return 0;
+
+failed:
+ dict_destroy(tlv_by_tag);
+ dict_destroy(tlv_by_name);
+ return -1;
+}
+
+
+int smpp_pdu_shutdown(void)
+{
+ if (initialized == 0)
+ return 0;
+
+ initialized = 0;
+ dict_destroy(tlv_by_tag);
+ dict_destroy(tlv_by_name);
+ tlv_by_tag = tlv_by_name = NULL;
+
+ return 0;
+}
+
+
static long decode_integer(Octstr *os, long pos, int octets)
{
unsigned long u;
@@ -101,7 +213,7 @@
long nul;
*data = NULL;
-
+
nul = octstr_search_char(os, '\0', *pos);
if (nul == -1) {
warning(0, "SMPP: PDU NULL terminated string (%s) has no NULL.", field_name);
@@ -129,7 +241,7 @@
#define TLV_INTEGER(name, octets) p->name = -1;
#define TLV_NULTERMINATED(name, max_len) p->name = NULL;
#define TLV_OCTETS(name, min_len, max_len) p->name = NULL;
- #define OPTIONAL_END
+ #define OPTIONAL_END p->tlv = dict_create(1024, octstr_destroy_item);
#define INTEGER(name, octets) p->name = 0;
#define NULTERMINATED(name, max_octets) p->name = NULL;
#define OCTETS(name, field_giving_octetst) p->name = NULL;
@@ -161,7 +273,7 @@
#define TLV_INTEGER(name, octets) p->name = -1;
#define TLV_NULTERMINATED(name, max_octets) octstr_destroy(p->name);
#define TLV_OCTETS(name, min_len, max_len) octstr_destroy(p->name);
- #define OPTIONAL_END
+ #define OPTIONAL_END dict_destroy(p->tlv);
#define INTEGER(name, octets) p->name = 0; /* Make sure "p" is used */
#define NULTERMINATED(name, max_octets) octstr_destroy(p->name);
#define OCTETS(name, field_giving_octets) octstr_destroy(p->name);
@@ -230,7 +342,55 @@
octstr_append(os, p->name); \
} \
}
- #define OPTIONAL_END
+ #define OPTIONAL_END \
+ if (p->tlv != NULL) { \
+ Octstr *key; \
+ List *keys; \
+ struct smpp_tlv *tlv; \
+ keys = dict_keys(p->tlv); \
+ while(keys != NULL && (key = gwlist_extract_first(keys)) != NULL) { \
+ tlv = dict_get(tlv_by_name, key); \
+ if (tlv == NULL) { \
+ error(0, "SMPP: Unknown TLV `%s', don't send.", octstr_get_cstr(key)); \
+ octstr_destroy(key); \
+ continue; \
+ } \
+ switch(tlv->type) { \
+ case SMPP_TLV_INTEGER: { \
+ long val = atol(octstr_get_cstr(dict_get(p->tlv, key))); \
+ append_encoded_integer(os, tlv->tag, 2); \
+ append_encoded_integer(os, tlv->length, 2); \
+ append_encoded_integer(os, val, tlv->length); \
+ break; \
+ } \
+ case SMPP_TLV_OCTETS: \
+ case SMPP_TLV_NULTERMINATED: { \
+ Octstr *val = dict_get(p->tlv, key); \
+ unsigned long len = octstr_len(val); \
+ if (len > tlv->length) { \
+ error(0, "SMPP: Optional field (%s) with invalid length (%ld) (should be %ld) dropped.", \
+ octstr_get_cstr(key), len, tlv->length);\
+ octstr_destroy(key); \
+ continue; \
+ } \
+ append_encoded_integer(os, tlv->tag, 2); \
+ if (tlv->type == SMPP_TLV_NULTERMINATED) \
+ append_encoded_integer(os, len + 1, 2); \
+ else \
+ append_encoded_integer(os, len, 2); \
+ octstr_append(os, val); \
+ if (tlv->type == SMPP_TLV_NULTERMINATED) \
+ octstr_append_char(os, '\0'); \
+ break; \
+ } \
+ default: \
+ panic(0, "SMPP: Internal error, unknown configured TLV type %d.", tlv->type); \
+ break; \
+ } \
+ octstr_destroy(key); \
+ } \
+ gwlist_destroy(keys, octstr_destroy_item); \
+ }
#define INTEGER(name, octets) \
append_encoded_integer(os, p->name, octets);
#define NULTERMINATED(name, max_octets) \
@@ -332,13 +492,57 @@
} else
#define OPTIONAL_END \
{ \
- Octstr *val = octstr_copy(data_without_len, pos, opt_len); \
- if (val) octstr_binary_to_hex(val, 0); \
- else val = octstr_create(""); \
- warning(0, "SMPP: Unknown TLV(0x%04lx,0x%04lx,%s) for PDU type (%s) received!", \
- opt_tag, opt_len, octstr_get_cstr(val), pdu->type_name); \
- pos += opt_len; \
- octstr_destroy(val); \
+ Octstr *val = NULL; \
+ struct smpp_tlv *tlv; \
+ Octstr *tmp = octstr_format("%ld", opt_tag); \
+ /* check configured TLVs */ \
+ tlv = dict_get(tlv_by_tag, tmp); \
+ octstr_destroy(tmp); \
+ if (tlv != NULL) { \
+ /* found configured tlv */ \
+ /* check length */ \
+ if (opt_len > tlv->length) { \
+ error(0, "SMPP: Optional field (%s) with invalid length (%ld) (should be %ld) dropped.", \
+ octstr_get_cstr(tlv->name), opt_len, tlv->length); \
+ pos += opt_len; \
+ continue; \
+ } \
+ switch (tlv->type) { \
+ case SMPP_TLV_INTEGER: { \
+ long val_i; \
+ if ((val_i = decode_integer(data_without_len, pos, opt_len)) == -1) \
+ goto err; \
+ val = octstr_format("%ld", val_i); \
+ dict_put(p->tlv, tlv->name, val); \
+ pos += opt_len; \
+ break; \
+ } \
+ case SMPP_TLV_OCTETS: { \
+ val = octstr_copy(data_without_len, pos, opt_len); \
+ dict_put(p->tlv, tlv->name, val); \
+ pos += opt_len; \
+ break; \
+ } \
+ case SMPP_TLV_NULTERMINATED: { \
+ if (copy_until_nul(octstr_get_cstr(tlv->name), data_without_len, &pos, opt_len, &val) == 0) \
+ dict_put(p->tlv, tlv->name, val); \
+ break; \
+ } \
+ default: \
+ panic(0, "SMPP: Internal error, unknown configured TLV type %d.", tlv->type); \
+ break; \
+ } \
+ } else { \
+ val = octstr_copy(data_without_len, pos, opt_len); \
+ if (val) \
+ octstr_binary_to_hex(val, 0); \
+ else \
+ val = octstr_create(""); \
+ warning(0, "SMPP: Unknown TLV(0x%04lx,0x%04lx,%s) for PDU type (%s) received!", \
+ opt_tag, opt_len, octstr_get_cstr(val), pdu->type_name); \
+ octstr_destroy(val); \
+ pos += opt_len; \
+ } \
} \
} \
}
@@ -394,7 +598,17 @@
if (p->name != NULL) { \
OCTETS(name, max_len) \
}
- #define OPTIONAL_END
+ #define OPTIONAL_END \
+ if (p->tlv != NULL) { \
+ List *keys; \
+ Octstr *key; \
+ keys = dict_keys(p->tlv); \
+ while(keys != NULL && (key = gwlist_extract_first(keys)) != NULL) { \
+ octstr_dump_short(dict_get(p->tlv, key), 2, octstr_get_cstr(key)); \
+ octstr_destroy(key); \
+ } \
+ gwlist_destroy(keys, octstr_destroy_item); \
+ }
#define INTEGER(name, octets) \
debug("sms.smpp", 0, " %s: %lu = 0x%08lx", #name, p->name, p->name);
#define NULTERMINATED(name, max_octets) \
Index: gw/smsc/smsc_smpp.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_smpp.c,v
retrieving revision 1.104
diff -u -r1.104 smsc_smpp.c
--- gw/smsc/smsc_smpp.c 24 Aug 2008 15:44:00 -0000 1.104
+++ gw/smsc/smsc_smpp.c 8 Sep 2008 17:19:52 -0000
@@ -78,6 +78,7 @@
#include "sms.h"
#include "dlr.h"
#include "bearerbox.h"
+#include "meta_data.h"
#define SMPP_DEFAULT_CHARSET "UTF-8"
@@ -559,6 +560,10 @@
/* set priority flag */
msg->sms.priority = pdu->u.deliver_sm.priority_flag;
+ if (msg->sms.meta_data == NULL)
+ msg->sms.meta_data = octstr_create("");
+ meta_data_set_values(msg->sms.meta_data, pdu->u.deliver_sm.tlv, "smpp");
+
return msg;
error:
@@ -705,6 +710,10 @@
}
}
+ if (msg->sms.meta_data == NULL)
+ msg->sms.meta_data = octstr_create("");
+ meta_data_set_values(msg->sms.meta_data, pdu->u.data_sm.tlv, "smpp");
+
return msg;
error:
@@ -929,6 +938,9 @@
if (smpp->version > 0x33 && msg->sms.msg_left > 0)
pdu->u.submit_sm.more_messages_to_send = 1;
+ dict_destroy(pdu->u.submit_sm.tlv);
+ pdu->u.submit_sm.tlv = meta_data_get_values(msg->sms.meta_data, "smpp");
+
return pdu;
}
@@ -1352,7 +1364,12 @@
octstr_get_cstr(smpp->conn->id));
dlrmsg = handle_dlr(smpp, pdu->u.data_sm.source_addr, NULL, pdu->u.data_sm.message_payload,
pdu->u.data_sm.receipted_message_id, pdu->u.data_sm.message_state);
+
if (dlrmsg != NULL) {
+ if (dlrmsg->sms.meta_data == NULL)
+ dlrmsg->sms.meta_data = octstr_create("");
+ meta_data_set_values(dlrmsg->sms.meta_data, pdu->u.data_sm.tlv, "smpp");
+
/* passing DLR to upper layer */
reason = bb_smscconn_receive(smpp->conn, dlrmsg);
} else {
@@ -1407,12 +1424,18 @@
dlrmsg = handle_dlr(smpp, pdu->u.deliver_sm.source_addr, pdu->u.deliver_sm.short_message, pdu->u.deliver_sm.message_payload,
pdu->u.deliver_sm.receipted_message_id, pdu->u.deliver_sm.message_state);
- resp = smpp_pdu_create(deliver_sm_resp,
- pdu->u.deliver_sm.sequence_number);
- if (dlrmsg != NULL)
+
+ resp = smpp_pdu_create(deliver_sm_resp, pdu->u.deliver_sm.sequence_number);
+
+ if (dlrmsg != NULL) {
+ if (dlrmsg->sms.meta_data == NULL)
+ dlrmsg->sms.meta_data = octstr_create("");
+ meta_data_set_values(dlrmsg->sms.meta_data, pdu->u.deliver_sm.tlv, "smpp");
+
reason = bb_smscconn_receive(smpp->conn, dlrmsg);
- else
+ } else {
reason = SMSCCONN_SUCCESS;
+ }
resp->u.deliver_sm_resp.command_status = smscconn_failure_reason_to_smpp_status(reason);
} else {/* MO-SMS */
resp = smpp_pdu_create(deliver_sm_resp,
Index: doc/userguide/userguide.xml
===================================================================
RCS file: /home/cvs/gateway/doc/userguide/userguide.xml,v
retrieving revision 1.337
diff -u -r1.337 userguide.xml
--- doc/userguide/userguide.xml 28 Jul 2008 20:19:37 -0000 1.337
+++ doc/userguide/userguide.xml 8 Sep 2008 17:19:42 -0000
@@ -6130,6 +6130,10 @@
the delivery report value
</entry></row>
+<row><entry><literal>%D</literal></entry><entry>
+the Meta-Data value
+</entry></row>
+
<row><entry><literal>%A</literal></entry><entry>
the delivery report SMSC reply, if any
</entry></row>
Index: gwlib/cfg.def
===================================================================
RCS file: /home/cvs/gateway/gwlib/cfg.def,v
retrieving revision 1.134
diff -u -r1.134 cfg.def
--- gwlib/cfg.def 26 Jul 2008 09:21:17 -0000 1.134
+++ gwlib/cfg.def 8 Sep 2008 17:19:52 -0000
@@ -582,6 +582,16 @@
OCTSTR(unified-prefix)
)
+
+MULTI_GROUP(smpp-tlv,
+ OCTSTR(name)
+ OCTSTR(tag)
+ OCTSTR(length)
+ OCTSTR(type)
+ OCTSTR(smsc-id)
+)
+
+
#undef OCTSTR
#undef SINGLE_GROUP
#undef MULTI_GROUP
Index: gw/meta_data.c
===================================================================
RCS file: gw/meta_data.c
diff -N gw/meta_data.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gw/meta_data.c 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,376 @@
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2004 Kannel Group
+ * Copyright (c) 1998-2001 WapIT Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Kannel Group (http://www.kannel.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Kannel" and "Kannel Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please
+ * contact [email protected].
+ *
+ * 5. Products derived from this software may not be called "Kannel",
+ * nor may "Kannel" appear in their name, without prior written
+ * permission of the Kannel Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Kannel Group. For more information on
+ * the Kannel Group, please see <http://www.kannel.org/>.
+ *
+ * Portions of this software are based upon software originally written at
+ * WapIT Ltd., Helsinki, Finland for the Kannel project.
+ */
+
+/*
+ * meta_data.h
+ *
+ * Meta Data manupilation.
+ *
+ * Alexander Malysh <amalysh kannel.org>, 2007
+ */
+
+#include "gwlib/gwlib.h"
+#include "meta_data.h"
+
+
+struct meta_data {
+ Octstr *group;
+ Dict *values;
+ struct meta_data *next;
+};
+
+
+static struct meta_data *meta_data_create(void)
+{
+ struct meta_data *ret;
+
+ ret = gw_malloc(sizeof(*ret));
+ ret->group = NULL;
+ ret->values = NULL;
+ ret->next = NULL;
+
+ return ret;
+}
+
+
+static void meta_data_destroy(struct meta_data *meta)
+{
+ struct meta_data *next;
+
+ if (meta == NULL)
+ return;
+
+ do {
+ next = meta->next;
+ octstr_destroy(meta->group);
+ dict_destroy(meta->values);
+ gw_free(meta);
+ meta = next;
+ } while(meta != NULL);
+}
+
+
+/* format: ?group-name?key=value&key=value?group?... group, key, value are urlencoded */
+static struct meta_data *meta_data_unpack(const Octstr *data)
+{
+ struct meta_data *ret = NULL, *curr = NULL;
+ const char *str;
+ long pos;
+ Octstr *key = NULL;
+ int type, next_type;
+ long start, end;
+
+ start = end = -1;
+ type = next_type = -1;
+ for (pos = 0, str = octstr_get_cstr(data); pos <= octstr_len(data); str++, pos++) {
+ switch(*str) {
+ case '?':
+ if (start == -1) { /* start of str */
+ start = pos;
+ type = 0;
+ } else if (type == 0) { /* end of group */
+ end = pos;
+ next_type = 1;
+ } else if (type == 2 && key != NULL) { /* end of value */
+ end = pos;
+ next_type = 0;
+ } else if (key == NULL) { /* start of next group without key and value */
+ start = pos;
+ type = 0;
+ } else {
+ /* FAILED */
+ error(0, "MDATA: Found '?' but not expected it end=%ld start=%ld type=%d.", end, start, type);
+ meta_data_destroy(ret);
+ octstr_destroy(key);
+ return NULL;
+ }
+ break;
+ case '=':
+ if (type == 1 && curr != NULL && key == NULL) { /* end of key */
+ end = pos;
+ next_type = 2;
+ } else {
+ /* FAILED */
+ error(0, "MDATA: Found '=' but not expected it end=%ld start=%ld type=%d.", end, start, type);
+ meta_data_destroy(ret);
+ octstr_destroy(key);
+ return NULL;
+ }
+ break;
+ case '&':
+ if (type == 2 && curr != NULL && key != NULL) { /* end of value */
+ end = pos;
+ next_type = 1;
+ } else if (type == 1 && key == NULL) { /* just & skip it */
+ start = pos;
+ } else {
+ /* FAILED */
+ error(0, "MDATA: Found '&' but not expected it end=%ld start=%ld type=%d.", end, start, type);
+ meta_data_destroy(ret);
+ octstr_destroy(key);
+ return NULL;
+ }
+ break;
+ case '\0':
+ if (type == 2) /* end of value */
+ end = pos;
+ break;
+ }
+ if (start >= 0 && end >= 0) {
+ Octstr *tmp;
+
+ if (end - start - 1 == 0)
+ tmp = octstr_create("");
+ else
+ tmp = octstr_create_from_data(str - end + start + 1, end - start - 1);
+
+ octstr_url_decode(tmp);
+
+ switch(type) {
+ case 0: /* group */
+ if (curr == NULL) {
+ curr = gw_malloc(sizeof(*curr));
+ } else {
+ curr->next = gw_malloc(sizeof(*curr));
+ curr = curr->next;
+ }
+ curr->group = tmp;
+ tmp = NULL;
+ curr->values = dict_create(1024, octstr_destroy_item);
+ curr->next = NULL;
+ if (ret == NULL)
+ ret = curr;
+ debug("meta_data", 0, "new group created `%s'", octstr_get_cstr(curr->group));
+ break;
+ case 1: /* key */
+ key = tmp;
+ tmp = NULL;
+ break;
+ case 2: /* value */
+ debug("meta_data", 0, "group=`%s' key=`%s' value=`%s'", octstr_get_cstr(curr->group),
+ octstr_get_cstr(key), octstr_get_cstr(tmp));
+ dict_put(curr->values, key, tmp);
+ tmp = NULL;
+ octstr_destroy(key);
+ key = NULL;
+ break;
+ }
+ octstr_destroy(tmp);
+ type = next_type;
+ next_type = -1;
+ start = end;
+ end = -1;
+ }
+ }
+ octstr_destroy(key);
+
+ return ret;
+}
+
+
+static int meta_data_pack(struct meta_data *mdata, Octstr *data)
+{
+ List *l;
+ Octstr *tmp;
+
+ if (mdata == NULL || data == NULL)
+ return -1;
+ /* clear data */
+ octstr_delete(data, 0, octstr_len(data));
+ do {
+ octstr_format_append(data, "?%E?", mdata->group);
+ l = dict_keys(mdata->values);
+ while(l != NULL && (tmp = gwlist_extract_first(l)) != NULL) {
+ octstr_format_append(data, "%E=%E&", tmp, dict_get(mdata->values, tmp));
+ octstr_destroy(tmp);
+ }
+ gwlist_destroy(l, octstr_destroy_item);
+ mdata = mdata->next;
+ } while(mdata != NULL);
+
+ return 0;
+}
+
+
+Dict *meta_data_get_values(const Octstr *data, const char *group)
+{
+ struct meta_data *mdata, *curr;
+ Dict *ret = NULL;
+
+ if (data == NULL || group == NULL)
+ return NULL;
+
+ mdata = meta_data_unpack(data);
+ if (mdata == NULL)
+ return NULL;
+ for (curr = mdata; curr != NULL; curr = curr->next) {
+ if (octstr_str_case_compare(curr->group, group) == 0) {
+ ret = curr->values;
+ curr->values = NULL;
+ break;
+ }
+ }
+
+ meta_data_destroy(mdata);
+
+ return ret;
+}
+
+
+int meta_data_set_values(Octstr *data, const Dict *dict, const char *group)
+{
+ struct meta_data *mdata, *curr;
+ int i;
+
+ if (data == NULL || group == NULL)
+ return -1;
+
+ mdata = meta_data_unpack(data);
+ for (curr = mdata; curr != NULL; curr = curr->next) {
+ if (octstr_str_case_compare(curr->group, group) == 0) {
+ dict_destroy(curr->values);
+ curr->values = (Dict*)dict;
+ break;
+ }
+ }
+
+ if (curr == NULL) {
+ curr = meta_data_create();
+ curr->group = octstr_create(group);
+ curr->values = (Dict*)dict;
+ curr->next = NULL;
+ if (mdata == NULL) {
+ mdata = curr;
+ } else {
+ curr->next = mdata->next;
+ mdata->next = curr;
+ }
+ }
+ i = meta_data_pack(mdata, data);
+ curr->values = NULL;
+
+ meta_data_destroy(mdata);
+
+ return i;
+}
+
+
+int meta_data_set_value(Octstr *data, const char *group, const Octstr *key, const Octstr *value, int replace)
+{
+ struct meta_data *mdata, *curr;
+ int ret = 0;
+
+ if (data == NULL || group == NULL || value == NULL)
+ return -1;
+
+ mdata = meta_data_unpack(data);
+ for (curr = mdata; curr != NULL; curr = curr->next) {
+ if (octstr_str_case_compare(curr->group, group) == 0)
+ break;
+ }
+ if (curr == NULL) {
+ /* group doesn't exists */
+ curr = meta_data_create();
+ curr->group = octstr_create(group);
+ curr->values = dict_create(10, octstr_destroy_item);
+ if (mdata != NULL) {
+ curr->next = mdata->next;
+ mdata->next = curr;
+ } else {
+ mdata = curr;
+ }
+ }
+ if (replace) {
+ /* delete old value if any */
+ dict_put(curr->values, key, NULL);
+ /* put new value */
+ dict_put(curr->values, key, octstr_duplicate(value));
+ } else if (dict_get(curr->values, key) == NULL) {
+ /* put new value */
+ dict_put(curr->values, key, octstr_duplicate(value));
+ }
+
+ /* pack it */
+ ret = meta_data_pack(mdata, data);
+
+ meta_data_destroy(mdata);
+
+ return ret;
+}
+
+
+Octstr *meta_data_get_value(Octstr *data, const char *group, const Octstr *key)
+{
+ struct meta_data *mdata, *curr;
+ Octstr *ret = NULL;
+
+ if (data == NULL || group == NULL || key == NULL)
+ return NULL;
+
+ mdata = meta_data_unpack(data);
+ if (mdata == NULL)
+ return NULL;
+ for (curr = mdata; curr != NULL; curr = curr->next) {
+ if (octstr_str_case_compare(curr->group, group) == 0) {
+ ret = dict_remove(curr->values, key);
+ break;
+ }
+ }
+
+ meta_data_destroy(mdata);
+
+ return ret;
+}
Index: gw/meta_data.h
===================================================================
RCS file: gw/meta_data.h
diff -N gw/meta_data.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gw/meta_data.h 1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,85 @@
+/* ====================================================================
+ * The Kannel Software License, Version 1.0
+ *
+ * Copyright (c) 2001-2004 Kannel Group
+ * Copyright (c) 1998-2001 WapIT Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Kannel Group (http://www.kannel.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Kannel" and "Kannel Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please
+ * contact [email protected].
+ *
+ * 5. Products derived from this software may not be called "Kannel",
+ * nor may "Kannel" appear in their name, without prior written
+ * permission of the Kannel Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Kannel Group. For more information on
+ * the Kannel Group, please see <http://www.kannel.org/>.
+ *
+ * Portions of this software are based upon software originally written at
+ * WapIT Ltd., Helsinki, Finland for the Kannel project.
+ */
+
+/*
+ * meta_data.h
+ *
+ * Meta Data manupilation.
+ *
+ * Alexander Malysh <amalysh kannel.org>, 2007
+ */
+
+#ifndef META_DATA_H
+#define META_DATA_H
+
+/**
+ * Get Dictionary with all values for this group.
+ */
+Dict *meta_data_get_values(const Octstr *data, const char *group);
+/**
+ * Replace Dictionary for the given group.
+ */
+int meta_data_set_values(Octstr *data, const Dict *dict, const char *group);
+/**
+ * Set or replace value for a given group and key.
+ */
+int meta_data_set_value(Octstr *data, const char *group, const Octstr *key, const Octstr *value, int replace);
+/**
+ * Get value for a given group and key.
+ */
+Octstr *meta_data_get_value(Octstr *data, const char *group, const Octstr *key);
+
+#endif