+/* ----------------------------------------------------------------------
+ * Helper routines that contain the actual SCP protocol elements,
+ * so they can be switched to use SFTP.
+ */
+
+int scp_send_errmsg(char *str)
+{
+ back->send("\001", 1); /* scp protocol error prefix */
+ back->send(str, strlen(str));
+ return 0; /* can't fail */
+}
+
+int scp_send_filetimes(unsigned long mtime, unsigned long atime)
+{
+ char buf[80];
+ sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
+ back->send(buf, strlen(buf));
+ return response();
+}
+
+int scp_send_filename(char *name, unsigned long size, int modes)
+{
+ char buf[40];
+ sprintf(buf, "C%04o %lu ", modes, size);
+ back->send(buf, strlen(buf));
+ back->send(name, strlen(name));
+ back->send("\n", 1);
+ return response();
+}
+
+int scp_send_filedata(char *data, int len)
+{
+ int bufsize = back->send(data, len);
+
+ /*
+ * If the network transfer is backing up - that is, the remote
+ * site is not accepting data as fast as we can produce it -
+ * then we must loop on network events until we have space in
+ * the buffer again.
+ */
+ while (bufsize > MAX_SCP_BUFSIZE) {
+ if (!scp_process_network_event())
+ return 1;
+ bufsize = back->sendbuffer();
+ }
+
+ return 0;
+}
+
+int scp_send_finish(void)
+{
+ back->send("", 1);
+ return response();
+}
+
+int scp_send_dirname(char *name, int modes)
+{
+ char buf[40];
+ sprintf(buf, "D%04o 0 ", modes);
+ back->send(buf, strlen(buf));
+ back->send(name, strlen(name));
+ back->send("\n", 1);
+ return response();
+}
+
+int scp_send_enddir(void)
+{
+ back->send("E\n", 2);
+ return response();
+}
+
+int scp_sink_init(void)
+{
+ back->send("", 1);
+ return 0;
+}
+
+#define SCP_SINK_FILE 1
+#define SCP_SINK_DIR 2
+#define SCP_SINK_ENDDIR 3
+struct scp_sink_action {
+ int action; /* FILE, DIR, ENDDIR */
+ char *buf; /* will need freeing after use */
+ char *name; /* filename or dirname (not ENDDIR) */
+ int mode; /* access mode (not ENDDIR) */
+ unsigned long size; /* file size (not ENDDIR) */
+ int settime; /* 1 if atime and mtime are filled */
+ unsigned long atime, mtime; /* access times for the file */
+};
+
+int scp_get_sink_action(struct scp_sink_action *act)
+{
+ int done = 0;
+ int i, bufsize;
+ int action;
+ char ch;
+
+ act->settime = 0;
+ act->buf = NULL;
+ bufsize = 0;
+
+ while (!done) {
+ if (ssh_scp_recv(&ch, 1) <= 0)
+ return 1;
+ if (ch == '\n')
+ bump("Protocol error: Unexpected newline");
+ i = 0;
+ action = ch;
+ do {
+ if (ssh_scp_recv(&ch, 1) <= 0)
+ bump("Lost connection");
+ if (i >= bufsize) {
+ bufsize = i + 128;
+ act->buf = srealloc(act->buf, bufsize);
+ }
+ act->buf[i++] = ch;
+ } while (ch != '\n');
+ act->buf[i - 1] = '\0';
+ switch (action) {
+ case '\01': /* error */
+ tell_user(stderr, "%s\n", act->buf);
+ errs++;
+ continue; /* go round again */
+ case '\02': /* fatal error */
+ bump("%s", act->buf);
+ case 'E':
+ back->send("", 1);
+ act->action = SCP_SINK_ENDDIR;
+ return 0;
+ case 'T':
+ if (sscanf(act->buf, "%ld %*d %ld %*d",
+ &act->mtime, &act->atime) == 2) {
+ act->settime = 1;
+ back->send("", 1);
+ continue; /* go round again */
+ }
+ bump("Protocol error: Illegal time format");
+ case 'C':
+ case 'D':
+ act->action = (action == 'C' ? SCP_SINK_FILE : SCP_SINK_DIR);
+ break;
+ default:
+ bump("Protocol error: Expected control record");
+ }
+ /*
+ * We will go round this loop only once, unless we hit
+ * `continue' above.
+ */
+ done = 1;
+ }
+
+ /*
+ * If we get here, we must have seen SCP_SINK_FILE or
+ * SCP_SINK_DIR.
+ */
+ if (sscanf(act->buf, "%o %lu %n", &act->mode, &act->size, &i) != 2)
+ bump("Protocol error: Illegal file descriptor format");
+ act->name = act->buf + i;
+ return 0;
+}
+
+int scp_accept_filexfer(void)
+{
+ back->send("", 1);
+ return 0; /* can't fail */
+}
+
+int scp_recv_filedata(char *data, int len)
+{
+ return ssh_scp_recv(data, len);
+}
+
+int scp_finish_filerecv(void)
+{
+ back->send("", 1);
+ return response();
+}
+
+/* ----------------------------------------------------------------------