+ if ((ic_send = iconv_open(UTF8_CHARSET, charset)) == (iconv_t)-1) {
+ rprintf(FERROR, "iconv_open(\"%s\", \"%s\") failed\n",
+ UTF8_CHARSET, charset);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ if ((ic_recv = iconv_open(charset, UTF8_CHARSET)) == (iconv_t)-1) {
+ rprintf(FERROR, "iconv_open(\"%s\", \"%s\") failed\n",
+ charset, UTF8_CHARSET);
+ exit_cleanup(RERR_UNSUPPORTED);
+ }
+
+ if (verbose > 1) {
+ rprintf(FINFO, "%s charset: %s\n",
+ am_server ? "server" : "client",
+ *charset ? charset : "[LOCALE]");
+ }
+# endif
+}
+
+# ifdef ICONV_OPTION
+
+/* This function converts the characters in the "in" xbuf into characters
+ * in the "out" xbuf. The "len" of the "in" xbuf is used starting from its
+ * "pos". The "size" of the "out" xbuf restricts how many characters can be
+ * stored, starting at its "pos+len" position. Note that the last byte of
+ * the buffer is never used, which reserves space for a terminating '\0'.
+ * We return a 0 on success or a -1 on error. An error also sets errno to
+ * E2BIG, EILSEQ, or EINVAL (see below); otherwise errno will be set to 0.
+ * The "in" xbuf is altered to update "pos" and "len". The "out" xbuf has
+ * data appended, and its "len" incremented. If ICB_EXPAND_OUT is set in
+ * "flags", the "out" xbuf will also be allocated if empty, and expanded if
+ * too small (so E2BIG will not be returned). If ICB_INCLUDE_BAD is set in
+ * "flags", any badly-encoded chars are included verbatim in the "out" xbuf,
+ * so EILSEQ will not be returned. Likewise for ICB_INCLUDE_INCOMPLETE with
+ * respect to an incomplete multi-byte char at the end, which ensures that
+ * EINVAL is not returned. Anytime "in.pos" is 0 we will reset the iconv()
+ * state prior to processing the characters. */
+int iconvbufs(iconv_t ic, xbuf *in, xbuf *out, int flags)
+{
+ ICONV_CONST char *ibuf;
+ size_t icnt, ocnt;
+ char *obuf;
+
+ if (!out->size && flags & ICB_EXPAND_OUT)
+ alloc_xbuf(out, 1024);
+
+ if (!in->pos)
+ iconv(ic, NULL, 0, NULL, 0);
+
+ ibuf = in->buf + in->pos;
+ icnt = in->len;
+
+ obuf = out->buf + (out->pos + out->len);
+ ocnt = out->size - (out->pos + out->len) - 1;
+
+ while (icnt) {
+ while (iconv(ic, &ibuf, &icnt, &obuf, &ocnt) == (size_t)-1) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EINVAL) {
+ if (!(flags & ICB_INCLUDE_INCOMPLETE))
+ goto finish;
+ } else if (errno == EILSEQ) {
+ if (!(flags & ICB_INCLUDE_BAD))
+ goto finish;
+ } else {
+ size_t opos = obuf - out->buf;
+ if (!(flags & ICB_EXPAND_OUT)) {
+ errno = E2BIG;
+ goto finish;
+ }
+ realloc_xbuf(out, out->size + 1024);
+ obuf = out->buf + opos;
+ ocnt += 1024;
+ continue;
+ }
+ *obuf++ = *ibuf++;
+ ocnt--, icnt--;
+ }
+ }
+
+ errno = 0;
+
+ finish:
+ in->len = icnt;
+ in->pos = ibuf - in->buf;
+ out->len = obuf - out->buf - out->pos;
+
+ return errno ? -1 : 0;
+}
+# endif
+#endif
+
+int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr,
+ char *buf, int *len_ptr)
+{
+ int len, iflags = 0;
+ struct file_list *flist;
+ uchar fnamecmp_type = FNAMECMP_FNAME;
+ int ndx;
+
+ read_loop:
+ while (1) {
+ ndx = read_ndx(f_in);
+
+ if (ndx >= 0)
+ break;
+ if (ndx == NDX_DONE)
+ return ndx;
+ if (!inc_recurse || am_sender)
+ goto invalid_ndx;
+ if (ndx == NDX_FLIST_EOF) {
+ flist_eof = 1;
+ send_msg(MSG_FLIST_EOF, "", 0, 0);
+ continue;
+ }
+ ndx = NDX_FLIST_OFFSET - ndx;
+ if (ndx < 0 || ndx >= dir_flist->used) {
+ ndx = NDX_FLIST_OFFSET - ndx;
+ rprintf(FERROR,
+ "[%s] Invalid dir index: %d (%d - %d)\n",
+ who_am_i(), ndx, NDX_FLIST_OFFSET,
+ NDX_FLIST_OFFSET - dir_flist->used + 1);
+ exit_cleanup(RERR_PROTOCOL);
+ }
+
+ /* Send everything read from f_in to msg_fd_out. */
+ send_msg_int(MSG_FLIST, ndx);
+ start_flist_forward(f_in);
+ if (verbose > 3) {
+ rprintf(FINFO, "[%s] receiving flist for dir %d\n",
+ who_am_i(), ndx);
+ }
+ flist = recv_file_list(f_in);
+ flist->parent_ndx = ndx;
+ stop_flist_forward();
+ }
+
+ iflags = protocol_version >= 29 ? read_shortint(f_in)
+ : ITEM_TRANSFER | ITEM_MISSING_DATA;
+
+ /* Honor the old-style keep-alive indicator. */
+ if (protocol_version < 30
+ && ndx == cur_flist->used && iflags == ITEM_IS_NEW) {
+ if (am_sender)
+ maybe_send_keepalive();
+ goto read_loop;
+ }
+
+ if (!(flist = flist_for_ndx(ndx))) {
+ invalid_ndx:
+ rprintf(FERROR,
+ "Invalid file index: %d (%d - %d) with iflags %x [%s]\n",
+ ndx, first_flist->ndx_start - 1, first_flist->prev->ndx_end,
+ iflags, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ cur_flist = flist;
+
+ if (iflags & ITEM_BASIS_TYPE_FOLLOWS)
+ fnamecmp_type = read_byte(f_in);
+ *type_ptr = fnamecmp_type;
+
+ if (iflags & ITEM_XNAME_FOLLOWS) {
+ if ((len = read_vstring(f_in, buf, MAXPATHLEN)) < 0)
+ exit_cleanup(RERR_PROTOCOL);
+ } else {
+ *buf = '\0';
+ len = -1;
+ }
+ *len_ptr = len;
+
+ if (iflags & ITEM_TRANSFER) {
+ int i = ndx - cur_flist->ndx_start;
+ if (i < 0 || !S_ISREG(cur_flist->files[i]->mode)) {
+ rprintf(FERROR,
+ "received request to transfer non-regular file: %d [%s]\n",
+ ndx, who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
+ }
+
+ *iflag_ptr = iflags;
+ return ndx;
+}