/*
20140210
Jan Mojzis
Public domain.
*/

#include "bug.h"
#include "buf.h"
#include "channel.h"
#include "ssh.h"
#include "packetparser.h"
#include "packet.h"

int packet_channel_recv_data(struct buf *b) {

    long long pos = 0;
    crypto_uint32 len, id;
    crypto_uint8 ch;

    pos = packetparser_uint8(b->buf, b->len, pos,
                             &ch); /* byte      SSH_MSG_CHANNEL_DATA */
    if (ch != SSH_MSG_CHANNEL_DATA) bug_proto();
    pos = packetparser_uint32(b->buf, b->len, pos,
                              &id); /* uint32    recipient channel    */
    if (id != channel_getid()) bug_proto();
    pos = packetparser_uint32(b->buf, b->len, pos, &len); /* string    data */
    pos = packetparser_skip(b->buf, b->len, pos, len);
    pos = packetparser_end(b->buf, b->len, pos);

    channel_put(b->buf + pos - len, len);
    buf_purge(b);
    return 1;
}

int packet_channel_recv_extendeddata(struct buf *b) {

    long long pos = 0;
    crypto_uint32 type, len, id;
    crypto_uint8 ch;

    pos = packetparser_uint8(b->buf, b->len, pos,
                             &ch); /* byte SSH_MSG_CHANNEL_EXTENDED_DATA */
    if (ch != SSH_MSG_CHANNEL_EXTENDED_DATA) bug_proto();
    pos = packetparser_uint32(b->buf, b->len, pos,
                              &id); /* uint32 recipient channel */
    if (id != channel_getid()) bug_proto();
    pos = packetparser_uint32(b->buf, b->len, pos,
                              &type); /* uint32 data type code */
    pos = packetparser_uint32(b->buf, b->len, pos, &len); /* string data */
    pos = packetparser_skip(b->buf, b->len, pos, len);
    pos = packetparser_end(b->buf, b->len, pos);

    (void) type;
    channel_putextended(len);
    buf_purge(b);
    return 1;
}

int packet_channel_recv_windowadjust(struct buf *b) {

    long long pos = 0;
    crypto_uint32 len, id;
    crypto_uint8 ch;

    pos = packetparser_uint8(b->buf, b->len, pos,
                             &ch); /* byte      SSH_MSG_CHANNEL_WINDOW_ADJUST */
    if (ch != SSH_MSG_CHANNEL_WINDOW_ADJUST) bug_proto();
    pos = packetparser_uint32(b->buf, b->len, pos,
                              &id); /* uint32    recipient channel */
    if (id != channel_getid()) bug_proto();
    pos = packetparser_uint32(b->buf, b->len, pos,
                              &len); /* uint32    bytes to add */
    pos = packetparser_end(b->buf, b->len, pos);

    channel_incrementremotewindow(len);
    buf_purge(b);
    return 1;
}

int packet_channel_recv_eof(struct buf *b) {

    long long pos = 0;
    crypto_uint32 id;
    crypto_uint8 ch;

    pos = packetparser_uint8(b->buf, b->len, pos,
                             &ch); /* byte      SSH_MSG_CHANNEL_EOF */
    if (ch != SSH_MSG_CHANNEL_EOF) bug_proto();
    pos = packetparser_uint32(b->buf, b->len, pos,
                              &id); /* uint32    recipient channel   */
    if (id != channel_getid()) bug_proto();
    pos = packetparser_end(b->buf, b->len, pos);

    log_d1("packet=SSH_MSG_CHANNEL_EOF received");
    channel_puteof();
    buf_purge(b);
    return 1;
}

int packet_channel_recv_close(struct buf *b) {

    long long pos = 0;
    crypto_uint32 id;
    crypto_uint8 ch;

    pos = packetparser_uint8(b->buf, b->len, pos,
                             &ch); /* byte      SSH_MSG_CHANNEL_CLOSE */
    if (ch != SSH_MSG_CHANNEL_CLOSE) bug_proto();
    pos = packetparser_uint32(b->buf, b->len, pos,
                              &id); /* uint32    recipient channel   */
    if (id != channel_getid()) bug_proto();
    pos = packetparser_end(b->buf, b->len, pos);

    log_d1("packet=SSH_MSG_CHANNEL_CLOSE received");
    packet_channel_send_eof(b);
    if (!packet.flagclosesent) {
        buf_purge(b);
        buf_putnum8(b, SSH_MSG_CHANNEL_CLOSE);
        buf_putnum32(b, channel_getid());
        packet_put(b);
        packet.flagclosesent = 1;
    }
    packet.flagchanneleofreceived = 1;
    buf_purge(b);
    return 1;
}
