diff --git a/debian/patches/series b/debian/patches/series
index 5af67d914..c317eb3bd 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -67,6 +67,8 @@ upstream/pv_headers-use-tm.t_find-API.patch
upstream/tm-added-t_unset-intermodule-API-function.patch
upstream/pv_headers-use-t_unset-based-on-vref-instead-of-rese.patch
sipwise/pv_headers-rework-pvh_remove_header_param-take-two.patch
+sipwise/bigger_short_codes.patch
+
#
### Don't just put stuff in any order
### use gbp pq import/export tooling to help maintain patches
diff --git a/debian/patches/sipwise/bigger_short_codes.patch b/debian/patches/sipwise/bigger_short_codes.patch
new file mode 100644
index 000000000..4c33fee52
--- /dev/null
+++ b/debian/patches/sipwise/bigger_short_codes.patch
@@ -0,0 +1,351 @@
+--- a/src/modules/speeddial/sdlookup.c
++++ b/src/modules/speeddial/sdlookup.c
+@@ -58,6 +58,44 @@ static inline int rewrite_ruri(struct si
+ return 0;
+ }
+
++int process_result(db1_res_t* db_res, str *ret, char *buffer)
++{
++ ret->s=buffer+4;
++ if (RES_ROW_N(db_res) > 1)
++ {
++ LM_WARN("too many similar results, returning first result\n");
++ }
++ switch(RES_ROWS(db_res)[0].values[0].type)
++ {
++ case DB1_STRING:
++ strcpy(ret->s,
++ (char*)RES_ROWS(db_res)[0].values[0].val.string_val);
++ ret->len = strlen(ret->s);
++ break;
++ case DB1_STR:
++ strncpy(ret->s,
++ (char*)RES_ROWS(db_res)[0].values[0].val.str_val.s,
++ RES_ROWS(db_res)[0].values[0].val.str_val.len);
++ ret->len = RES_ROWS(db_res)[0].values[0].val.str_val.len;
++ ret->s[ret->len] = '\0';
++ break;
++ case DB1_BLOB:
++ strncpy(ret->s,
++ (char*)RES_ROWS(db_res)[0].values[0].val.blob_val.s,
++ RES_ROWS(db_res)[0].values[0].val.blob_val.len);
++ ret->len = RES_ROWS(db_res)[0].values[0].val.blob_val.len;
++ ret->s[ret->len] = '\0';
++ break;
++ default:
++ LM_ERR("unknown type of DB new_uri column\n");
++ if (db_funcs.free_result(db_handle, db_res) < 0) {
++ LM_DBG("failed to free result of query\n");
++ }
++ return -1;
++ }
++ return 1;
++}
++
+ /**
+ *
+ */
+@@ -175,34 +213,127 @@ int sd_lookup_owner(sip_msg_t* _msg, str
+ return -1;
+ }
+
+- user_s.s = useruri_buf+4;
+- switch(RES_ROWS(db_res)[0].values[0].type)
++ if(process_result(db_res, &user_s, useruri_buf) < 0)
+ {
+- case DB1_STRING:
+- strcpy(user_s.s,
+- (char*)RES_ROWS(db_res)[0].values[0].val.string_val);
+- user_s.len = strlen(user_s.s);
+- break;
+- case DB1_STR:
+- strncpy(user_s.s,
+- (char*)RES_ROWS(db_res)[0].values[0].val.str_val.s,
+- RES_ROWS(db_res)[0].values[0].val.str_val.len);
+- user_s.len = RES_ROWS(db_res)[0].values[0].val.str_val.len;
+- user_s.s[user_s.len] = '\0';
+- break;
+- case DB1_BLOB:
+- strncpy(user_s.s,
+- (char*)RES_ROWS(db_res)[0].values[0].val.blob_val.s,
+- RES_ROWS(db_res)[0].values[0].val.blob_val.len);
+- user_s.len = RES_ROWS(db_res)[0].values[0].val.blob_val.len;
+- user_s.s[user_s.len] = '\0';
+- break;
+- default:
+- LM_ERR("unknown type of DB new_uri column\n");
+- if (db_funcs.free_result(db_handle, db_res) < 0) {
+- LM_DBG("failed to free result of query\n");
+- }
+- goto err_server;
++ LM_DBG("failed to process result\n");
++ return -1;
++ }
++
++ /* check 'sip:' */
++ if(user_s.len<4 || strncmp(user_s.s, "sip:", 4))
++ {
++ memcpy(useruri_buf, "sip:", 4);
++ user_s.s -= 4;
++ user_s.len += 4;
++ }
++
++ /**
++ * Free the result because we don't need it anymore
++ */
++ if (db_funcs.free_result(db_handle, db_res) < 0) {
++ LM_DBG("failed to free result of query\n");
++ }
++
++ /* set the URI */
++ LM_DBG("URI of sd from R-URI [%s]\n", user_s.s);
++ if(rewrite_ruri(_msg, user_s.s)<0)
++ {
++ LM_ERR("failed to replace the R-URI\n");
++ goto err_server;
++ }
++
++ return 1;
++
++err_server:
++ return -1;
++}
++
++
++int sd_lookup_group(sip_msg_t* _msg, str* stable, str* sgroup)
++{
++ str user_s, table_s;
++ int nr_keys;
++ db_key_t db_keys[3];
++ db_val_t db_vals[3];
++ db_key_t db_cols[1];
++ db1_res_t* db_res = NULL;
++
++ if(stable==NULL || stable->s==NULL || stable->len<=0)
++ {
++ LM_ERR("invalid table parameter");
++ return -1;
++ }
++ table_s = *stable;
++
++ if(sgroup==NULL || sgroup->s==NULL || sgroup->len<=0)
++ {
++ LM_ERR("invalid group parameter");
++ return -1;
++ }
++
++ /* init */
++ nr_keys = 0;
++ db_cols[0]=&new_uri_column;
++
++ /* take sd from r-uri */
++ if (parse_sip_msg_uri(_msg) < 0)
++ {
++ LM_ERR("failed to parsing Request-URI\n");
++ goto err_server;
++ }
++
++ db_keys[nr_keys]=&sd_user_column;
++ db_vals[nr_keys].type = DB1_STR;
++ db_vals[nr_keys].nul = 0;
++ db_vals[nr_keys].val.str_val.s = _msg->parsed_uri.user.s;
++ db_vals[nr_keys].val.str_val.len = _msg->parsed_uri.user.len;
++ nr_keys++;
++
++ if(use_domain>=2)
++ {
++ db_keys[nr_keys]=&sd_domain_column;
++ db_vals[nr_keys].type = DB1_STR;
++ db_vals[nr_keys].nul = 0;
++ db_vals[nr_keys].val.str_val.s = _msg->parsed_uri.host.s;
++ db_vals[nr_keys].val.str_val.len = _msg->parsed_uri.host.len;
++
++ if (dstrip_s.s!=NULL && dstrip_s.len>0
++ && dstrip_s.len<_msg->parsed_uri.host.len
++ && strncasecmp(_msg->parsed_uri.host.s,dstrip_s.s,dstrip_s.len)==0)
++ {
++ db_vals[nr_keys].val.str_val.s += dstrip_s.len;
++ db_vals[nr_keys].val.str_val.len -= dstrip_s.len;
++ }
++ nr_keys++;
++ }
++
++
++ db_keys[nr_keys]=&group_column;
++ db_vals[nr_keys].type = DB1_STR;
++ db_vals[nr_keys].nul = 0;
++ db_vals[nr_keys].val.str_val = *sgroup;
++ nr_keys++;
++
++ db_funcs.use_table(db_handle, &table_s);
++ if(db_funcs.query(db_handle, db_keys, NULL, db_vals, db_cols,
++ nr_keys /*no keys*/, 1 /*no cols*/, NULL, &db_res)!=0 || db_res==NULL)
++ {
++ LM_ERR("failed to query database\n");
++ goto err_server;
++ }
++
++ if(RES_ROW_N(db_res) <= 0 || RES_ROWS(db_res)[0].values[0].nul != 0) {
++ LM_DBG("no sip address found for R-URI\n");
++ if(db_funcs.free_result(db_handle, db_res) < 0) {
++ LM_DBG("failed to free result of query\n");
++ }
++ goto err_server;
++ }
++
++ if(process_result(db_res, &user_s, useruri_buf) < 0)
++ {
++ LM_DBG("failed to process result\n");
++ goto err_server;
+ }
+
+ /* check 'sip:' */
+@@ -259,3 +390,24 @@ int w_sd_lookup(struct sip_msg* _msg, ch
+
+ return sd_lookup_owner(_msg, &table_s, NULL);
+ }
++
++/**
++ *
++ */
++int w_sd_lookup_group(struct sip_msg* _msg, char* _table, char* _group)
++{
++ str table_s, group_s;
++
++ if(_table==NULL || fixup_get_svalue(_msg, (gparam_p)_table, &table_s)!=0)
++ {
++ LM_ERR("invalid table parameter");
++ return -1;
++ }
++
++ if(_group==NULL ||fixup_get_svalue(_msg, (gparam_p)_group, &group_s)!=0)
++ {
++ LM_ERR("invalid group parameter");
++ return -1;
++ }
++ return sd_lookup_group(_msg, &table_s, &group_s);
++}
+--- a/src/modules/speeddial/speeddial.c
++++ b/src/modules/speeddial/speeddial.c
+@@ -54,6 +54,8 @@ str domain_column = str_init("domain"
+ str sd_user_column = str_init("sd_username");
+ str sd_domain_column = str_init("sd_domain");
+ str new_uri_column = str_init("new_uri");
++str attribute_column = str_init("attribute");
++str group_column = str_init("group_id");
+ int use_domain = 0;
+ static str domain_prefix = {NULL, 0};
+
+@@ -70,6 +72,8 @@ static cmd_export_t cmds[] = {
+ REQUEST_ROUTE},
+ {"sd_lookup", (cmd_function)w_sd_lookup, 2, fixup_spve_spve, 0,
+ REQUEST_ROUTE},
++ {"sd_lookup_group", (cmd_function)w_sd_lookup_group, 2, fixup_spve_spve, 0,
++ REQUEST_ROUTE},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+@@ -180,6 +184,11 @@ static sr_kemi_t sr_kemi_speeddial_expor
+ { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
+ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
+ },
++ { str_init("speeddial"), str_init("lookup_group"),
++ SR_KEMIP_INT, sd_lookup_group,
++ { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
++ SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
++ },
+
+ { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
+ };
+--- a/src/modules/speeddial/speeddial.h
++++ b/src/modules/speeddial/speeddial.h
+@@ -20,25 +20,25 @@
+ *
+ */
+
+-
+ #ifndef _SPEEDDIAL_H_
+ #define _SPEEDDIAL_H_
+
+ #include "../../lib/srdb1/db.h"
+ #include "../../core/parser/msg_parser.h"
+
+-
+ /* Module parameters variables */
+
+-extern str user_column; /* 'username' column name */
+-extern str domain_column; /* 'domain' column name */
+-extern str sd_user_column; /* 'sd_username' column name */
+-extern str sd_domain_column; /* 'sd_domain' column name */
+-extern str new_uri_column; /* 'new_uri' column name */
+-extern int use_domain; /* use or not the domain for sd lookup */
+-extern str dstrip_s;
++extern str user_column; /* 'username' column name */
++extern str domain_column; /* 'domain' column name */
++extern str sd_user_column; /* 'sd_username' column name */
++extern str sd_domain_column; /* 'sd_domain' column name */
++extern str new_uri_column; /* 'new_uri' column name */
++extern str attribute_column; /* 'attribute' column name */
++extern str group_column; /* 'group' column name */
++extern int use_domain; /* use or not the domain for sd lookup */
++extern str dstrip_s;
+
+ extern db_func_t db_funcs; /* Database functions */
+-extern db1_con_t* db_handle; /* Database connection handle */
++extern db1_con_t* db_handle; /* Database connection handle */
+
+ #endif /* _SPEEDDIAL_H_ */
+--- a/src/modules/speeddial/doc/speeddial_admin.xml
++++ b/src/modules/speeddial/doc/speeddial_admin.xml
+@@ -252,6 +252,40 @@ if(uri=~"sip:[0-9]{2}@.*")
+
+
+
++
++
++ sd_lookup_group(table , group)
++
++
++ The function lookups the short dial number from the group defined in the 'table' and replaces the R-URI with associated address.
++
++ Meaning of the parameters is as follows:
++
++
++ table - The name of the table storing the
++ speed dial records.
++
++
++
++ group - The group that has the
++ short dialing codes.
++
++
++
++
++ This function can be used from REQUEST_ROUTE.
++
++
++ sd_lookup_group usage
++
++...
++# 'speed_dial' is the default table name created by kamailio db script
++if(uri=~"sip:[0-9]{2}@.*")
++ sd_lookup("speed_dial", "group_id");
++...
++
++
++
+
+
+ Installation and Running
+--- a/src/modules/speeddial/sdlookup.h
++++ b/src/modules/speeddial/sdlookup.h
+@@ -27,6 +27,8 @@
+ #include "../../core/parser/msg_parser.h"
+
+ int w_sd_lookup(struct sip_msg* _msg, char* _table, char* _owner);
++int w_sd_lookup_group(struct sip_msg* _msg, char* _table, char* _group);
+ int sd_lookup_owner(struct sip_msg* _msg, str* _stable, str* _sowner);
++int sd_lookup_group(struct sip_msg* _msg, str* _stable, str* _sgroup);
+
+ #endif /* _SDLOOKUP_H_ */