Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions teraterm/teraterm/agent_jsonrpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ static const char *agent_err_msg(int rc)
return "not allowed";
case AGENT_ERR_NOSESSION:
return "unknown session";
case AGENT_ERR_BUSY:
return "transfer in progress";
default:
return rc < 0 ? "operation failed" : NULL;
}
Expand Down Expand Up @@ -221,6 +223,18 @@ static cJSON *call_status(const AgentBackend *be, const cJSON *params, const cha
cJSON_AddNumberToObject(r, "offset", (double)st.offset);
cJSON_AddNumberToObject(r, "cols", st.cols);
cJSON_AddNumberToObject(r, "rows", st.rows);

/* Transfer state as a tagged union: "ok" is meaningful only once done. */
cJSON *t = cJSON_CreateObject();
if (st.xfer_active) {
cJSON_AddStringToObject(t, "state", "active");
} else if (st.xfer_last_result < 0) {
cJSON_AddStringToObject(t, "state", "idle");
} else {
cJSON_AddStringToObject(t, "state", "done");
cJSON_AddBoolToObject(t, "ok", st.xfer_last_result == 1);
}
cJSON_AddItemToObject(r, "transfer", t);
return r;
}

Expand Down Expand Up @@ -381,6 +395,28 @@ static cJSON *call_send_key(const AgentBackend *be, const cJSON *params, const c
return r;
}

static cJSON *call_zmodem_send(const AgentBackend *be, const cJSON *params, const char **err)
{
const cJSON *pj = cJSON_GetObjectItem(params, "path");
if (!cJSON_IsString(pj) || pj->valuestring[0] == 0) {
*err = "path required";
return NULL;
}
int binary = 1;
const cJSON *bj = cJSON_GetObjectItem(params, "binary");
if (cJSON_IsBool(bj))
binary = cJSON_IsTrue(bj) ? 1 : 0;

int rc = be->zmodem_send(be->ctx, param_session(params), pj->valuestring, binary);
*err = agent_err_msg(rc);
if (*err != NULL)
return NULL;

cJSON *r = cJSON_CreateObject();
cJSON_AddTrueToObject(r, "started");
return r;
}

