/* <string> getenv false */ static int zgetenv(i_ctx_t *i_ctx_p) { os_ptr op = osp; char *str; byte *value; int len = 0; check_read_type(*op, t_string); str = ref_to_string(op, imemory, "getenv key"); if (str == 0) return_error(e_VMerror); if (gp_getenv(str, (char *)0, &len) > 0) { /* key missing */ ifree_string((byte *) str, r_size(op) + 1, "getenv key"); make_false(op); return 0; } value = ialloc_string(len, "getenv value"); if (value == 0) { ifree_string((byte *) str, r_size(op) + 1, "getenv key"); return_error(e_VMerror); } DISCARD(gp_getenv(str, (char *)value, &len)); /* can't fail */ ifree_string((byte *) str, r_size(op) + 1, "getenv key"); /* Delete the stupid C string terminator. */ value = iresize_string(value, len, len - 1, "getenv value"); /* can't fail */ push(1); make_string(op - 1, a_all | icurrent_space, len - 1, value); make_true(op); return 0; }
/* Convert a string from the current locale's character set to UTF-8. * Unfortunately, "current locale" can mean a few different things on * Windows -- we use the default ANSI code page, which does the right * thing for command-line arguments (like "-sPDFPassword=foo") and * for strings typed as input to gswin32.exe. It doesn't work for * strings typed as input to gswin32c.exe, which are normally in the * default OEM code page instead. * <string> .locale_to_utf8 <string> */ static int zlocale_to_utf8(i_ctx_t *i_ctx_p) { #define LOCALE_TO_UTF8_BUFFER_SIZE 1024 os_ptr op = osp; char *input; WCHAR wide_buffer[LOCALE_TO_UTF8_BUFFER_SIZE]; char utf8_buffer[LOCALE_TO_UTF8_BUFFER_SIZE]; int success; int code; check_read_type(*op, t_string); input = ref_to_string(op, imemory, "locale_to_utf8 input"); if (input == 0) return_error(gs_error_VMerror); success = MultiByteToWideChar(CP_ACP, 0, input, -1, wide_buffer, LOCALE_TO_UTF8_BUFFER_SIZE); ifree_string((byte *)input, r_size(op) + 1, "locale_to_utf8 input"); if (success == 0) return_error(gs_error_ioerror); success = WideCharToMultiByte(CP_UTF8, 0, wide_buffer, -1, utf8_buffer, LOCALE_TO_UTF8_BUFFER_SIZE, NULL, NULL); if (success == 0) return_error(gs_error_ioerror); code = string_to_ref(utf8_buffer, op, iimemory, "locale_to_utf8 output"); if (code < 0) return code; return 0; #undef LOCALE_TO_UTF8_BUFFER_SIZE }
/* Given a UTF-8 password string, convert it to the canonical form * defined by SASLprep (RFC 4013). This is a permissive implementation, * suitable for verifying existing passwords but not for creating new * ones -- if you want to create a new password, you'll need to add a * strict mode that returns stringprep errors to the user, and uses the * STRINGPREP_NO_UNASSIGNED flag to disallow unassigned characters. * <string> .saslprep <string> */ static int zsaslprep(i_ctx_t *i_ctx_p) { os_ptr op = osp; uint input_size = r_size(op); byte *buffer; uint buffer_size; uint output_size; Stringprep_rc err; check_read_type(*op, t_string); /* According to http://unicode.org/faq/normalization.html, converting * a UTF-8 string to normalization form KC has a worst-case expansion * factor of 11, so we allocate 11 times the length of the string plus * 1 for the NUL terminator. If somehow that's still not big enough, * stringprep will return STRINGPREP_TOO_SMALL_BUFFER; there's no * danger of corrupting memory. */ buffer_size = input_size * 11 + 1; buffer = ialloc_string(buffer_size, "saslprep result"); if (buffer == 0) return_error(e_VMerror); memcpy(buffer, op->value.bytes, input_size); buffer[input_size] = '\0'; err = stringprep((char *)buffer, buffer_size, 0, stringprep_saslprep); if (err != STRINGPREP_OK) { ifree_string(buffer, buffer_size, "saslprep result"); /* Since we're just verifying the password to an existing * document here, we don't care about "invalid input" errors * like STRINGPREP_CONTAINS_PROHIBITED. In these cases, we * ignore the error and return the original string unchanged -- * chances are it's not the right password anyway, and if it * is we shouldn't gratuitously fail to decrypt the document. * * On the other hand, errors like STRINGPREP_NFKC_FAILED are * real errors, and should be returned to the user. * * Fortunately, the stringprep error codes are sorted to make * this easy: the errors we want to ignore are the ones with * codes less than 100. */ if ((int)err < 100) return 0; return_error(e_ioerror); } output_size = strlen((char *)buffer); buffer = iresize_string(buffer, buffer_size, output_size, "saslprep result"); /* can't fail */ make_string(op, a_all | icurrent_space, output_size, buffer); return 0; }
/* Convert a string from the current locale's character set to UTF-8. * <string> .locale_to_utf8 <string> */ static int zlocale_to_utf8(i_ctx_t *i_ctx_p) { os_ptr op = osp; char *input; char *output; int code; check_read_type(*op, t_string); input = ref_to_string(op, imemory, "locale_to_utf8 input"); if (input == 0) return_error(gs_error_VMerror); output = stringprep_locale_to_utf8(input); ifree_string((byte *)input, r_size(op) + 1, "locale_to_utf8 input"); if (output == 0) { /* This function is intended to be used on strings whose * character set is unknown, so it's not an error if the * input contains invalid characters. Just return the input * string unchanged. * * Sadly, EINVAL from stringprep_locale_to_utf8 can mean * either an invalid character set conversion (which we care * about), or an incomplete input string (which we don't). * For now, we ignore EINVAL; the right solution is probably * to not use stringprep_locale_to_utf8, and just call iconv * by hand. */ if (errno == EILSEQ || errno == EINVAL) return 0; /* Other errors (like ENFILE) are real errors, which we * want to return to the user. */ return_error(gs_error_ioerror); } code = string_to_ref(output, op, iimemory, "locale_to_utf8 output"); free(output); if (code < 0) return code; return 0; }
/* <prefix|null> <access_string> .tempfile <name_string> <file> */ static int ztempfile(i_ctx_t *i_ctx_p) { os_ptr op = osp; const char *pstr; char fmode[4]; int code = parse_file_access_string(op, fmode); char prefix[gp_file_name_sizeof]; char fname[gp_file_name_sizeof]; uint fnlen; FILE *sfile; stream *s; byte *buf, *sbody; if (code < 0) return code; strcat(fmode, gp_fmode_binary_suffix); if (r_has_type(op - 1, t_null)) pstr = gp_scratch_file_name_prefix; else { uint psize; check_read_type(op[-1], t_string); psize = r_size(op - 1); if (psize >= gp_file_name_sizeof) return_error(e_rangecheck); memcpy(prefix, op[-1].value.const_bytes, psize); prefix[psize] = 0; pstr = prefix; } if (gp_file_name_is_absolute(pstr, strlen(pstr))) { if (check_file_permissions(i_ctx_p, pstr, strlen(pstr), "PermitFileWriting") < 0) { return_error(e_invalidfileaccess); } } else if (!prefix_is_simple(pstr)) { return_error(e_invalidfileaccess); } s = file_alloc_stream(imemory, "ztempfile(stream)"); if (s == 0) return_error(e_VMerror); buf = gs_alloc_bytes(imemory, file_default_buffer_size, "ztempfile(buffer)"); if (buf == 0) return_error(e_VMerror); sfile = gp_open_scratch_file(imemory, pstr, fname, fmode); if (sfile == 0) { gs_free_object(imemory, buf, "ztempfile(buffer)"); return_error(e_invalidfileaccess); } fnlen = strlen(fname); sbody = ialloc_string(fnlen, ".tempfile(fname)"); if (sbody == 0) { gs_free_object(imemory, buf, "ztempfile(buffer)"); return_error(e_VMerror); } memcpy(sbody, fname, fnlen); file_init_stream(s, sfile, fmode, buf, file_default_buffer_size); code = ssetfilename(s, (const unsigned char*) fname, fnlen); if (code < 0) { gx_io_device *iodev_dflt = iodev_default(imemory); sclose(s); iodev_dflt->procs.delete_file(iodev_dflt, fname); ifree_string(sbody, fnlen, ".tempfile(fname)"); return_error(e_VMerror); } make_string(op - 1, a_readonly | icurrent_space, fnlen, sbody); make_stream_file(op, s, fmode); return code; }
/* Free a file name that was copied to a C string. */ void free_file_name(parsed_file_name *pfn, client_name_t cname) { if ( pfn->fname != 0 ) ifree_string((byte *)pfn->fname, pfn->len, cname); }