コード例 #1
0
ファイル: rfc2822.c プロジェクト: fjaeckel/sid-milter
int
main(int argc, char **argv)
{
    int err;
    char *domain, *user;

    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s mailheader\n", argv[0]);
        exit(64);
    }

    err = rfc2822_mailbox_split(argv[1], &user, &domain);

    if (err)
    {
        printf("error %d\n", err);
    }
    else
    {
        printf("user: '******'\ndomain: '%s'\n",
               user ? unescape(user) : "null",
               domain ? unescape(domain) : "null");
    }

    return 0;
}
コード例 #2
0
ファイル: ausearch-report.c プロジェクト: pexip/os-audit
static void print_escaped(char *val)
{
	char *str;

	if (*val == '"') {
		char *term;
		val++;
		term = strchr(val, '"');
		if (term == NULL)
			return;
		*term = 0;
		printf("%s ", val);
// FIXME: working here...was trying to detect (null) and handle that differently
// The other 2 should have " around the file names.
/*	} else if (*val == '(') {
		char *term;
		val++;
		term = strchr(val, ' ');
		if (term == NULL)
			return;
		*term = 0;
		printf("%s ", val); */
	} else {
		if (val[0] == '0' && val[1] == '0')
			str = unescape(&val[2]); // Abstract name
		else
			str = unescape(val);
		printf("%s ", str ? str: "(null)");
		free(str);
	}
}
コード例 #3
0
void    cleanup_post_jail(char *unused_name, char **unused_argv)
{

    /*
     * Optionally set the file size resource limit. XXX This limits the
     * message content to somewhat less than requested, because the total
     * queue file size also includes envelope information. Unless people set
     * really low limit, the difference is going to matter only when a queue
     * file has lots of recipients.
     */
    if (var_message_limit > 0)
	set_file_limit((off_t) var_message_limit);

    /*
     * Control how unmatched extensions are propagated.
     */
    cleanup_ext_prop_mask =
	ext_prop_mask(VAR_PROP_EXTENSION, var_prop_extension);

    /*
     * Setup the filters for characters that should be rejected, and for
     * characters that should be removed.
     */
    if (*var_msg_reject_chars) {
	cleanup_reject_chars = vstring_alloc(strlen(var_msg_reject_chars));
	unescape(cleanup_reject_chars, var_msg_reject_chars);
    }
    if (*var_msg_strip_chars) {
	cleanup_strip_chars = vstring_alloc(strlen(var_msg_strip_chars));
	unescape(cleanup_strip_chars, var_msg_strip_chars);
    }
}
コード例 #4
0
ファイル: util.cpp プロジェクト: LubosD/twinkle
vector<string> split_escaped(const string &s, char c) {
	vector<string> l;
	
	string::size_type start_pos = 0;
	for (string::size_type i = 0; i < s.size(); i++) {
		if (s[i] == '\\') {
			// Skip escaped character
			if (i < s.size()) i++;
			continue;
		}
		
		if (s[i] == c) {
			l.push_back(unescape(s.substr(start_pos, i - start_pos)));
			start_pos = i + 1;
		}
	}
	
	if (start_pos < s.size()) {
		l.push_back(unescape(s.substr(start_pos, s.size() - start_pos)));
	} else if (start_pos == s.size()) {
		l.push_back("");
	}
	
	return l;
}
コード例 #5
0
string CCLocalization::getString(const string& key) {
    // get strings, may fallback to English if not found
    bool fallback = false;
    string lan = CCLocale::sharedLocale()->getLanguage();
    CCDictionary* strings = (CCDictionary*)m_lanMap.objectForKey(lan);
    if(!strings) {
        lan = CCLocale::sharedLocale()->getISOLanguage();
        strings = (CCDictionary*)m_lanMap.objectForKey(lan);
        if(!strings) {
            fallback = true;
            strings = (CCDictionary*)m_lanMap.objectForKey("en");
        }
    }

    // find string, or return empty if not found
    if(strings) {
        CCString* s = (CCString*)strings->objectForKey(key);
        if(s) {
            string ret = s->getCString();
            ret = unescape(ret);
            return ret;
        } else if(!fallback) {
            strings = (CCDictionary*)m_lanMap.objectForKey("en");
            CCString* s = (CCString*)strings->objectForKey(key);
            if(s) {
                string ret = s->getCString();
                ret = unescape(ret);
                return ret;
            }
        }
    }

    return "!" + key + "!";
}
コード例 #6
0
ファイル: dbm_gdb.c プロジェクト: matbaj/geany-plugins
/*
 * updates variables from vars list 
 */