cJSON *agent_call(const AgentBackend *be, const char *method, const cJSON *params, const char **err)
{
*err = NULL;
Expand All @@ -398,6 +434,8 @@ cJSON *agent_call(const AgentBackend *be, const char *method, const cJSON *param
return call_send_bytes(be, params, err);
if (strcmp(method, "send_key") == 0)
return call_send_key(be, params, err);
if (strcmp(method, "zmodem_send") == 0)
return call_zmodem_send(be, params, err);
*err = "unknown method";
return NULL;
}
Expand Down
9 changes: 9 additions & 0 deletions teraterm/teraterm/agent_jsonrpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ extern "C" {
#define AGENT_ERR_NOTCONN (-1) /* no live connection (cv null / !Ready) */
#define AGENT_ERR_NOTALLOWED (-2) /* send not armed for this session */
#define AGENT_ERR_NOSESSION (-3) /* unknown session id */
#define AGENT_ERR_BUSY (-4) /* a file transfer is already in flight */

typedef struct {
int ready;
int port_type; /* cv->PortType (IdTCPIP / IdSerial / ...) */
uint64_t offset; /* ring total; next unread offset */
int cols, rows;
char host[256];
int xfer_active; /* non-zero while a file transfer is in flight */
int xfer_last_result; /* -1 none, 0 failed, 1 succeeded (valid when !active) */
} AgentStatus;

typedef struct {
Expand Down Expand Up @@ -75,6 +78,12 @@ typedef struct {

/* Send raw bytes. Returns bytes accepted or a negative AGENT_ERR_*. */
int (*send_bytes)(void *ctx, const char *session, const void *data, size_t len);

/* Start a ZMODEM send of the local file at pathU8 over the session's line
* (binary != 0 selects binary mode). Async: returns 0 once the transfer is
* started, or a negative AGENT_ERR_* (NOTALLOWED if send is not armed,
* BUSY if a transfer is already in flight). */
int (*zmodem_send)(void *ctx, const char *session, const char *pathU8, int binary);
} AgentBackend;

/* Per-connection state threaded across requests on one socket. */
Expand Down
6 changes: 6 additions & 0 deletions teraterm/teraterm/agent_mcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ static const McpTool TOOLS[] = {
{"send_key", "Send a named key (enter, tab, esc, ctrl-c, up, down, left, right, ...).",
"{\"type\":\"object\",\"properties\":{\"session\":{\"type\":\"string\"},"
"\"key\":{\"type\":\"string\"}},\"required\":[\"key\"]}"},
{"zmodem_send",
"Start a ZMODEM send of a local file over the connection (the peer must be "
"running a ZMODEM receiver, e.g. 'rz'). Async: returns once started; poll "
"status.transfer for state (active -> done) and ok.",
"{\"type\":\"object\",\"properties\":{\"session\":{\"type\":\"string\"},"
"\"path\":{\"type\":\"string\"},\"binary\":{\"type\":\"boolean\"}},\"required\":[\"path\"]}"},
};

static cJSON *build_tools_array(void)
Expand Down
43 changes: 43 additions & 0 deletions teraterm/teraterm/agent_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include "tttypes.h"
#include "ttwinman.h" /* cv, ts, HVTWin */
#include "ttcommon.h" /* CommBinaryOut, CommTextOutW */
#include "codeconv.h" /* ToWcharU8 */
#include "filesys.h" /* ZMODEMStartSend, ProtoGetProtoFlag, ProtoGetLastResult */

#include "agent_shmem.h"
#include "agent_jsonrpc.h"
Expand Down Expand Up @@ -177,6 +179,11 @@ static int be_get_status(void *ctx, const char *session, AgentStatus *out)
if (slot == A.slot) {
out->cols = ts.TerminalWidth;
out->rows = ts.TerminalHeight;
out->xfer_active = ProtoGetProtoFlag() ? 1 : 0;
out->xfer_last_result = ProtoGetLastResult();
} else {
/* Another window's transfer state is not visible from this process. */
out->xfer_last_result = -1;
}
return 0;
}
Expand Down Expand Up @@ -268,6 +275,41 @@ static int be_send_bytes(void *ctx, const char *session, const void *data, size_
return be_send_common(session, AGENT_CMD_BINARY, data, len);
}

static int be_zmodem_send(void *ctx, const char *session, const char *pathU8, int binary)
{
(void)ctx;
int slot = resolve_slot(session);
if (slot < 0) {
return AGENT_ERR_NOSESSION;
}
AgentSession *s = &A.shm->sessions[slot];
if (!s->ready) {
return AGENT_ERR_NOTCONN;
}
if (!s->send_armed) {
return AGENT_ERR_NOTALLOWED;
}
/* Local window only. ZMODEMStartSend arms the protocol pump on this GUI
* thread against this process's cv/FileVar, and the transfer's outcome is
* observable only through this window's own status. A foreign window's
* transfer would be neither driven nor visible here, so cross-window ZMODEM
* is deferred (send_bytes/text still hand off via the shm queue). */
if (slot != A.slot) {
return AGENT_ERR_NOTALLOWED;
}
wchar_t *w = ToWcharU8(pathU8);
if (w == NULL) {
return AGENT_ERR_BUSY;
}
/* FALSE start => a transfer is already in flight (FileVar != NULL) or a
* resource failure; both surface to the caller as "busy". A bad path is
* NOT caught here -- the send starts, then fails via ProtoEnd, which the
* caller observes as transfer.state=done, ok=false. */
BOOL ok = ZMODEMStartSend(w, binary ? 1 : 0, FALSE);
free(w);
return ok ? 0 : AGENT_ERR_BUSY;
}

static void init_backend(void)
{
memset(&A.backend, 0, sizeof(A.backend));
Expand All @@ -280,6 +322,7 @@ static void init_backend(void)
A.backend.read_scrollback = be_read_scrollback;
A.backend.send_text = be_send_text;
A.backend.send_bytes = be_send_bytes;
A.backend.zmodem_send = be_zmodem_send;
}

/* ---- client bookkeeping ---- */
Expand Down
1 change: 1 addition & 0 deletions teraterm/teraterm/filesys.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ void FileSendPause(BOOL Pause);

// filesys_proto.cpp
BOOL ProtoGetProtoFlag(void);
int ProtoGetLastResult(void); /* -1 none, 0 failed, 1 succeeded */
BOOL IsFileVarNULL(void);
void ProtoEnd(void);
int ProtoDlgParse(void);
Expand Down
16 changes: 14 additions & 2 deletions teraterm/teraterm/filesys_proto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ typedef enum {
static PFileVarProto FileVar = NULL;
static PProtoDlg PtDlg = NULL;
static BOOL cv_ProtoFlag = FALSE;
/* Outcome of the most recent transfer for out-of-band observers (the agent):
* -1 none yet, 0 failed/cancelled, 1 succeeded. Set at the ProtoEnd funnel. */
static int cv_ProtoLastResult = -1;

static void _SetDlgTime(TFileVarProto *fv, DWORD elapsed, int bytes)
{
Expand Down Expand Up @@ -438,14 +441,23 @@ void ProtoEnd(void)

CloseProtoDlg();

if ((FileVar!=NULL) && FileVar->Success)
if ((FileVar!=NULL) && FileVar->Success) {
cv_ProtoLastResult = 1;
EndDdeCmnd(1);
else
}
else {
cv_ProtoLastResult = 0;
EndDdeCmnd(0);
}

FreeFileVar_(&FileVar);
}

int ProtoGetLastResult(void)
{
return cv_ProtoLastResult;
}

/* ダイアログを中央に移動する */
static void CenterCommonDialog(HWND hDlg)
{
Expand Down
Loading
Loading