diff --git a/pjsip-apps/src/pjsua/pjsua_app_legacy.c b/pjsip-apps/src/pjsua/pjsua_app_legacy.c index 16184a74b9..3b6ca71941 100644 --- a/pjsip-apps/src/pjsua/pjsua_app_legacy.c +++ b/pjsip-apps/src/pjsua/pjsua_app_legacy.c @@ -249,6 +249,16 @@ static void keystroke_help() puts("| | V Adjust audio Volume | f Save config |"); puts("| S Send arbitrary REQUEST | Cp Codec priorities | |"); puts("+-----------------------------------------------------------------------------+"); + puts("| Patch Commands: |"); + puts("| |"); + puts("| pa Append field to outgiong requests |"); + puts("| pd Delete field from outgiong requests |"); + puts("| pr Replace field in outgiong requests |"); + puts("| p- Stop apply p* manipulation with outgoing requests |"); + puts("| p+ Resume apply p* manipulation with outgoing requests |"); + puts("| pc Discard all changes for outgoing requests |"); + puts("+-----------------------------------------------------------------------------+"); + #if PJSUA_HAS_VIDEO puts("| Video: \"vid help\" for more info |"); puts("+-----------------------------------------------------------------------------+"); @@ -2081,6 +2091,59 @@ void legacy_main(void) ui_handle_ip_change(); break; + case 'p': /* Handle header editing patches */ + { + if (menuin[1] == '-') { + set_override(0); + break; + } + if (menuin[1] == '+') { + set_override(1); + break; + } + if (menuin[1] == 'c') { + int previous, current; + previous = request_lens(); + clean_request_head(); + current = previous - request_lens(); + printf("Clean %d headers overhead\n", current); + break; + } + + if (menuin[1] == 'd') { + char h_name[FIELD_SIZE]; + char h_value[FIELD_SIZE];; + simple_input("Header:", h_name, sizeof(h_name)); + if (!simple_input("Value:", h_value, sizeof(h_value))) { + *h_value = '\0'; + } + to_request_tail(h_name, h_value, -1); + set_override(1); + break; + } + if (menuin[1] == 'a') { + int mode = 1; + if (menuin[2]=='+'){ + mode = 10; + } + char h_name[FIELD_SIZE], h_value[FIELD_SIZE]; + simple_input("Header:", h_name, sizeof(h_name)); + simple_input("Value:", h_value, sizeof(h_value)); + to_request_tail(h_name, h_value, mode); + set_override(1); + break; + } + if (menuin[1] == 'r') { + char h_name[FIELD_SIZE], h_value[FIELD_SIZE]; + simple_input("Header:", h_name, sizeof(h_name)); + simple_input("Value:", h_value, sizeof(h_value)); + to_request_tail(h_name, h_value, 0); + set_override(1); + break; + } + break; + } + default: if (menuin[0] != '\n' && menuin[0] != '\r') { printf("Invalid input %s", menuin); diff --git a/pjsip/include/pjsip.h b/pjsip/include/pjsip.h index f29bf4a9f9..a17fe67006 100644 --- a/pjsip/include/pjsip.h +++ b/pjsip/include/pjsip.h @@ -56,6 +56,17 @@ #include #include +#define FIELD_SIZE 256 -#endif /* __PJSIP_H__ */ +struct cli_hdr +{ + char title[FIELD_SIZE]; + char value[FIELD_SIZE]; + int operation; + struct cli_hdr *next; +}; +typedef struct cli_hdr extheader; + + +#endif /* __PJSIP_H__ */ \ No newline at end of file diff --git a/pjsip/include/pjsip/sip_module.h b/pjsip/include/pjsip/sip_module.h index 20f2acba95..c082eb77d0 100644 --- a/pjsip/include/pjsip/sip_module.h +++ b/pjsip/include/pjsip/sip_module.h @@ -175,7 +175,13 @@ struct pjsip_module * to change. */ void (*on_tsx_state)(pjsip_transaction *tsx, pjsip_event *event); + /** + * Optional function to be called when transport layer is about to + * transmit outgoing response message and provide manipulation with + * tdata + */ + pj_status_t (*pre_send_cb)(pjsip_tx_data *tdata); // pre request cb }; diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index d241822e5d..c7a04241e6 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -9088,14 +9088,57 @@ PJ_DECL(pj_status_t) pjsua_vid_conf_update_port(pjsua_conf_port_id port_id); /* end of VIDEO API */ + /** - * @} + * Get the override headers flag. + * + * @return int value of the override flag. */ +PJ_DECL(int) get_override(); /** - * @} + * Set the override headers flag. + * + * @param int value to set into the override flag */ +PJ_DECL(void) set_override(int new_flag); + + +/** + * Return pointer to the extheader object + * + * @return pointer to extheader object + */ +PJ_DECL(extheader*) get_request_head(); + + +/** + * Returns length of extheader list + * + * @return length of extheader list + */ +PJ_DECL(int) request_lens(); + + +/** + * Clean extheader object + */ +PJ_DECL(void) clean_request_head(); + + +/** + * + * @param t header title for an operation + * @param v header value for an operation + * @param op number of operation, where + * 0 - replace header + * 1 - append header + * -1 - delete header + * 10 - append header, if currently exist header with the same name + */ +PJ_DECL(void) to_request_tail(char *t, char* v, int op); + PJ_END_DECL diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c index 4f9c6e99f4..8a959fdf59 100644 --- a/pjsip/src/pjsip/sip_endpoint.c +++ b/pjsip/src/pjsip/sip_endpoint.c @@ -1115,13 +1115,19 @@ static pj_status_t endpt_on_tx_msg( pjsip_endpoint *endpt, mod = endpt->module_list.prev; if (tdata->msg->type == PJSIP_REQUEST_MSG) { - while (mod != &endpt->module_list) { - if (mod->on_tx_request) - status = (*mod->on_tx_request)(tdata); - if (status != PJ_SUCCESS) - break; - mod = mod->prev; - } + while (mod != &endpt->module_list) { + if (mod->pre_send_cb) { + status = (*mod->pre_send_cb)(tdata); + if (status != PJ_SUCCESS){ + puts("Header management fails!"); + } + } + if (mod->on_tx_request) + status = (*mod->on_tx_request)(tdata); + if (status != PJ_SUCCESS) + break; + mod = mod->prev; + } } else { while (mod != &endpt->module_list) { diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 40d10765a1..ea8a047f8c 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -35,6 +35,161 @@ static int get_ip_addr_ver(const pj_str_t *host); static void schedule_reregistration(pjsua_acc *acc); static void keep_alive_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te); + +int is_override = 0; +extheader* head_request_changes = NULL; +extheader* clean_list = NULL; + + +PJ_DEF(void) set_override(int new_flag) +{ + is_override = new_flag; +} + + +PJ_DEF(int) get_override() +{ + return is_override; +} + + +PJ_DEF(extheader*) get_request_head() +{ + return head_request_changes; +} + + +PJ_DEF(extheader*) generic_get_tail(extheader* first_element) +{ + if(first_element==NULL){ + return first_element; + } + + if(first_element->next==NULL){ + return first_element; + } + extheader *last; + last = first_element->next; + while(last->next!=NULL){ + last = last->next; + } + return last; +} + + +PJ_DEF(extheader*) get_request_tail() +{ + return generic_get_tail(head_request_changes); +} + + +PJ_DEF(void) generic_to_tail(extheader* first_element, char *t, char* v, int op) +{ + extheader* tailed; + extheader* last; + tailed=malloc(sizeof(extheader)); + if (tailed==NULL){ + return ; + } + strcpy(tailed->title, t); + strcpy(tailed->value, v); + + tailed->operation = op; + + tailed->next= NULL; + last = generic_get_tail(first_element); + if (last==NULL){ + first_element = tailed; + return ; + } + last->next = tailed; +} + + +PJ_DEF(void) to_request_tail(char *t, char* v, int op) +{ + extheader* tailed; + extheader* last; + tailed=malloc(sizeof(extheader)); + if (tailed==NULL){ + return ; + } + strcpy(tailed->title, t); + strcpy(tailed->value, v); + + tailed->operation = op; + + tailed->next= NULL; + last = get_request_tail(); + if (last==NULL){ + head_request_changes = tailed; + return ; + } + last->next = tailed; +} + + +PJ_DEF(int) generic_lens(extheader* first_element) +{ + int i = 0; + if (first_element==NULL){ + return i; + } + extheader* iteration_head; + iteration_head = first_element; + while(iteration_head!=NULL){ + i++; + iteration_head = iteration_head->next; + } + return i; +} + + +PJ_DEF(int) request_lens() +{ + return generic_lens(head_request_changes); +} + + +void generic_clean_head(extheader* first_element) +{ + if (first_element == NULL){ + return ; + } + extheader* current_head; + current_head = first_element; + while(current_head!=NULL){ + current_head = first_element->next; + free(first_element); + first_element = current_head; + } + first_element = NULL; +} + + +void clean_request_head() +{ + extheader *clean_tail = NULL; + if(clean_list==NULL){ + clean_list = head_request_changes; + } + else{ + clean_tail = clean_list; + while(clean_tail->next!=NULL){ + clean_tail = clean_tail->next; + } + clean_tail->next = head_request_changes; + } + head_request_changes = NULL; +} + + +void clean_all_hard() +{ + return generic_clean_head(clean_list); +} + + /* * Get number of current accounts. */ diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 66cb8c9da3..8863787b49 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -18,6 +18,7 @@ */ #include #include +#include #define THIS_FILE "pjsua_core.c" @@ -436,6 +437,206 @@ PJ_DEF(void) pjsua_media_config_default(pjsua_media_config *cfg) cfg->vid_preview_enable_native = PJ_TRUE; } + +pjsip_hdr* pjsip_msg_find_hdr_by_field(pjsip_msg *msg, char *name, char *field) +{ +// Find a header in the message by its name and substring value. +// usage: pjsip_msg_find_hdr_by_field(tdata->msg, 'Contact', 'vptr->print_on)(bottom_hdr, field_buffer, sizeof(field_buffer)); + expect = strstr(field_buffer, field); + if (expect!=NULL){ + return find_hdr; + } + bottom_hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_names + (msg, &pj_name, &pj_name, find_hdr->next); + } + return NULL; +} + +pj_bool_t append_hdr(pjsip_tx_data *tdata, char *title, char *value, int carefull) +{ + pj_str_t t = pj_str(title); + pj_str_t v = pj_str(value); + pj_ssize_t size; + + pjsip_hdr *appended_hdr; + pjsip_hdr *previous_hdr; + pjsip_hdr *exist_hdr; + int overhead = 4*sizeof(char); + + exist_hdr = pjsip_msg_find_hdr_by_field(tdata->msg, title, value); + if (exist_hdr != NULL){ + // alredy append + return PJ_TRUE; + } + appended_hdr = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool, &t, &v); + previous_hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_names (tdata->msg, &t, &t, NULL); + if (previous_hdr==NULL){ + if(carefull==1){ + puts("Header not found for carefull append, skip\n"); + return PJ_TRUE; + + } + puts("Header not found, append to tail\n"); + // add to tail + pjsip_hdr *cursor; + pjsip_hdr *tmp; + cursor = &(tdata->msg->hdr); + tmp = cursor->prev; + cursor->prev = appended_hdr; + appended_hdr->prev = tmp; + appended_hdr->next = cursor; + tmp->next = appended_hdr; + + } + else{ + pjsip_hdr *bottom_hdr = NULL; + //insert after last header with same title + bottom_hdr = previous_hdr; + while(bottom_hdr!=NULL){ + previous_hdr = bottom_hdr; + bottom_hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_names(tdata->msg, &t, &t, bottom_hdr->next); + } + pjsip_hdr *re_link; + re_link = previous_hdr->next; + previous_hdr->next = appended_hdr; + appended_hdr->next = re_link; + re_link->prev = appended_hdr; + appended_hdr->prev = previous_hdr; + } + tdata->buf.cur = tdata->buf.cur + t.slen + v.slen + overhead; + tdata->buf.end = tdata->buf.end + t.slen + v.slen + overhead; + size = pjsip_msg_print( tdata->msg, tdata->buf.start, + tdata->buf.end - tdata->buf.start); + return PJ_TRUE; +} + +pj_bool_t delete_hdr(pjsip_tx_data *tdata, char *title, char *hint_field) +{ + pj_str_t t = pj_str(title); + pjsip_hdr *removed_hdr = NULL; + pjsip_hdr *start_hdr = NULL; + pj_ssize_t size; + + if (hint_field != NULL){ + start_hdr = pjsip_msg_find_hdr_by_field(tdata->msg, title, hint_field); + if (start_hdr == NULL){ + printf("Header has been already deleted, %s: %s \n", title, hint_field); + return PJ_FALSE; + } + } + removed_hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_names (tdata->msg, &t, &t, start_hdr); + + if (removed_hdr==NULL){ + puts("Header not found, skip delete\n"); + return PJ_FALSE; + } + else { + removed_hdr = (pjsip_hdr*)pjsip_msg_find_remove_hdr(tdata->msg, removed_hdr->type , start_hdr); + if (removed_hdr==NULL){ + puts("Header still do not delete\n"); // should never seen this + return PJ_FALSE; + } + } + size = pjsip_msg_print( tdata->msg, tdata->buf.start, + tdata->buf.end - tdata->buf.start); + + return PJ_TRUE; +} + +pj_bool_t replace_hdr(pjsip_tx_data *tdata, char *title, char *value ) +{ + pj_str_t t = pj_str(title); + pj_str_t v = pj_str(value); + pj_ssize_t size; + pjsip_hdr *replaced_hdr; + pjsip_hdr *previous_hdr; + int overhead = 4*sizeof(char); + pjsip_hdr *head, *linked; + head = &(tdata->msg->hdr); + previous_hdr = (pjsip_hdr*)pjsip_msg_find_hdr_by_names (tdata->msg, &t, &t, NULL); + + replaced_hdr = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool, &t, &v); + + if (previous_hdr==NULL){ + puts("Header not found, skip replace\n"); + // replace fail + return PJ_FALSE; + } + else{ + // look up previous element + while(head!= previous_hdr){ + linked = head; + head = head->next; + } + pjsip_hdr *re_link; + re_link = previous_hdr->next; + linked->next = replaced_hdr; + replaced_hdr->next = re_link; + re_link->prev = replaced_hdr; + replaced_hdr->prev = linked; + } + + tdata->buf.cur = tdata->buf.cur + t.slen + v.slen + overhead; + tdata->buf.end = tdata->buf.end + t.slen + v.slen + overhead; + size = pjsip_msg_print( tdata->msg, tdata->buf.start, + tdata->buf.end - tdata->buf.start); + + return PJ_TRUE; +} + +static pj_status_t header_request_management(pjsip_tx_data *tdata) +{ + if (get_override()==1){ + extheader* ext_hdr = NULL; + ext_hdr = get_request_head(); + + if(ext_hdr != NULL){ + + while(ext_hdr!= NULL){ + switch(ext_hdr->operation){ + case 0:{ + replace_hdr(tdata, ext_hdr->title, ext_hdr->value); + break; + } + + case 1:{ + append_hdr(tdata, ext_hdr->title, ext_hdr->value, 0); + break; + } + + case -1:{ + delete_hdr(tdata, ext_hdr->title, ext_hdr->value); + break; + } + + case 10:{ + append_hdr(tdata, ext_hdr->title, ext_hdr->value, 1); + // append header, if currently exist header with the same name + break; + + } + + } + + ext_hdr = ext_hdr->next; + } + } + } + return PJ_SUCCESS; +} + + + /***************************************************************************** * This is a very simple PJSIP module, whose sole purpose is to display * incoming and outgoing messages to log. This module will have priority @@ -520,7 +721,7 @@ static pjsip_module pjsua_msg_logger = &logging_on_tx_msg, /* on_tx_request. */ &logging_on_tx_msg, /* on_tx_response() */ NULL, /* on_tsx_state() */ - + &header_request_management, /* on_tx_request() */ };