void get_variables (GList *vars)
{
	while (vars)
	{
		gchar command[1000];
		
		variable *var = (variable*)vars->data;

		gchar *varname = var->internal->str;
		gchar *record = NULL;
		gchar *pos;

		/* path expression */
		sprintf(command, "-var-info-path-expression \"%s\"", varname);
		exec_sync_command(command, TRUE, &record);
		pos = strstr(record, "path_expr=\"") + strlen("path_expr=\"");
		*(strrchr(pos, '\"')) = '\0';
		gchar *expression = unescape(pos);
		g_string_assign(var->expression, expression);
		g_free(expression);
		g_free(record);
		
		/* children number */
		sprintf(command, "-var-info-num-children \"%s\"", varname);
		exec_sync_command(command, TRUE, &record);
		pos = strstr(record, "numchild=\"") + strlen("numchild=\"");
		*(strchr(pos, '\"')) = '\0';
		int numchild = atoi(pos);
		var->has_children = numchild > 0;
		g_free(record);

		/* value */
		sprintf(command, "-data-evaluate-expression \"%s\"", var->expression->str);
		exec_sync_command(command, TRUE, &record);
		pos = strstr(record, "value=\"");
		if (!pos)
		{
			g_free(record);
			sprintf(command, "-var-evaluate-expression \"%s\"", varname);
			exec_sync_command(command, TRUE, &record);
			pos = strstr(record, "value=\"");
		}
		pos +=  + strlen("value=\"");
		*(strrchr(pos, '\"')) = '\0';
		gchar *value = unescape(pos);
		g_string_assign(var->value, value);
		g_free(value);
		g_free(record);

		/* type */
		sprintf(command, "-var-info-type \"%s\"", varname);
		exec_sync_command(command, TRUE, &record);
		pos = strstr(record, "type=\"") + strlen("type=\"");
		*(strchr(pos, '\"')) = '\0';
		g_string_assign(var->type, pos);
		g_free(record);

		vars = vars->next;
	}
}
コード例 #7
0
static int read_tag(uint8_t *line, AVDictionary **m)
{
    uint8_t *key, *value, *p = line;

    /* find first not escaped '=' */
    while (1) {
        if (*p == '=')
            break;
        else if (*p == '\\')
            p++;

        if (*p++)
            continue;

        return 0;
    }

    if (!(key = unescape(line, p - line)))
        return AVERROR(ENOMEM);
    if (!(value = unescape(p + 1, strlen(p + 1)))) {
        av_free(key);
        return AVERROR(ENOMEM);
    }

    av_dict_set(m, key, value, AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
    return 0;
}
コード例 #8
0
std::string LLURI::hostNameAndPort() const
{
	std::string user, host, port;
	findAuthorityParts(mEscapedAuthority, user, host, port);
	return port.empty() ? unescape(host) : unescape(host + ":" + port);

}
コード例 #9
0
ファイル: ssh-wintunnel.c プロジェクト: AJH16/TortoiseGit
/* based on gitno_extract_url_parts, keep c&p copy here until https://github.com/libgit2/libgit2/pull/2492 gets merged or forever ;)*/
static int extract_url_parts(
	char **host,
	char **port,
	char **path,
	char **username,
	char **password,
	const char *url,
	const char *default_port)
{
	struct http_parser_url u = { 0 };
	const char *_host, *_port, *_path, *_userinfo;

	if (http_parser_parse_url(url, strlen(url), false, &u)) {
		giterr_set(GITERR_NET, "Malformed URL '%s'", url);
		return GIT_EINVALIDSPEC;
	}

	_host = url + u.field_data[UF_HOST].off;
	_port = url + u.field_data[UF_PORT].off;
	_path = url + u.field_data[UF_PATH].off;
	_userinfo = url + u.field_data[UF_USERINFO].off;

	if (u.field_set & (1 << UF_HOST)) {
		*host = git__substrdup(_host, u.field_data[UF_HOST].len);
		GITERR_CHECK_ALLOC(*host);
	}

	if (u.field_set & (1 << UF_PORT)) {
		*port = git__substrdup(_port, u.field_data[UF_PORT].len);
	} else if (default_port) {
		*port = git__strdup(default_port);
		GITERR_CHECK_ALLOC(*port);
	} else {
		*port = NULL;
	}

	if (u.field_set & (1 << UF_PATH)) {
		*path = git__substrdup(_path, u.field_data[UF_PATH].len);
		GITERR_CHECK_ALLOC(*path);
	} else {
		giterr_set(GITERR_NET, "invalid url, missing path");
		return GIT_EINVALIDSPEC;
	}

	if (u.field_set & (1 << UF_USERINFO)) {
		const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len);
		if (colon) {
			*username = unescape(git__substrdup(_userinfo, colon - _userinfo));
			*password = unescape(git__substrdup(colon + 1, u.field_data[UF_USERINFO].len - (colon + 1 - _userinfo)));
			GITERR_CHECK_ALLOC(*password);
		} else {
			*username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len);
		}
		GITERR_CHECK_ALLOC(*username);

	}

	return 0;
}
コード例 #10
0
//------------------------------------------------------------------------------
// FUNCTION
//
//
// DESCRIPTION
//
//  
// PARAMETERS
//
//  
// RETURN
//
//  
//------------------------------------------------------------------------------
void query_init(http_req *req)
{
	char *q, *query, *val;
	int len;
	int idx=0;
	query_var *var;
	
	req->query_tbl = (query_var *)malloc(sizeof(query_var)*NUM_QUERYVAR);
	memset(req->query_tbl, 0, sizeof(query_var)*NUM_QUERYVAR);
	
	var = req->query_tbl;
	
	query = req->query;
	len = req->query_len;
	
	if ( (!query) || (*query == '\0') ) {
		len = 0;
		return;
	}

	/* Parse into individual assignments */
	//while (strsep(&query, "&;"));
	q = query;
	var->name = q;
	var++;
	//unescape(q); /* Unescape each assignment */
	while((q = strchr(q, '&'))){
		*q = '\0';
		var->name = ++q;
		var++;
	}
#if 0
	/* Unescape each assignment */
	for (q = query; q < (query + len);) {
		unescape(q);
		for (q += strlen(q); q < (query + len) && !*q; q++);
	}
#endif
	
	var = req->query_tbl;
	while( var->name && (idx<NUM_QUERYVAR) ) 
	{
		if ((val = strchr(var->name, '=')) != 0)
		{
			*val = '\0';
			var->value = val+1;
		}
		else
			var->value = err_string;
		
		unescape(var->name);
		unescape(var->value);
		var++;
		idx++;
	}
}
コード例 #11
0
ファイル: MwUtils.c プロジェクト: UlricE/Mowitz
void MwLoadDictionary(char *fn)
{
        FILE *fp;
        char b[10000], *p, *q;

        /* convert everything after language code to upper case */
        p = strchr(fn, '_');
        if (p) {
                q = p;
                while (*q) {
                        *q = toupper(*q);
                        q++;
                }
        }

        if (p) {
                q = strchr(p, '.');
        } else {
                q = NULL;
        }

        fp = fopen(fn, "r");
        
        if (!fp && q) {
                /* try abridged file name */
                *q = '\0';
                fp = fopen(fn, "r");
        }
        if (!fp && p) {
                /* try abridged file name */
                *p = '\0';
                fp = fopen(fn, "r");
        }
        if (!fp) {
#ifdef DEBUG
                fprintf(stderr, "Can't open dictionary\n");
#endif
                return;
        }
        while (fgets(b, sizeof b, fp)) {
                if (b[0] == '#') continue;
                MwChomp(b);
                p = strchr(b, '\t');
                if (!p) continue;
                *p++ = 0;
                p += strspn(p, "\t");
                dict = MwRealloc(dict, (nw+1)*(sizeof dict[0]));
                unescape(b);
                unescape(p);
                dict[nw].key = MwStrdup(b);
                dict[nw++].xl = MwStrdup(p);
        }
        qsort(dict, nw, sizeof dict[0], compar);
}
コード例 #12
0
RegExp *strToRE( SubStr s )
{
    RegExp  *re;

    s.len -= 2; s.str += 1;
    if( s.len == 0 )
        return( RegExp_new_NullOp() );
    re = matchChar( unescape( &s ) );
    while( s.len > 0 )
        re = RegExp_new_CatOp( re, matchChar( unescape( &s ) ) );
    return( re );
}
コード例 #13
0
void deserialize( std::istream& is, tut::test_result& tr ) {
	std::string name;
	std::getline( is, name );
	if ( is.eof() ) {
		throw tut::no_more_tests();
	}

	tr._group->set_name( unescape( name ) );

	tr._testNo = -1;
	is >> tr._testNo;
	if ( tr._testNo < 0 ) {
		throw std::logic_error( "operator >> : bad test number" );
	}

	int n = -1;
	is >> n;
	switch ( n ) {
		case 0:
			tr._result = test_result::ok;
		break;
		case 1:
			tr._result = test_result::fail;
		break;
		case 2:
			tr._result = test_result::ex;
		break;
		case 3:
			tr._result = test_result::warn;
		break;
		case 4:
			tr._result = test_result::term;
		break;
		default:
			throw std::logic_error( "operator >> : bad result_type" );
	}

	is.ignore( 1 );    // space
	std::getline( is, tr._name );
	tr._name = unescape( tr._name );
	std::getline( is, tr._file );
	tr._file = unescape( tr._file );
	is.ignore( 1 );    // space
	is >> tr._line;
	is.ignore( 1 );    // space
	std::getline( is, tr._message );
	tr._message = unescape( tr._message );
	if ( !is.good() ) {
		throw std::logic_error( "malformed test result" );
	}
}
コード例 #14
0
void ReplaceList::Load()
{
	Clear();
	wchar_t *data;
	int size;
	LoadFile(path, &data, &size);
	wchar_t profile[MAX_PATH] = L"*";
	config.replace.AddProfile(profile);
	if (data)
	{
		wchar_t *pos = data;
		wchar_t *last = 0;
		int final = 0;
		while (pos)
		{
			wchar_t *end = wcschr(pos, '\n');
			if (!end)
			{
				end = wcschr(pos, 0);
				final = 1;
			}
			*end = 0;
			if (!last)
			{
				if (!wcsnicmp(pos, L"Profile:", 8))
				{
					pos += 8;
					while (pos[0] == ' ') pos++;
					if (end-pos < MAX_PATH)
						wcscpy(profile, pos);
				}
				else
					if (pos[0])
						last = pos;
			}
			else
			{
				if (pos-last-1 > 1000) last[1000] = 0;
				if (end-pos > 1000) pos[1000] = 0;
				wchar_t *old = unescape(last);
				wchar_t *replace = unescape(pos);
				ReplaceList::AddString(old, replace, profile);
				free(replace);
				free(old);
				last = 0;
			}

			if (final) break;
			pos = end+1;
		}
コード例 #15
0
ファイル: xml_tokenizer.cpp プロジェクト: Zenol/clanlib-2.4
inline void CL_XMLTokenizer_Generic::unescape(CL_StringRef &unescaped, const CL_StringRef &text)
{
	static const CL_StringRef quot("&quot;");
	static const CL_StringRef apos("&apos;");
	static const CL_StringRef lt("&lt;");
	static const CL_StringRef gt("&gt;");
	static const CL_StringRef amp("&amp;");

	unescaped = string_allocator.alloc(text);
	unescape(unescaped, quot, '"');
	unescape(unescaped, apos, '\'');
	unescape(unescaped, lt, '<');
	unescape(unescaped, gt, '>');
	unescape(unescaped, amp, '&');
}
コード例 #16
0
ファイル: xml_tokenizer.cpp プロジェクト: Cassie90/ClanLib
inline void XMLTokenizer_Impl::unescape(std::string &unescaped, const std::string &text)
{
	static const std::string quot("&quot;");
	static const std::string apos("&apos;");
	static const std::string lt("&lt;");
	static const std::string gt("&gt;");
	static const std::string amp("&amp;");

	unescaped = text;
	unescape(unescaped, quot, '"');
	unescape(unescaped, apos, '\'');
	unescape(unescaped, lt, '<');
	unescape(unescaped, gt, '>');
	unescape(unescaped, amp, '&');
}
コード例 #17
0
ファイル: lex.c プロジェクト: mikekap/wine
static int parse_string_literal(parser_ctx_t *ctx, const WCHAR **ret, WCHAR endch)
{
    const WCHAR *ptr = ++ctx->ptr;
    WCHAR *wstr;
    int len;

    while(ctx->ptr < ctx->end && *ctx->ptr != endch) {
        if(*ctx->ptr++ == '\\')
            ctx->ptr++;
    }

    if(ctx->ptr == ctx->end)
        return lex_error(ctx, JS_E_UNTERMINATED_STRING);

    len = ctx->ptr-ptr;

    *ret = wstr = parser_alloc(ctx, (len+1)*sizeof(WCHAR));
    memcpy(wstr, ptr, (len+1)*sizeof(WCHAR));
    wstr[len] = 0;

    ctx->ptr++;

    if(!unescape(wstr)) {
        WARN("unescape failed\n");
        return lex_error(ctx, E_FAIL);
    }

    return tStringLiteral;
}
コード例 #18
0
ファイル: expand.cpp プロジェクト: frogshead/fish-shell
static wchar_t *expand_unescape(parser_t &parser, const wchar_t * in, int escape_special)
{
    wchar_t *res = unescape(in, escape_special);
    if (!res)
        parser.error(SYNTAX_ERROR, -1, L"Unexpected end of string");
    return res;
}
コード例 #19
0
void test_string_unescape (char str_source[])
{
  char str_copy[100];
  printf("+ %s\n", str_source);
  unescape(str_copy, str_source);
  printf("\n+%s\n", str_copy);
}
コード例 #20
0
ファイル: macros.c プロジェクト: serjepatoff/vifm
char *
ma_expand_single(const char command[])
{
	char *const res = expand_macros_i(command, NULL, NULL, 0, &filter_single);
	unescape(res, 0);
	return res;
}
コード例 #21
0
std::string MessageEngine::get(const std::string& key, const std::string& s) {
	std::string message = messages[key];
	if (message == "") message = key;
	size_t index = message.find("%s");
	if (index != std::string::npos) message = message.replace(index, 2, s);
	return unescape(message);
}
コード例 #22
0
/* Unescapes a string. 
 *
 * Returns: Pointer to start of unescaped string. If an error occurs, NULL is
 * returned (an error consists of an escaped value being truncated).
 */
char *unescape_into(char *dest, const char *src, size_t len) {
	char *start, *write;
	size_t i;
	
	assert(dest);
	assert(src);
	
	write = start = dest;
	
	for (i = 0; i < len; i++, write++) {
		if (src[i] == ESCAPE_CHARACTER) {
			/*** Check that the characters are legal, and that the value has
			 * not been truncated. 
			 ***/
			if ((i + 2 < len) && isxdigit(src[i+1]) && isxdigit(src[i+2])) {
				*write = unescape(src[i+1], src[i+2]);
				i += 2;
			} 
			else {
				return NULL;
			}
		} 
		else {
			*write = src[i];
		}
	}

	return start;
}
コード例 #23
0
ファイル: json.c プロジェクト: AmesianX/wine
/* ECMA-262 5.1 Edition    15.12.1.1 */
static HRESULT parse_json_string(json_parse_ctx_t *ctx, WCHAR **r)
{
    const WCHAR *ptr = ++ctx->ptr;
    size_t len;
    WCHAR *buf;

    while(*ctx->ptr && *ctx->ptr != '"') {
        if(*ctx->ptr++ == '\\')
            ctx->ptr++;
    }
    if(!*ctx->ptr) {
        FIXME("unterminated string\n");
        return E_FAIL;
    }

    len = ctx->ptr-ptr;
    buf = heap_alloc((len+1)*sizeof(WCHAR));
    if(!buf)
        return E_OUTOFMEMORY;
    if(len)
        memcpy(buf, ptr, len*sizeof(WCHAR));
    buf[len] = 0;

    if(!unescape(buf)) {
        FIXME("unescape failed\n");
        heap_free(buf);
        return E_FAIL;
    }

    ctx->ptr++;
    *r = buf;
    return S_OK;
}
コード例 #24
0
static Range *getRange( SubStr *s )
{
    uchar lb = unescape( s ), ub;

    if( s->len < 2 || *s->str != '-' ) {
        ub = lb;
    } else {
        s->len--; s->str++;
        ub = unescape( s );
        if( ub < lb ) {
            uchar tmp;
            tmp = lb; lb = ub; ub = tmp;
        }
    }
    return( Range_new( lb, ub + 1 ) );
}
コード例 #25
0
ファイル: cgi.c プロジェクト: themiron/asuswrt-merlin
void
init_cgi(char *query)
{
	int len, nel;
	char *q, *name, *value;

	/* Clear variables */
	if (!query) {
		hdestroy_r(&htab);
		return;
	}

	/* Parse into individual assignments */
	q = query;
	len = strlen(query);
	nel = 1;
	while (strsep(&q, "&;"))
		nel++;
	hcreate_r(nel, &htab);

	for (q = query; q < (query + len);) {
		/* Unescape each assignment */
		unescape(name = value = q);

		/* Skip to next assignment */
		for (q += strlen(q); q < (query + len) && !*q; q++);

		/* Assign variable */
		name = strsep(&value, "=");
		if (value) {
//			printf("set_cgi: name=%s, value=%s.\n", name , value);	// N12 test
			set_cgi(name, value);
		}
	}
}
コード例 #26
0
ファイル: ausearch-report.c プロジェクト: pexip/os-audit
static void print_key(char *val)
{
	int count=0;
	char *saved=NULL;
	if (*val == '"') {
		char *term;
		val++;
		term = strchr(val, '"');
		if (term == NULL)
			return;
		*term = 0;
		printf("%s ", val);
	} else {
		char *keyptr = unescape(val);
		char *kptr = strtok_r(keyptr, key_sep, &saved);
		if (kptr == NULL) {
			printf("%s", keyptr);
		}
		while (kptr) {
			if (count == 0) {
				printf("%s", kptr);
				count++;
			} else
				printf(" key=%s", kptr);
			kptr = strtok_r(NULL, key_sep, &saved);
		}
		printf(" ");
		free(keyptr);
	}
}
コード例 #27
0
ファイル: cgi.c プロジェクト: themiron/asuswrt-merlin
void webcgi_init(char *query)
{
       int nel;
       char *q, *end, *name, *value;
 
       if (htab.table) hdestroy_r(&htab);
       if (query == NULL) return;
 
//    cprintf("query = %s\n", query);
       
       end = query + strlen(query);
       q = query;
       nel = 1;
       while (strsep(&q, "&;")) {
               nel++;
       }
       hcreate_r(nel, &htab);
 
       for (q = query; q < end; ) {
               value = q;
               q += strlen(q) + 1;
 
               unescape(value);
               name = strsep(&value, "=");
               if (value) webcgi_set(name, value);
       }
}
コード例 #28
0
void CGIDemangler::analyseVar(char *var)
{
    char *eq;

    // name / value
    if ((eq=strchr(var, '=')))
    {
        *eq = '\0';
        unescape(var);
        unescape(eq+1);
        stdString name, value;
        name = var;
        value = eq+1;
        add(name, value);
    }
}
コード例 #29
0
ファイル: inifile.cpp プロジェクト: Apocalypsing/MultiMC5
bool INIFile::loadFile( QByteArray file )
{
	QTextStream in(file);
	in.setCodec("UTF-8");
	
	QStringList lines = in.readAll().split('\n');
	for (int i = 0; i < lines.count(); i++)
	{
		QString & lineRaw = lines[i];
		// Ignore comments.
		QString line = lineRaw.left(lineRaw.indexOf('#')).trimmed();
		
		int eqPos = line.indexOf('=');
		if(eqPos == -1)
			continue;
		QString key = line.left(eqPos).trimmed();
		QString valueStr = line.right(line.length() - eqPos - 1).trimmed();
		
		valueStr = unescape(valueStr);
		
		QVariant value(valueStr);
		this->operator [](key) = value;
	}
	
	return true;
}
コード例 #30
0
ファイル: socket.cpp プロジェクト: isaach1000/emissary
void Socket::read(async::ErrorCallback<const std::string&> callback)
{
    auto bufferPtr = std::make_shared<boost::asio::streambuf>();
    boost::asio::async_read_until(
        _socket,
        *bufferPtr,
        DELIM,
        [bufferPtr, callback](const boost::system::error_code&  ec,
                              std::size_t                       numBytes) {
            async::Error err;

            if (ec) {
                err = async::Error("Read error", ec);
                callback(err, "");
                return;
            }

            std::istream is(bufferPtr.get());
            const auto msgSize = numBytes - DELIM.size();
            std::string str(msgSize, '\0');
            is.read(&str[0], msgSize);
            unescape(str);

            if (!is) {
                callback(async::Error("Read error"), "");
                return;
            }

            callback(async::Error(), str);
        });
}