int wolfSSH_SFTP_read(WOLFSSH* ssh)
{
    int maxSz, ret = WS_SUCCESS;
    WS_SFTP_RECV_STATE* state = NULL;

    WLOG(WS_LOG_SFTP, "Entering wolfSSH_SFTP_read()");

    if (ssh == NULL)
        return WS_BAD_ARGUMENT;

    if (ssh->error == WS_WANT_READ || ssh->error == WS_WANT_WRITE)
        ssh->error = WS_SUCCESS;

    state = ssh->recvState;
    if (state == NULL) {
        state = (WS_SFTP_RECV_STATE*)WMALLOC(sizeof(WS_SFTP_RECV_STATE),
                ssh->ctx->heap, DYNTYPE_SFTP_STATE);
        if (state == NULL) {
            ssh->error = WS_MEMORY_E;
            return WS_FATAL_ERROR;
        }
        WMEMSET(state, 0, sizeof(WS_SFTP_RECV_STATE));
        ssh->recvState = state;
        state->state = STATE_RECV_READ;
    }

    switch (state->state) {
        case STATE_RECV_READ:
            /* Wait for packet to come in then handle it, maxSz is the size of
             * packet expected to come. */
            ret = SFTP_GetHeader(ssh, (word32*)&state->reqId,
                    &state->type, &state->buffer);
            if (ret <= 0) {
                return WS_FATAL_ERROR;
            }
            if (wolfSSH_SFTP_buffer_create(ssh, &state->buffer, ret) !=
                    WS_SUCCESS) {
                return WS_MEMORY_E;
            }
            ssh->reqId  = state->reqId;

            state->state = STATE_RECV_DO;
            NO_BREAK;

        case STATE_RECV_DO:
            ret = wolfSSH_SFTP_buffer_read(ssh, &state->buffer,
                    state->buffer.sz);
            if (ret < 0) {
                if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE)
                        wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV);
                return ret;
            }

            switch (state->type) {
                case WOLFSSH_FTP_REALPATH:
                    ret = wolfSSH_SFTP_RecvRealPath(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;

                case WOLFSSH_FTP_RMDIR:
                    ret = wolfSSH_SFTP_RecvRMDIR(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;

                case WOLFSSH_FTP_MKDIR:
                    ret = wolfSSH_SFTP_RecvMKDIR(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;

                case WOLFSSH_FTP_STAT:
                    ret = wolfSSH_SFTP_RecvSTAT(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;

                case WOLFSSH_FTP_LSTAT:
                    ret = wolfSSH_SFTP_RecvLSTAT(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;

            #ifndef USE_WINDOWS_API
                case WOLFSSH_FTP_FSTAT:
                    ret = wolfSSH_SFTP_RecvFSTAT(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;
            #endif

                case WOLFSSH_FTP_OPEN:
                    ret = wolfSSH_SFTP_RecvOpen(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;

                case WOLFSSH_FTP_READ:
                    ret = wolfSSH_SFTP_RecvRead(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;

                case WOLFSSH_FTP_WRITE:
                    ret = wolfSSH_SFTP_RecvWrite(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;

                case WOLFSSH_FTP_CLOSE:
                    ret = wolfSSH_SFTP_RecvClose(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;

                case WOLFSSH_FTP_REMOVE:
                    ret = wolfSSH_SFTP_RecvRemove(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;

                case WOLFSSH_FTP_RENAME:
                    ret = wolfSSH_SFTP_RecvRename(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;
            #ifndef _WIN32_WCE
                case WOLFSSH_FTP_SETSTAT:
                    ret = wolfSSH_SFTP_RecvSetSTAT(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;
            #endif

            #ifndef NO_WOLFSSH_DIR
                case WOLFSSH_FTP_OPENDIR:
                    ret = wolfSSH_SFTP_RecvOpenDir(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;

                case WOLFSSH_FTP_READDIR:
                    ret = wolfSSH_SFTP_RecvReadDir(ssh, state->reqId,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    break;
            #endif

                default:
                    WLOG(WS_LOG_SFTP, "Unknown packet type [%d] received",
                            state->type);
                    if (wolfSSH_SFTP_CreateStatus(ssh, WOLFSSH_FTP_FAILURE,
                                state->reqId, "Unknown/Unsupported packet type",
                                "English", NULL, (word32*)&maxSz) != WS_SIZE_ONLY) {
                        wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV);
                        return WS_FATAL_ERROR;
                    }


                    if (maxSz > (int)wolfSSH_SFTP_buffer_size(&state->buffer)) {
                        if (wolfSSH_SFTP_buffer_create(ssh, &state->buffer,
                                    maxSz) != WS_SUCCESS) {
                            wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV);
                            return WS_FATAL_ERROR;
                        }
                    }

                    maxSz = (int)wolfSSH_SFTP_buffer_size(&state->buffer);
                    ret = wolfSSH_SFTP_CreateStatus(ssh, WOLFSSH_FTP_FAILURE,
                            state->reqId,
                            "Unknown/Unsupported packet type", "English",
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            (word32*)(&maxSz));

                    if (ret == WS_SUCCESS) {
                        ret = wolfSSH_SFTP_buffer_set_size(&state->buffer,
                                (word32)maxSz);
                    }

                    if (ret == WS_SUCCESS) {
                        /* set send out buffer, "state->data" is taken by ssh  */
                        wolfSSH_SFTP_RecvSetSend(ssh,
                            wolfSSH_SFTP_buffer_data(&state->buffer),
                            wolfSSH_SFTP_buffer_size(&state->buffer));
                    }
            }

            /* break out if encountering an error with nothing stored to send */
            if (ret < 0 && !state->toSend) {
                if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE)
                    wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV);
                return ret;
            }
            state->buffer.idx   = 0;
            state->state = STATE_RECV_SEND;
            NO_BREAK;

        case STATE_RECV_SEND:
            if (state->toSend) {
                do {
                    if (state->toSend != 2) {
                        ret = wolfSSH_SFTP_buffer_send(ssh, &state->buffer);
                        if (ret < 0) {
                            if (ssh->error != WS_WANT_READ &&
                                    ssh->error != WS_WANT_WRITE &&
                                    ssh->error != WS_REKEYING &&
                                    ssh->error != WS_WINDOW_FULL)
                                wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV);
                            return WS_FATAL_ERROR;
                        }
                    }
                    state->toSend = 1;

                    if (wolfSSH_SFTP_buffer_idx(&state->buffer)
                            < wolfSSH_SFTP_buffer_size(&state->buffer)) {
                        ret = wolfSSH_worker(ssh, NULL);
                        if (ret != WS_SUCCESS && ssh->error == WS_WANT_READ) {
                            /* was something there to read, try again */
                            state->toSend = 2;
                            return WS_FATAL_ERROR;
                        }
                    }
                } while (wolfSSH_SFTP_buffer_idx(&state->buffer)
                            < wolfSSH_SFTP_buffer_size(&state->buffer));
                ret = WS_SUCCESS;
                state->toSend = 0;
            }
            wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV);
            return ret;

        default:
            WLOG(WS_LOG_SFTP, "Unknown SFTP read state");
            wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV);
            return WS_FATAL_ERROR;

    }
}