Exemple #1
0
rtp_stream_t *
call_group_get_next_stream(sip_call_group_t *group, rtp_stream_t *stream)
{
    rtp_stream_t *next = NULL;
    rtp_stream_t *cand;
    sip_call_t *call;
    vector_iter_t streams;
    int i;

    for (i = 0; i < vector_count(group->calls); i++) {
        call = vector_item(group->calls, i);
        streams = vector_iterator(call->streams);
        while ( (cand = vector_iterator_next(&streams))) {
            if (!stream_get_count(cand))
                continue;

            // candidate must be between msg and next
            if (stream_is_older(cand, stream) && (!next || stream_is_older(next, cand))) {
                next = cand;
            }
        }
    }

    return next;
}
Exemple #2
0
void
capture_close()
{
    capture_info_t *capinfo;

    // Nothing to close
    if (vector_count(capture_cfg.sources) == 0)
        return;

    // Stop all captures
    vector_iter_t it = vector_iterator(capture_cfg.sources);
    while ((capinfo = vector_iterator_next(&it))) {
        //Close PCAP file
        if (capinfo->handle) {
            pcap_breakloop(capinfo->handle);
            pthread_join(capinfo->capture_t, NULL);
            pcap_close(capinfo->handle);
        }
    }

    // Close dump file
    if (capture_cfg.pd) {
        dump_close(capture_cfg.pd);
    }
}
Exemple #3
0
sip_msg_t *
call_group_get_next_msg(sip_call_group_t *group, sip_msg_t *msg)
{
    sip_msg_t *next = NULL;
    sip_msg_t *cand;
    vector_iter_t msgs;
    sip_call_t *call;
    int i;

    for (i = 0; i < vector_count(group->calls); i++) {

        call = vector_item(group->calls, i);
        msgs = vector_iterator(call->msgs);

        if (msg && call == msg_get_call(msg))
            vector_iterator_set_current(&msgs, msg->index);

        if (group->sdp_only)
            vector_iterator_set_filter(&msgs, msg_has_sdp);

        cand = NULL;
        while ((cand = vector_iterator_next(&msgs))) {
            // candidate must be between msg and next
            if (msg_is_older(cand, msg) && (!next || !msg_is_older(cand, next))) {
                next = cand;
                break;
            }
        }
    }

    return sip_parse_msg(next);
}
Exemple #4
0
const char *
media_get_format(sdp_media_t *media, uint32_t code)
{
    sdp_media_fmt_t *format;
    vector_iter_t iter;
    iter = vector_iterator(media->formats);
    while ((format = vector_iterator_next(&iter))) {
        if (format->id == code)
            return format->format;
    }

    return "Unassigned";
}
Exemple #5
0
void
dump_packet(pcap_dumper_t *pd, const packet_t *packet)
{
    if (!pd || !packet)
        return;

    vector_iter_t it = vector_iterator(packet->frames);
    frame_t *frame;
    while ((frame = vector_iterator_next(&it))) {
        pcap_dump((u_char*) pd, frame->header, frame->data);
    }
    pcap_dump_flush(pd);
}
Exemple #6
0
void
sip_calls_rotate()
{
    sip_call_t *call;
    vector_iter_t it = vector_iterator(calls.list);
    while ((call = vector_iterator_next(&it))) {
        if (!call->locked) {
            // Remove from callids hash
            htable_remove(calls.callids, call->callid);
            // Remove first call from active and call lists
            vector_remove(calls.active, call);
            vector_remove(calls.list, call);
            return;
        }
    }
}
Exemple #7
0
int
capture_launch_thread(capture_info_t *capinfo)
{
    //! capture thread attributes
    pthread_attr_t attr;
    pthread_attr_init(&attr);

    // Start all captures threads
    vector_iter_t it = vector_iterator(capture_cfg.sources);
    while ((capinfo = vector_iterator_next(&it))) {
        if (pthread_create(&capinfo->capture_t, &attr, (void *) capture_thread, capinfo)) {
            return 1;
        }
    }

    pthread_attr_destroy(&attr);
    return 0;
}
Exemple #8
0
int
capture_set_bpf_filter(const char *filter)
{
    vector_iter_t it = vector_iterator(capture_cfg.sources);
    capture_info_t *capinfo;

    // Apply the given filter to all sources
    while ((capinfo = vector_iterator_next(&it))) {
        //! Check if filter compiles
        if (pcap_compile(capinfo->handle, &capture_cfg.fp, filter, 0, capinfo->mask) == -1)
            return 1;

        // Set capture filter
        if (pcap_setfilter(capinfo->handle, &capture_cfg.fp) == -1)
            return 1;

    }

    return 0;
}
Exemple #9
0
int
vector_iterator_count(vector_iter_t *it)
{
    int count = 0;
    int pos = it->current;

    vector_iterator_reset(it);

    if (!it->filter) {
        count = vector_count(it->vector);
    } else {
        while (vector_iterator_next(it)) {
            count++;
        }
    }

    vector_iterator_set_current(it, pos);

    return count;
}
Exemple #10
0
void
sip_calls_clear_soft()
{
        // Create again the callid hash table
        htable_destroy(calls.callids);
        calls.callids = htable_create(calls.limit);

        // Repopulate list applying current filter
        calls.list = vector_copy_if(sip_calls_vector(), filter_check_call);
        calls.active = vector_copy_if(sip_active_calls_vector(), filter_check_call);

        // Repopulate callids based on filtered list
        sip_call_t *call;
        vector_iter_t it = vector_iterator(calls.list);

        while ((call = vector_iterator_next(&it)))
        {
                htable_insert(calls.callids, call->callid, call);
        }
}
Exemple #11
0
vector_t *
vector_clone(vector_t *original)
{
    vector_t *clone;
    vector_iter_t it;
    void *item;

    // Check we have a valid vector pointer
    if (!original)
        return NULL;

    // Create a new vector structure
    clone = vector_create(original->limit, original->step);
    vector_set_destroyer(clone, original->destroyer);
    vector_set_sorter(clone, original->sorter);

    // Fill the clone vector with the same elements
    it = vector_iterator(original);
    while ((item = vector_iterator_next(&it)))
        vector_append(clone, item);

    // Return the cloned vector
    return clone;
}
Exemple #12
0
packet_t *
capture_packet_reasm_tcp(packet_t *packet, struct tcphdr *tcp, u_char *payload, int size_payload) {

    vector_iter_t it = vector_iterator(capture_cfg.tcp_reasm);
    packet_t *pkt;
    u_char *new_payload;

    //! Assembled
    if ((int32_t) size_payload <= 0)
        return packet;

    while ((pkt = vector_iterator_next(&it))) {
        if (addressport_equals(pkt->src, packet->src) &&
                addressport_equals(pkt->dst, packet->dst)) {
            break;
        }
    }

    // If we already have this packet stored
    if (pkt) {
        frame_t *frame;
        // Append this frames to the original packet
        vector_iter_t frames = vector_iterator(packet->frames);
        while ((frame = vector_iterator_next(&frames)))
            packet_add_frame(pkt, frame->header, frame->data);
        // Destroy current packet as its frames belong to the stored packet
        packet_destroy(packet);
    } else {
        // First time this packet has been seen
        pkt = packet;
        // Add To the possible reassembly list
        vector_append(capture_cfg.tcp_reasm, packet);
    }

    // Store firt tcp sequence
    if (pkt->tcp_seq == 0) {
        pkt->tcp_seq = ntohl(tcp->th_seq);
    }

    // If the first frame of this packet
    if (vector_count(pkt->frames) == 1) {
        // Set initial payload
        packet_set_payload(pkt, payload, size_payload);
    } else {
        // Check payload length. Dont handle too big payload packets
        if (pkt->payload_len + size_payload > MAX_CAPTURE_LEN) {
            packet_destroy(pkt);
            vector_remove(capture_cfg.tcp_reasm, pkt);
            return NULL;
        }
        new_payload = sng_malloc(pkt->payload_len + size_payload);
        if (pkt->tcp_seq < ntohl(tcp->th_seq)) {
            // Append payload to the existing
            pkt->tcp_seq =  ntohl(tcp->th_seq);
            memcpy(new_payload, pkt->payload, pkt->payload_len);
            memcpy(new_payload + pkt->payload_len, payload, size_payload);
        } else {
            // Prepend payload to the existing
            memcpy(new_payload, payload, size_payload);
            memcpy(new_payload + size_payload, pkt->payload, pkt->payload_len);
        }
        packet_set_payload(pkt, new_payload, pkt->payload_len + size_payload);
        sng_free(new_payload);

    }

    // This packet is ready to be parsed
    int valid = sip_validate_packet(pkt);
    if (valid == VALIDATE_COMPLETE_SIP) {
        // Full SIP packet!
        vector_remove(capture_cfg.tcp_reasm, pkt);
        return pkt;
    } else if (valid == VALIDATE_NOT_SIP) {
        vector_remove(capture_cfg.tcp_reasm, pkt);
        return pkt;
    }

    // An incomplete SIP Packet
    return NULL;
}
Exemple #13
0
packet_t *
capture_packet_reasm_ip(capture_info_t *capinfo, const struct pcap_pkthdr *header, u_char *packet, uint32_t *size, uint32_t *caplen)
{
    // IP header data
    struct ip *ip4;
#ifdef USE_IPV6
    // IPv6 header data
    struct ip6_hdr *ip6;
#endif
    // IP version
    uint32_t ip_ver;
    // IP protocol
    uint8_t ip_proto;
    // IP header size
    uint32_t ip_hl = 0;
    // Fragment offset
    uint16_t ip_off = 0;
    // IP content len
    uint16_t ip_len = 0;
    // Fragmentation flag
    uint16_t ip_frag = 0;
    // Fragmentation identifier
    uint32_t ip_id = 0;
    // Fragmentation offset
    uint16_t ip_frag_off = 0;
    //! Source Address
    address_t src = { };
    //! Destination Address
    address_t dst = { };
    //! Common interator for vectors
    vector_iter_t it;
    //! Packet containers
    packet_t *pkt;
    //! Storage for IP frame
    frame_t *frame;
    uint32_t len_data = 0;

    // Get IP header
    ip4 = (struct ip *) (packet + capinfo->link_hl);

#ifdef USE_IPV6
    // Get IPv6 header
    ip6 = (struct ip6_hdr *) (packet + capinfo->link_hl);
#endif

    // Get IP version
    ip_ver = ip4->ip_v;

    switch (ip_ver) {
        case 4:
            ip_hl = ip4->ip_hl * 4;
            ip_proto = ip4->ip_p;
            ip_off = ntohs(ip4->ip_off);
            ip_len = ntohs(ip4->ip_len);

            ip_frag = ip_off & (IP_MF | IP_OFFMASK);
            ip_frag_off = (ip_frag) ? (ip_off & IP_OFFMASK) * 8 : 0;
            ip_id = ntohs(ip4->ip_id);

            inet_ntop(AF_INET, &ip4->ip_src, src.ip, sizeof(src.ip));
            inet_ntop(AF_INET, &ip4->ip_dst, dst.ip, sizeof(dst.ip));
            break;
#ifdef USE_IPV6
        case 6:
            ip_hl = sizeof(struct ip6_hdr);
            ip_proto = ip6->ip6_nxt;
            ip_len = ntohs(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen) + ip_hl;

            if (ip_proto == IPPROTO_FRAGMENT) {
                struct ip6_frag *ip6f = (struct ip6_frag *) (ip6 + ip_hl);
                ip_frag_off = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK);
                ip_id = ntohl(ip6f->ip6f_ident);
            }

            inet_ntop(AF_INET6, &ip6->ip6_src, src.ip, sizeof(src.ip));
            inet_ntop(AF_INET6, &ip6->ip6_dst, dst.ip, sizeof(dst.ip));
            break;
#endif
        default:
            return NULL;
    }

    // Fixup VSS trailer in ethernet packets
    *caplen = capinfo->link_hl + ip_len;

    // Remove IP Header length from payload
    *size = *caplen - capinfo->link_hl - ip_hl;

    // If no fragmentation
    if (ip_frag == 0) {
        // Just create a new packet with given network data
        pkt = packet_create(ip_ver, ip_proto, src, dst, ip_id);
        packet_add_frame(pkt, header, packet);
        return pkt;
    }

    // Look for another packet with same id in IP reassembly vector
    it = vector_iterator(capture_cfg.ip_reasm);
    while ((pkt = vector_iterator_next(&it))) {
        if (addressport_equals(pkt->src, src)
                && addressport_equals(pkt->dst, dst)
                && pkt->ip_id == ip_id) {
            break;
        }
    }

    // If we already have this packet stored, append this frames to existing one
    if (pkt) {
        packet_add_frame(pkt, header, packet);
    } else {
        // Add To the possible reassembly list
        pkt = packet_create(ip_ver, ip_proto, src, dst, ip_id);
        packet_add_frame(pkt, header, packet);
        vector_append(capture_cfg.ip_reasm, pkt);
        return NULL;
    }

    // If no more fragments
    if ((ip_off & IP_MF) == 0) {
        // TODO Dont check the flag, check the holes
        // Calculate assembled IP payload data
        it = vector_iterator(pkt->frames);
        while ((frame = vector_iterator_next(&it))) {
            struct ip *frame_ip = (struct ip *) (frame->data + capinfo->link_hl);
            len_data += frame->header->caplen - capinfo->link_hl - frame_ip->ip_hl * 4;
        }

        // Check packet content length
        if (len_data > MAX_CAPTURE_LEN)
            return NULL;

        // Initialize memory for the assembly packet
        memset(packet, 0, capinfo->link_hl + ip_hl + len_data);

        it = vector_iterator(pkt->frames);
        while ((frame = vector_iterator_next(&it))) {
            // Get IP header
            struct ip *frame_ip = (struct ip *) (frame->data + capinfo->link_hl);
            memcpy(packet + capinfo->link_hl + ip_hl + (ntohs(frame_ip->ip_off) & IP_OFFMASK) * 8,
                   frame->data + capinfo->link_hl + frame_ip->ip_hl * 4,
                   frame->header->caplen - capinfo->link_hl - frame_ip->ip_hl * 4);
        }

        *caplen = capinfo->link_hl + ip_hl + len_data;
        *size = len_data;

        // Return the assembled IP packet
        vector_remove(capture_cfg.ip_reasm, pkt);
        return pkt;
    }

    return NULL;
}
Exemple #14
0
int
call_list_handle_key(PANEL *panel, int key)
{
    int i, height, width, rnpag_steps = setting_get_intvalue(SETTING_CL_SCROLLSTEP);
    call_list_info_t *info;
    ui_t *next_panel;
    sip_call_group_t *group;
    int action = -1;
    sip_call_t *call;

    // Sanity check, this should not happen
    if (!(info  = call_list_info(panel)))
        return -1;

    // Handle form key
    if (info->form_active)
        return call_list_handle_form_key(panel, key);

    // Get window of call list panel
    WINDOW *win = info->list_win;
    getmaxyx(win, height, width);

    // Reset iterator position to current call
    vector_iterator_set_current(&info->calls, info->cur_call);

    // Check actions for this key
    while ((action = key_find_action(key, action)) != ERR) {
        // Check if we handle this action
        switch (action) {
            case ACTION_DOWN:
                // Check if there is a call below us
                if (!vector_iterator_next(&info->calls))
                    break;
                info->cur_call = vector_iterator_current(&info->calls);
                info->cur_line++;
                // If we are out of the bottom of the displayed list
                // refresh it starting in the next call
                if (info->cur_line > height) {
                    vector_iterator_set_current(&info->calls, info->first_call);
                    vector_iterator_next(&info->calls);
                    info->first_call = vector_iterator_current(&info->calls);
                    info->first_line++;
                    info->cur_line = height;
                }
                // Disable Autoscroll
                info->autoscroll = 0;
                break;
            case ACTION_UP:
                // Check if there is a call above us
                if (!vector_iterator_prev(&info->calls))
                    break;
                info->cur_call = vector_iterator_current(&info->calls);
                info->cur_line--;
                // If we are out of the top of the displayed list
                // refresh it starting in the previous (in fact current) call
                if (info->cur_line <= 0) {
                    info->first_call = info->cur_call;
                    info->first_line--;
                    info->cur_line = 1;
                }
                // Disable Autoscroll
                info->autoscroll = 0;
                break;
            case ACTION_HNPAGE:
                rnpag_steps = rnpag_steps / 2;
                /* no break */
            case ACTION_NPAGE:
                // Next page => N key down strokes
                for (i = 0; i < rnpag_steps; i++)
                    call_list_handle_key(panel, KEY_DOWN);
                // Disable Autoscroll
                info->autoscroll = 0;
                break;
            case ACTION_HPPAGE:
                rnpag_steps = rnpag_steps / 2;
                /* no break */
            case ACTION_PPAGE:
                // Prev page => N key up strokes
                for (i = 0; i < rnpag_steps; i++)
                    call_list_handle_key(panel, KEY_UP);
                // Disable Autoscroll
                info->autoscroll = 0;
                break;
            case ACTION_BEGIN:
                // Initialize structures
                info->first_call = info->cur_call = -1;
                info->first_line = info->cur_line = 0;
                // Disable Autoscroll
                info->autoscroll = 0;
                break;
            case ACTION_END:
                // Check if there is a call below us
                while (vector_iterator_next(&info->calls)) {
                    info->cur_call = vector_iterator_current(&info->calls);
                    info->cur_line++;
                    // If we are out of the bottom of the displayed list
                    // refresh it starting in the next call
                    if (info->cur_line > height) {
                        vector_iterator_set_current(&info->calls, info->first_call);
                        vector_iterator_next(&info->calls);
                        info->first_call = vector_iterator_current(&info->calls);
                        info->first_line++;
                        info->cur_line = height;
                        vector_iterator_set_current(&info->calls, info->cur_call);
                    }
                }
                break;
            case ACTION_DISP_FILTER:
                // Activate Form
                call_list_form_activate(panel, 1);
                // Disable Autoscroll
                info->autoscroll = 0;
                break;
            case ACTION_SHOW_FLOW:
            case ACTION_SHOW_FLOW_EX:
            case ACTION_SHOW_RAW:
                // Check we have calls in the list
                if (info->cur_call == -1)
                    break;
                // Create a new group of calls
                group = call_group_clone(info->group);
                // If not selected call, show current call flow
                if (call_group_count(info->group) == 0)
                    call_group_add(group, sip_find_by_index(info->cur_call));

                // Add xcall to the group
                if (action == ACTION_SHOW_FLOW_EX)
                    call_group_add(group, call_get_xcall(sip_find_by_index(info->cur_call)));

                if (action == ACTION_SHOW_RAW) {
                    // Create a Call Flow panel
                    ui_create_panel(PANEL_CALL_RAW);
                    call_raw_set_group(group);
                } else {
                    // Display current call flow (normal or extended)
                    ui_create_panel(PANEL_CALL_FLOW);
                    call_flow_set_group(group);
                }
                break;
            case ACTION_SHOW_FILTERS:
                ui_create_panel(PANEL_FILTER);
                break;
            case ACTION_SHOW_COLUMNS:
                ui_create_panel(PANEL_COLUMN_SELECT);
                break;
            case ACTION_SHOW_STATS:
                ui_create_panel(PANEL_STATS);
                break;
            case ACTION_SAVE:
                next_panel = ui_create_panel(PANEL_SAVE);
                save_set_group(ui_get_panel(next_panel), info->group);
                break;
            case ACTION_CLEAR:
                // Clear group calls
                vector_clear(info->group->calls);
                break;
            case ACTION_CLEAR_CALLS:
                // Remove all stored calls
                sip_calls_clear();
                // Clear List
                call_list_clear(panel);
                break;
            case ACTION_AUTOSCROLL:
                info->autoscroll = (info->autoscroll) ? 0 : 1;
                break;
            case ACTION_SHOW_SETTINGS:
                ui_create_panel(PANEL_SETTINGS);
                break;
            case ACTION_SELECT:
                call = vector_item(vector_iterator_vector(&info->calls), info->cur_call);
                if (call_group_exists(info->group, call)) {
                    call_group_del(info->group, call);
                } else {
                    call_group_add(info->group, call);
                }
                break;
            case ACTION_PREV_SCREEN:
                // Handle quit from this screen unless requested
                if (setting_enabled(SETTING_EXITPROMPT)) {
                    if (dialog_confirm("Confirm exit", "Are you sure you want to quit?", "Yes,No") == 0) {
                        return KEY_ESC;
                    } else {
                        return 0;
                    }
                } else {
                    return KEY_ESC;
                }
                break;
            default:
                // Parse next action
                continue;
        }

        // This panel has handled the key successfully
        break;
    }

    // Return if this panel has handled or not the key
    return (action == ERR) ? key : 0;
}
Exemple #15
0
void
call_list_draw_list(PANEL *panel)
{
    WINDOW *win;
    int height, width, cline = 0;
    struct sip_call *call;
    int i, collen;
    char coltext[256];
    int colid;
    int colpos;
    int color;

    // Get panel info
    call_list_info_t *info = call_list_info(panel);

    // Get window of call list panel
    win = info->list_win;
    getmaxyx(win, height, width);

    // If autoscroll is enabled, select the last dialog
    if (info->autoscroll) {
        call_list_handle_key(panel, key_action_key(ACTION_END));
    }

    // If no active call, use the fist one (if exists)
    if (info->first_call == -1 && vector_iterator_count(&info->calls)) {
        vector_iterator_reset(&info->calls);
        call = vector_iterator_next(&info->calls);
        info->cur_call = info->first_call = vector_index(vector_iterator_vector(&info->calls), call);
        info->cur_line = info->first_line = 1;
    }

    // Clear call list before redrawing
    werase(win);

    // Set the iterator position to the first call
    vector_iterator_set_current(&info->calls, info->first_call - 1 );

    // Fill the call list
    while ((call = vector_iterator_next(&info->calls))) {
        // Stop if we have reached the bottom of the list
        if (cline == height)
            break;

        // We only print calls with messages (In fact, all call should have msgs)
        if (!call_msg_count(call))
            continue;

        // Show bold selected rows
        if (call_group_exists(info->group, call))
            wattron(win, A_BOLD | COLOR_PAIR(CP_DEFAULT));

        // Highlight active call
        if (call->index == info->cur_call + 1) {
            wattron(win, COLOR_PAIR(CP_WHITE_ON_BLUE));
            // Reverse colors on monochrome terminals
            if (!has_colors())
                wattron(win, A_REVERSE);
        }
        // Set current line background
        clear_line(win, cline);
        // Set current line selection box
        mvwprintw(win, cline, 2, call_group_exists(info->group, call) ? "[*]" : "[ ]");

        // Print requested columns
        colpos = 6;
        for (i = 0; i < info->columncnt; i++) {
            // Get current column id
            colid = info->columns[i].id;
            // Get current column width
            collen = info->columns[i].width;
            // Check if next column fits on window width
            if (colpos + collen >= width)
                break;

            // Initialize column text
            memset(coltext, 0, sizeof(coltext));

            // Get call attribute for current column
            if (!call_get_attribute(call, colid, coltext)) {
                colpos += collen + 1;
                continue;
            }

            // Enable attribute color (if not current one)
            color = 0;
            if (call->index != info->cur_call + 1 && (color = sip_attr_get_color(colid, coltext)) > 0)
                wattron(win, color);

            // Add the column text to the existing columns
            mvwprintw(win, cline, colpos, "%.*s", collen, coltext);
            colpos += collen + 1;

            // Disable attribute color
            if (color > 0)
                wattroff(win, color);
        }
        cline++;

        wattroff(win, COLOR_PAIR(CP_DEFAULT));
        wattroff(win, COLOR_PAIR(CP_DEF_ON_BLUE));
        wattroff(win, A_BOLD | A_REVERSE);
    }

    // Draw scrollbar to the right
    draw_vscrollbar(win, info->first_line, info->dispcallcnt, 1);
    wnoutrefresh(info->list_win);

}
Exemple #16
0
int
save_to_file(ui_t *ui)
{
    char savepath[256];
    char savefile[256];
    char fullfile[512];
    sip_call_t *call = NULL;
    sip_msg_t *msg = NULL;
    pcap_dumper_t *pd = NULL;
    FILE *f = NULL;
    int cur = 0, total = 0;
    WINDOW *progress;
    vector_iter_t calls, msgs, rtps, packets;
    packet_t *packet;
    vector_t *sorted;

    // Get panel information
    save_info_t *info = save_info(ui);

    // Get current path field value.
    memset(savepath, 0, sizeof(savepath));
    strcpy(savepath, field_buffer(info->fields[FLD_SAVE_PATH], 0));
    strtrim(savepath);
    if (strlen(savepath))
        strcat(savepath, "/");

    // Get current file field value.
    memset(savefile, 0, sizeof(savefile));
    strcpy(savefile, field_buffer(info->fields[FLD_SAVE_FILE], 0));
    strtrim(savefile);

    if (!strlen(savefile)) {
        dialog_run("Please enter a valid filename");
        return 1;
    }

    if (info->saveformat == SAVE_PCAP || info->saveformat == SAVE_PCAP_RTP) {
        if (!strstr(savefile, ".pcap"))
            strcat(savefile, ".pcap");
    } else {
        if (!strstr(savefile, ".txt"))
            strcat(savefile, ".txt");
    }

    // Absolute filename
    sprintf(fullfile, "%s%s", savepath, savefile);

    if (access(fullfile, R_OK) == 0) {
        if (dialog_confirm("Overwrite confirmation", "Selected file already exits.\n Do you want to overwrite it?", "Yes,No") != 0)
            return 1;
    }

    // Don't allow to save no packets!
    if (info->savemode == SAVE_SELECTED && call_group_msg_count(info->group) == 0) {
        dialog_run("Unable to save: No selected dialogs.");
        return 1;
    }

    if (info->saveformat == SAVE_PCAP || info->saveformat == SAVE_PCAP_RTP) {
        // Open dump file
        pd = dump_open(fullfile);
        if (access(fullfile, W_OK) != 0) {
            dialog_run(capture_last_error());
            return 1;
        }
    } else {
        // Open a text file
        if (!(f = fopen(fullfile, "w"))) {
            dialog_run("Error: %s", strerror(errno));
            return 0;
        }
    }

    // Get calls iterator
    switch (info->savemode) {
        case SAVE_ALL:
            // Get calls iterator
            calls = sip_calls_iterator();
            break;
        case SAVE_SELECTED:
            // Save selected packets to file
            calls = vector_iterator(info->group->calls);
            break;
        case SAVE_DISPLAYED:
            // Set filtering for this iterator
            calls = sip_calls_iterator();
            vector_iterator_set_filter(&calls, filter_check_call);
            break;
        default:
            break;
    }

    if (info->savemode == SAVE_MESSAGE) {
        if (info->saveformat == SAVE_TXT) {
            // Save selected message to file
            save_msg_txt(f, info->msg);
        } else {
            // Save selected message packet to pcap
            dump_packet(pd, info->msg->packet);
        }
    } else if (info->saveformat == SAVE_TXT) {
        // Save selected packets to file
        while ((call = vector_iterator_next(&calls))) {
            msgs = vector_iterator(call->msgs);
            // Save SIP message content
            while ((msg = vector_iterator_next(&msgs))) {
                save_msg_txt(f, msg);
            }
        }
    } else {
        // Store all messages in a time sorted vector
        sorted = vector_create(100, 50);
        vector_set_sorter(sorted, capture_packet_time_sorter);

        // Count packages for progress bar
        while ((call = vector_iterator_next(&calls))) {
            total += vector_count(call->msgs);
            if (info->saveformat == SAVE_PCAP_RTP)
                total += vector_count(call->rtp_packets);
        }
        vector_iterator_reset(&calls);

        progress = dialog_progress_run("Saving packets...");
        dialog_progress_set_value(progress, 0);

        // Save selected packets to file
        while ((call = vector_iterator_next(&calls))) {
            msgs = vector_iterator(call->msgs);
            // Save SIP message content
            while ((msg = vector_iterator_next(&msgs))) {
                // Update progress bar dialog
                dialog_progress_set_value(progress, (++cur * 100) / total);
                vector_append(sorted, msg->packet);
            }

            // Save RTP packets
            if (info->saveformat == SAVE_PCAP_RTP) {
                rtps = vector_iterator(call->rtp_packets);
                while ((packet = vector_iterator_next(&rtps))) {
                    // Update progress bar dialog
                    dialog_progress_set_value(progress, (++cur * 100) / total);
                    vector_append(sorted, packet);
                }
            }
        }

        // Save sorted packets
        packets = vector_iterator(sorted);
        while ((packet = vector_iterator_next(&packets))) {
            dump_packet(pd, packet);
        }

        dialog_progress_destroy(progress);
    }

    // Close saved file
    if (info->saveformat == SAVE_PCAP || info->saveformat == SAVE_PCAP_RTP) {
        dump_close(pd);
    } else {
        fclose(f);
    }

    // Show success popup
    if (info->savemode == SAVE_MESSAGE) {
      dialog_run("Successfully saved selected SIP message to %s", savefile);
    } else {
      dialog_run("Successfully saved %d dialogs to %s", vector_iterator_count(&calls), savefile);
    }

    return 0;
}