PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr,
					       pjsip_rx_data *rdata)
{
    pjsip_transport *tr = rdata->tp_info.transport;

    char *current_pkt;
    pj_size_t remaining_len;
    pj_size_t total_processed = 0;

    /* Check size. */
    pj_assert(rdata->pkt_info.len > 0);
    if (rdata->pkt_info.len <= 0)
	return -1;

    current_pkt = rdata->pkt_info.packet;
    remaining_len = rdata->pkt_info.len;

    tr->last_recv_len = rdata->pkt_info.len;
    pj_get_timestamp(&tr->last_recv_ts);
    
    /* Must NULL terminate buffer. This is the requirement of the 
     * parser etc. 
     */
    current_pkt[remaining_len] = '\0';

    /* Process all message fragments. */
    while (remaining_len > 0) {

	pjsip_msg *msg;
	char *p, *end;
	char saved;
	pj_size_t msg_fragment_size;

	/* Skip leading newlines as pjsip_find_msg() currently can't
	 * handle leading newlines.
	 */
	for (p=current_pkt, end=p+remaining_len; p!=end; ++p) {
	    if (*p != '\r' && *p != '\n')
		break;
	}
	if (p!=current_pkt) {
	    remaining_len -= (p - current_pkt);
	    total_processed += (p - current_pkt);

	    /* Notify application about the dropped newlines */
	    if (mgr->tp_drop_data_cb) {
		pjsip_tp_dropped_data dd;
		pj_bzero(&dd, sizeof(dd));
		dd.tp = tr;
		dd.data = current_pkt;
		dd.len = p - current_pkt;
		dd.status = PJ_EIGNORED;
		(*mgr->tp_drop_data_cb)(&dd);
	    }

	    current_pkt = p;
	    if (remaining_len == 0) {
		return total_processed;
	    }
	}

	/* Initialize default fragment size. */
	msg_fragment_size = remaining_len;

	/* Clear and init msg_info in rdata. 
	 * Endpoint might inspect the values there when we call the callback
	 * to report some errors.
	 */
	pj_bzero(&rdata->msg_info, sizeof(rdata->msg_info));
	pj_list_init(&rdata->msg_info.parse_err);
	rdata->msg_info.msg_buf = current_pkt;
	rdata->msg_info.len = (int)remaining_len;

	/* For TCP transport, check if the whole message has been received. */
	if ((tr->flag & PJSIP_TRANSPORT_DATAGRAM) == 0) {
	    pj_status_t msg_status;
	    msg_status = pjsip_find_msg(current_pkt, remaining_len, PJ_FALSE, 
                                        &msg_fragment_size);
	    if (msg_status != PJ_SUCCESS) {
		if (remaining_len == PJSIP_MAX_PKT_LEN) {
		    mgr->on_rx_msg(mgr->endpt, PJSIP_ERXOVERFLOW, rdata);
		    
		    /* Notify application about the message overflow */
	    	    if (mgr->tp_drop_data_cb) {
			pjsip_tp_dropped_data dd;
			pj_bzero(&dd, sizeof(dd));
			dd.tp = tr;
			dd.data = current_pkt;
			dd.len = msg_fragment_size;
			dd.status = PJSIP_ERXOVERFLOW;
			(*mgr->tp_drop_data_cb)(&dd);
	    	    }
		    
		    /* Exhaust all data. */
		    return rdata->pkt_info.len;
		} else {
		    /* Not enough data in packet. */
		    return total_processed;
		}
	    }
	}

	/* Update msg_info. */
	rdata->msg_info.len = (int)msg_fragment_size;

	/* Null terminate packet */
	saved = current_pkt[msg_fragment_size];
	current_pkt[msg_fragment_size] = '\0';

	/* Parse the message. */
	rdata->msg_info.msg = msg = 
	    pjsip_parse_rdata( current_pkt, msg_fragment_size, rdata);

	/* Restore null termination */
	current_pkt[msg_fragment_size] = saved;

	/* Check for parsing syntax error */
	if (msg==NULL || !pj_list_empty(&rdata->msg_info.parse_err)) {
	    pjsip_parser_err_report *err;
	    char buf[256];
	    pj_str_t tmp;

	    /* Gather syntax error information */
	    tmp.ptr = buf; tmp.slen = 0;
	    err = rdata->msg_info.parse_err.next;
	    while (err != &rdata->msg_info.parse_err) {
		int len;
		len = pj_ansi_snprintf(tmp.ptr+tmp.slen, sizeof(buf)-tmp.slen,
				       ": %s exception when parsing '%.*s' "
				       "header on line %d col %d",
				       pj_exception_id_name(err->except_code),
				       (int)err->hname.slen, err->hname.ptr,
				       err->line, err->col);
		if (len >= (int)sizeof(buf)-tmp.slen) {
		    len = (int)sizeof(buf)-tmp.slen;
		}
		if (len > 0) {
		    tmp.slen += len;
		}
		err = err->next;
	    }

	    /* Only print error message if there's error.
	     * Sometimes we receive blank packets (packets with only CRLF)
	     * which were sent to keep NAT bindings.
	     */
	    if (tmp.slen) {
		PJ_LOG(1, (THIS_FILE, 
		      "Error processing %d bytes packet from %s %s:%d %.*s:\n"
		      "%.*s\n"
		      "-- end of packet.",
		      msg_fragment_size,
		      rdata->tp_info.transport->type_name,
		      rdata->pkt_info.src_name, 
		      rdata->pkt_info.src_port,
		      (int)tmp.slen, tmp.ptr,
		      (int)msg_fragment_size,
		      rdata->msg_info.msg_buf));
	    }

	    /* Notify application about the dropped data (syntax error) */
	    if (tmp.slen && mgr->tp_drop_data_cb) {
		pjsip_tp_dropped_data dd;
		pj_bzero(&dd, sizeof(dd));
		dd.tp = tr;
		dd.data = current_pkt;
		dd.len = msg_fragment_size;
		dd.status = PJSIP_EINVALIDMSG;
		(*mgr->tp_drop_data_cb)(&dd);
		
		if (dd.len > 0 && dd.len < msg_fragment_size)
		    msg_fragment_size = dd.len;
	    }

	    goto finish_process_fragment;
	}

	/* Perform basic header checking. */
	if (rdata->msg_info.cid == NULL ||
	    rdata->msg_info.cid->id.slen == 0 || 
	    rdata->msg_info.from == NULL || 
	    rdata->msg_info.to == NULL || 
	    rdata->msg_info.via == NULL || 
	    rdata->msg_info.cseq == NULL) 
	{
	    mgr->on_rx_msg(mgr->endpt, PJSIP_EMISSINGHDR, rdata);

	    /* Notify application about the missing header. */
	    if (mgr->tp_drop_data_cb) {
		pjsip_tp_dropped_data dd;
		pj_bzero(&dd, sizeof(dd));
		dd.tp = tr;
		dd.data = current_pkt;
		dd.len = msg_fragment_size;
		dd.status = PJSIP_EMISSINGHDR;
		(*mgr->tp_drop_data_cb)(&dd);	    
	    }
	    goto finish_process_fragment;
	}

	/* For request: */
	if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) {
	    /* always add received parameter to the via. */
	    pj_strdup2(rdata->tp_info.pool, 
		       &rdata->msg_info.via->recvd_param, 
		       rdata->pkt_info.src_name);

	    /* RFC 3581:
	     * If message contains "rport" param, put the received port there.
	     */
	    if (rdata->msg_info.via->rport_param == 0) {
		rdata->msg_info.via->rport_param = rdata->pkt_info.src_port;
	    }
	} else {
	    /* Drop malformed responses */
	    if (rdata->msg_info.msg->line.status.code < 100 ||
		rdata->msg_info.msg->line.status.code >= 700)
	    {
		mgr->on_rx_msg(mgr->endpt, PJSIP_EINVALIDSTATUS, rdata);

		/* Notify application about the invalid status. */
		if (mgr->tp_drop_data_cb) {
		    pjsip_tp_dropped_data dd;
		    pj_bzero(&dd, sizeof(dd));
		    dd.tp = tr;
		    dd.data = current_pkt;
		    dd.len = msg_fragment_size;
		    dd.status = PJSIP_EINVALIDSTATUS;
		    (*mgr->tp_drop_data_cb)(&dd);	    
		}
		goto finish_process_fragment;
	    }
	}

	/* Drop response message if it has more than one Via.
	*/
	/* This is wrong. Proxy DOES receive responses with multiple
	 * Via headers! Thanks Aldo <acampi at deis.unibo.it> for pointing
	 * this out.

	if (msg->type == PJSIP_RESPONSE_MSG) {
	    pjsip_hdr *hdr;
	    hdr = (pjsip_hdr*)rdata->msg_info.via->next;
	    if (hdr != &msg->hdr) {
		hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr);
		if (hdr) {
		    mgr->on_rx_msg(mgr->endpt, PJSIP_EMULTIPLEVIA, rdata);
		    goto finish_process_fragment;
		}
	    }
	}
	*/

	/* Call the transport manager's upstream message callback.
	 */
	mgr->on_rx_msg(mgr->endpt, PJ_SUCCESS, rdata);


finish_process_fragment:
	total_processed += msg_fragment_size;
	current_pkt += msg_fragment_size;
	remaining_len -= msg_fragment_size;

    }	/* while (rdata->pkt_info.len > 0) */


    return total_processed;
}