Packages
sippet
0.5.0
1.0.16
1.0.15
1.0.14
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
An Elixir Session Initiation Protocol (SIP) stack.
Current section
Files
Jump to
Current section
Files
c_src/parser.cc
// Copyright (c) 2017 The Sippet Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Parts of this code was taken from Chromium sources:
// Copyright (c) 2011-2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <erl_nif.h>
#include <cstring>
#include <unordered_map>
#include <iostream>
#include "prtime.h"
#include "string_piece.h"
#include "tokenizer.h"
#include "string_tokenizer.h"
#include "utils.h"
namespace {
typedef ERL_NIF_TERM (*ParseFunction)(ErlNifEnv* env,
std::string::const_iterator,
std::string::const_iterator);
std::unordered_map<char, ERL_NIF_TERM> g_aliases;
std::unordered_map<ERL_NIF_TERM, ParseFunction> g_parsers;
bool MakeExistingAtom(ErlNifEnv* env, StringPiece atom_name,
ERL_NIF_TERM *atom) {
return enif_make_existing_atom_len(env, atom_name.data(), atom_name.size(),
atom, ERL_NIF_LATIN1);
}
bool MakeLowerCaseExistingAtom(ErlNifEnv* env, StringPiece name,
ERL_NIF_TERM *atom) {
std::string atom_name(name.as_string());
for (auto& c : atom_name) {
if (c == '-')
c = '_';
else
c = ToLowerASCII(c);
}
return MakeExistingAtom(env, atom_name, atom);
}
ERL_NIF_TERM MakeString(ErlNifEnv* env, StringPiece s) {
ErlNifBinary bin;
if (!enif_alloc_binary(s.size(), &bin))
return enif_make_atom(env, "no_memory");
memcpy(bin.data, s.data(), s.size());
return enif_make_binary(env, &bin);
}
ERL_NIF_TERM MakeLowerCaseString(ErlNifEnv* env, StringPiece s) {
std::string lowercase(s.as_string());
for (auto& c : lowercase)
c = ToLowerASCII(c);
return MakeString(env, lowercase);
}
ERL_NIF_TERM MakeLowerCaseExistingAtomOrString(ErlNifEnv* env, StringPiece name) {
ERL_NIF_TERM result;
if (!MakeLowerCaseExistingAtom(env, name, &result))
result = MakeString(env, name);
return result;
}
bool IsStatusLine(
std::string::const_iterator line_begin,
std::string::const_iterator line_end) {
return ((line_end - line_begin > 4)
&& LowerCaseEqualsASCII(
StringPiece(line_begin, line_begin + 4), "sip/"));
}
std::string::const_iterator FindLineEnd(
std::string::const_iterator begin,
std::string::const_iterator end) {
size_t i = StringPiece(begin, end).find_first_of("\r\n");
if (i == StringPiece::npos)
return end;
return begin + i;
}
ERL_NIF_TERM ParseVersion(ErlNifEnv* env,
std::string::const_iterator line_begin,
std::string::const_iterator line_end) {
Tokenizer tok(line_begin, line_end);
if ((line_end - line_begin < 3) ||
!LowerCaseEqualsASCII(
StringPiece(line_begin, line_begin + 3), "sip")) {
return enif_make_atom(env, "missing_version_spec");
}
tok.Skip(3);
tok.Skip(SIP_LWS);
if (tok.EndOfInput()
|| *tok.current() != '/') {
return enif_make_atom(env, "missing_version");
}
tok.Skip();
std::string::const_iterator major_start = tok.Skip(SIP_LWS);
tok.SkipTo('.');
tok.Skip();
std::string::const_iterator minor_start = tok.Skip(SIP_LWS);
if (tok.EndOfInput()) {
return enif_make_atom(env, "malformed_version");
}
if (!isdigit(*major_start) || !isdigit(*minor_start)) {
return enif_make_atom(env, "malformed_version_number");
}
int major = *major_start - '0';
int minor = *minor_start - '0';
return enif_make_tuple2(env, enif_make_int(env, major),
enif_make_int(env, minor));
}
ERL_NIF_TERM ParseStatusLine(ErlNifEnv* env,
std::string::const_iterator line_begin,
std::string::const_iterator line_end) {
// Extract the version number
ERL_NIF_TERM version = ParseVersion(env, line_begin, line_end);
if (enif_is_atom(env, version)) {
return version;
}
std::string::const_iterator p = std::find(line_begin, line_end, ' ');
if (p == line_end) {
return enif_make_atom(env, "missing_status_code");
}
// Skip whitespace.
while (*p == ' ')
++p;
std::string::const_iterator code = p;
while (*p >= '0' && *p <= '9')
++p;
if (p == code) {
return enif_make_atom(env, "empty_status_code");
}
int status_code;
if (!StringToInt(StringPiece(code, p), &status_code)) {
return enif_make_atom(env, "invalid_status_code");
}
// Skip whitespace.
while (*p == ' ')
++p;
// Trim trailing whitespace.
while (line_end > p && line_end[-1] == ' ')
--line_end;
std::string reason_phrase;
if (p < line_end) {
reason_phrase.assign(p, line_end);
}
ERL_NIF_TERM status_line = enif_make_new_map(env);
enif_make_map_put(env, status_line, enif_make_atom(env, "version"), version,
&status_line);
enif_make_map_put(env, status_line, enif_make_atom(env, "status_code"),
enif_make_int(env, status_code), &status_line);
enif_make_map_put(env, status_line, enif_make_atom(env, "reason_phrase"),
MakeString(env, reason_phrase), &status_line);
return status_line;
}
ERL_NIF_TERM ParseRequestLine(ErlNifEnv* env,
std::string::const_iterator line_begin,
std::string::const_iterator line_end) {
// Skip any leading whitespace.
while (line_begin != line_end &&
(*line_begin == ' ' || *line_begin == '\t' ||
*line_begin == '\r' || *line_begin == '\n'))
++line_begin;
std::string::const_iterator method_start = line_begin;
std::string::const_iterator p = std::find(line_begin, line_end, ' ');
if (p == line_end) {
return enif_make_atom(env, "missing_method");
}
std::string method(method_start, p);
// Skip whitespace.
while (*p == ' ')
++p;
std::string::const_iterator uri_start = p;
p = std::find(p, line_end, ' ');
if (p == line_end) {
return enif_make_atom(env, "missing_uri");
}
std::string uri(uri_start, p);
// Skip whitespace.
while (*p == ' ')
++p;
// Extract the version number
ERL_NIF_TERM version = ParseVersion(env, p, line_end);
if (enif_is_atom(env, version)) {
return version;
}
ERL_NIF_TERM request_line = enif_make_new_map(env);
enif_make_map_put(env, request_line, enif_make_atom(env, "method"),
MakeLowerCaseExistingAtomOrString(env, method), &request_line);
enif_make_map_put(env, request_line, enif_make_atom(env, "request_uri"),
MakeString(env, uri), &request_line);
enif_make_map_put(env, request_line, enif_make_atom(env, "version"), version,
&request_line);
return request_line;
}
ERL_NIF_TERM ParseToken(ErlNifEnv* env, Tokenizer* tok) {
std::string::const_iterator token_start = tok->Skip(SIP_LWS);
if (tok->EndOfInput()) {
return enif_make_atom(env, "empty_value");
}
return MakeString(env, StringPiece(token_start,
tok->SkipNotIn(SIP_LWS ";")));
}
ERL_NIF_TERM ParseTypeSubtype(ErlNifEnv* env, Tokenizer* tok) {
std::string::const_iterator type_start = tok->Skip(SIP_LWS);
if (tok->EndOfInput()) {
// empty header is OK
return enif_make_tuple(env, 0);
}
StringPiece type(type_start, tok->SkipNotIn(SIP_LWS "/"));
if (!IsToken(type)) {
return enif_make_atom(env, "invalid_token");
}
tok->SkipTo('/');
tok->Skip();
std::string::const_iterator subtype_start = tok->Skip(SIP_LWS);
if (tok->EndOfInput()) {
return enif_make_atom(env, "missing_subtype");
}
StringPiece subtype(subtype_start, tok->SkipNotIn(SIP_LWS ";"));
if (!IsToken(subtype)) {
return enif_make_atom(env, "invalid_token");
}
return enif_make_tuple2(env, MakeLowerCaseString(env, type),
MakeLowerCaseString(env, subtype));
}
ERL_NIF_TERM ParseParameters(ErlNifEnv* env, Tokenizer* tok) {
// TODO(balena): accept generic param such as ";token"
ERL_NIF_TERM result = enif_make_new_map(env);
if (tok->EndOfInput())
return result;
tok->SkipTo(';');
tok->Skip();
GenericParametersIterator it(tok->current(), tok->end());
while (it.GetNext()) {
enif_make_map_put(env, result, MakeLowerCaseString(env, it.name()),
MakeString(env, it.value()), &result);
}
return result;
}
ERL_NIF_TERM ParseAuthScheme(ErlNifEnv* env, Tokenizer* tok) {
std::string::const_iterator scheme_start = tok->Skip(SIP_LWS);
if (tok->EndOfInput())
return enif_make_atom(env, "missing_auth_scheme");
StringPiece scheme(scheme_start, tok->SkipNotIn(SIP_LWS));
return MakeString(env, scheme);
}
ERL_NIF_TERM ParseAuthParams(ErlNifEnv* env, Tokenizer* tok) {
ERL_NIF_TERM result = enif_make_new_map(env);
NameValuePairsIterator it(tok->current(), tok->end(), ',');
while (it.GetNext()) {
enif_make_map_put(env, result, MakeString(env, it.name()),
MakeString(env, it.raw_value()), &result);
}
return result;
}
ERL_NIF_TERM ParseComment(ErlNifEnv* env, Tokenizer* tok) {
tok->SkipTo('(');
if (tok->EndOfInput())
return enif_make_atom(env, "invalid_comment");
std::string::const_iterator comment_start = tok->Skip();
std::string::const_iterator comment_end = tok->end();
int lparen = 1;
while (!tok->EndOfInput()) {
if (*tok->current() == ')') {
if (--lparen == 0) {
comment_end = tok->current();
tok->Skip();
break;
}
} else if (*tok->current() == '(') {
lparen++;
}
tok->Skip();
}
if (comment_end == tok->end())
return enif_make_atom(env, "invalid_comment");
TrimLWS(&comment_start, &comment_end);
StringPiece comment(comment_start, comment_end);
return MakeString(env, comment);
}
ERL_NIF_TERM ParseUri(ErlNifEnv* env, Tokenizer* tok) {
tok->SkipTo('<');
if (tok->EndOfInput())
return enif_make_atom(env, "invalid_uri");
std::string::const_iterator uri_start = tok->Skip();
std::string::const_iterator uri_end = tok->SkipTo('>');
if (tok->EndOfInput())
return enif_make_atom(env, "unclosed_laquot");
tok->Skip();
StringPiece uri(uri_start, uri_end);
return MakeString(env, uri);
}
ERL_NIF_TERM ParseContact(ErlNifEnv* env, Tokenizer* tok) {
std::string::const_iterator display_name_start, display_name_end;
StringPiece address;
tok->Skip(SIP_LWS);
if (IsQuote(*tok->current())) {
// contact-param = quoted-string LAQUOT addr-spec RAQUOT
display_name_start = tok->current();
tok->Skip();
for (; !tok->EndOfInput(); tok->Skip()) {
if (*tok->current() == '\\') {
tok->Skip();
continue;
}
if (IsQuote(*tok->current()))
break;
}
if (tok->EndOfInput())
return enif_make_atom(env, "unclosed_qstring");
display_name_end = tok->Skip();
tok->SkipTo('<');
if (tok->EndOfInput())
return enif_make_atom(env, "missing_address");
std::string::const_iterator address_start = tok->Skip();
tok->SkipTo('>');
if (tok->EndOfInput())
return enif_make_atom(env, "unclosed_laquot");
address = StringPiece(address_start, tok->current());
} else {
Tokenizer laquot(tok->current(), tok->end());
laquot.SkipTo('<');
if (!laquot.EndOfInput()) {
// contact-param = *(token LWS) LAQUOT addr-spec RAQUOT
display_name_start = tok->current();
display_name_end = laquot.current();
TrimLWS(&display_name_start, &display_name_end);
std::string::const_iterator address_start = laquot.Skip();
laquot.SkipTo('>');
if (laquot.EndOfInput())
return enif_make_atom(env, "unclosed_laquot");
address = StringPiece(address_start, laquot.current());
tok->set_current(laquot.Skip());
} else if (IsToken(tok->current(), tok->current() + 1)) {
display_name_start = display_name_end = tok->end();
std::string::const_iterator address_start = tok->current();
address = StringPiece(address_start, tok->SkipNotIn(SIP_LWS));
} else {
return enif_make_atom(env, "invalid_char_found");
}
}
std::string display_name(Unquote(display_name_start, display_name_end));
return enif_make_tuple2(env, MakeString(env, display_name),
MakeString(env, address));
}
bool ParseStar(ErlNifEnv* env, Tokenizer* tok, ERL_NIF_TERM* term) {
Tokenizer star(tok->current(), tok->end());
star.Skip(SIP_LWS);
if (star.EndOfInput())
return false;
if (*star.current() != '*')
return false;
*term = MakeString(env, "*");
return true;
}
ERL_NIF_TERM ParseWarning(ErlNifEnv* env, Tokenizer* tok) {
std::string::const_iterator code_start = tok->Skip(SIP_LWS);
if (tok->EndOfInput())
return enif_make_atom(env, "empty_input");
StringPiece code_string(code_start, tok->SkipNotIn(SIP_LWS));
int code = 0;
if (!StringToInt(code_string, &code)
|| code < 100 || code > 999)
return enif_make_atom(env, "invalid_code");
std::string::const_iterator agent_start = tok->Skip(SIP_LWS);
if (tok->EndOfInput())
return enif_make_atom(env, "empty_warn_agent");
StringPiece agent(agent_start, tok->SkipNotIn(SIP_LWS));
tok->Skip(SIP_LWS);
if (tok->EndOfInput())
return enif_make_atom(env, "missing_warn_text");
if (*tok->current() != '"')
return enif_make_atom(env, "invalid_warn_text");
std::string::const_iterator text_start = tok->current();
tok->Skip();
for (; !tok->EndOfInput(); tok->Skip()) {
if (*tok->current() == '\\') {
tok->Skip();
continue;
}
if (*tok->current() == '"')
break;
}
if (tok->EndOfInput())
return enif_make_atom(env, "unclosed_qstring");
std::string text(text_start, tok->Skip());
text = Unquote(text.begin(), text.end());
return enif_make_tuple3(env, enif_make_int(env, code),
MakeString(env, agent), MakeString(env, text));
}
ERL_NIF_TERM ParseVia(ErlNifEnv* env, Tokenizer* tok) {
std::string::const_iterator version_start = tok->Skip(SIP_LWS);
if ((tok->end() - tok->current() < 3)
|| !LowerCaseEqualsASCII(
StringPiece(tok->current(), tok->current() + 3), "sip"))
return enif_make_atom(env, "unknown_version");
tok->SkipTo('/');
tok->Skip();
ERL_NIF_TERM version = ParseVersion(env, version_start, tok->SkipTo('/'));
if (enif_is_atom(env, version))
return version;
tok->Skip();
std::string::const_iterator protocol_start = tok->Skip(SIP_LWS);
if (tok->EndOfInput())
return enif_make_atom(env, "missing_sent_protocol");
std::string protocol(protocol_start, tok->SkipNotIn(SIP_LWS));
protocol = ToLowerASCII(protocol);
std::string::const_iterator sentby_start = tok->Skip(SIP_LWS);
if (tok->EndOfInput())
return enif_make_atom(env, "missing_sentby");
std::string::const_iterator sentby_end = tok->SkipTo(';');
TrimLWS(&sentby_start, &sentby_end);
StringPiece sentby_string(sentby_start, sentby_end);
if (sentby_string.empty())
return enif_make_atom(env, "missing_sentby");
std::string host;
int port;
if (!ParseHostAndPort(sentby_string.as_string(), &host, &port))
return enif_make_atom(env, "invalid_sentby");
if (port == -1) {
if (protocol == "udp" || protocol == "tcp")
port = 5060;
else if (protocol == "tls")
port = 5061;
else
port = 0;
}
if (host[0] == '[') // remove brackets from IPv6 addresses
host = host.substr(1, host.size()-2);
return enif_make_tuple3(env, version,
MakeLowerCaseExistingAtomOrString(env, protocol),
enif_make_tuple2(env, MakeString(env, host), enif_make_int(env, port)));
}
ERL_NIF_TERM ParseSingleToken(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
Tokenizer tok(values_begin, values_end);
return ParseToken(env, &tok);
}
ERL_NIF_TERM ParseSingleTokenParams(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
Tokenizer tok(values_begin, values_end);
ERL_NIF_TERM value = ParseToken(env, &tok);
if (enif_is_atom(env, value))
return value;
ERL_NIF_TERM parameters = ParseParameters(env, &tok);
if (enif_is_atom(env, parameters))
return parameters;
return enif_make_tuple2(env, value, parameters);
}
ERL_NIF_TERM ParseMultipleTokens(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
ERL_NIF_TERM result = enif_make_list(env, 0);
ValuesIterator it(values_begin, values_end, ',');
while (it.GetNext()) {
ERL_NIF_TERM token = ParseSingleToken(env, it.value_begin(),
it.value_end());
if (enif_is_atom(env, token))
return token;
result = enif_make_list_cell(env, token, result);
}
enif_make_reverse_list(env, result, &result);
return result;
}
ERL_NIF_TERM ParseMultipleTokenParams(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
ERL_NIF_TERM result = enif_make_list(env, 0);
ValuesIterator it(values_begin, values_end, ',');
while (it.GetNext()) {
ERL_NIF_TERM value = ParseSingleTokenParams(env, it.value_begin(),
it.value_end());
if (enif_is_atom(env, value))
return value;
result = enif_make_list_cell(env, value, result);
}
enif_make_reverse_list(env, result, &result);
return result;
}
ERL_NIF_TERM ParseSingleTypeSubtypeParams(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
Tokenizer tok(values_begin, values_end);
ERL_NIF_TERM value = ParseTypeSubtype(env, &tok);
if (enif_is_atom(env, value))
return value;
ERL_NIF_TERM parameters = ParseParameters(env, &tok);
if (enif_is_atom(env, parameters))
return parameters;
return enif_make_tuple2(env, value, parameters);
}
ERL_NIF_TERM ParseMultipleTypeSubtypeParams(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
ERL_NIF_TERM result = enif_make_list(env, 0);
ValuesIterator it(values_begin, values_end, ',');
while (it.GetNext()) {
ERL_NIF_TERM value = ParseSingleTypeSubtypeParams(env, it.value_begin(),
it.value_end());
if (enif_is_atom(env, value))
return value;
result = enif_make_list_cell(env, value, result);
}
enif_make_reverse_list(env, result, &result);
return result;
}
ERL_NIF_TERM ParseMultipleUriParams(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
ERL_NIF_TERM result = enif_make_list(env, 0);
ValuesIterator it(values_begin, values_end, ',');
while (it.GetNext()) {
Tokenizer tok(it.value_begin(), it.value_end());
ERL_NIF_TERM uri = ParseUri(env, &tok);
if (enif_is_atom(env, uri))
return uri;
ERL_NIF_TERM parameters = ParseParameters(env, &tok);
if (enif_is_atom(env, parameters))
return parameters;
result = enif_make_list_cell(env,
enif_make_tuple2(env, uri, parameters), result);
}
enif_make_reverse_list(env, result, &result);
return result;
}
ERL_NIF_TERM ParseSingleInteger(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
Tokenizer tok(values_begin, values_end);
std::string::const_iterator token_start = tok.Skip(SIP_LWS);
std::string digits(token_start, tok.SkipNotIn(SIP_LWS));
int i = 0;
if (!StringToInt(digits, &i))
return enif_make_atom(env, "invalid_digits");
return enif_make_int(env, i);
}
ERL_NIF_TERM ParseOnlyAuthParams(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
Tokenizer tok(values_begin, values_end);
return ParseAuthParams(env, &tok);
}
ERL_NIF_TERM ParseSchemeAndAuthParams(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
Tokenizer tok(values_begin, values_end);
ERL_NIF_TERM scheme = ParseAuthScheme(env, &tok);
if (enif_is_atom(env, scheme))
return scheme;
ERL_NIF_TERM parameters = ParseAuthParams(env, &tok);
if (enif_is_atom(env, parameters))
return parameters;
return enif_make_list1(env,
enif_make_tuple2(env, scheme, parameters));
}
ERL_NIF_TERM ParseSingleContactParams(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
Tokenizer tok(values_begin, values_end);
ERL_NIF_TERM contact = ParseContact(env, &tok);
if (enif_is_atom(env, contact))
return contact;
ERL_NIF_TERM parameters = ParseParameters(env, &tok);
if (enif_is_atom(env, parameters))
return parameters;
int arity;
const ERL_NIF_TERM *name_and_address;
enif_get_tuple(env, contact, &arity, &name_and_address);
return enif_make_tuple3(env, name_and_address[0], name_and_address[1],
parameters);
}
ERL_NIF_TERM ParseMultipleContactParams(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
ERL_NIF_TERM result = enif_make_list(env, 0);
ValuesIterator it(values_begin, values_end, ',');
while (it.GetNext()) {
ERL_NIF_TERM value = ParseSingleContactParams(env, it.value_begin(),
it.value_end());
if (enif_is_atom(env, value))
return value;
result = enif_make_list_cell(env, value, result);
}
enif_make_reverse_list(env, result, &result);
return result;
}
ERL_NIF_TERM ParseStarOrMultipleContactParams(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
Tokenizer tok(values_begin, values_end);
ERL_NIF_TERM star;
if (ParseStar(env, &tok, &star)) {
return star;
} else {
return ParseMultipleContactParams(env, values_begin, values_end);
}
}
ERL_NIF_TERM ParseTrimmedUtf8(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
TrimLWS(&values_begin, &values_end);
return MakeString(env, StringPiece(values_begin, values_end));
}
ERL_NIF_TERM ParseCseq(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
Tokenizer tok(values_begin, values_end);
std::string::const_iterator integer_start = tok.Skip(SIP_LWS);
if (tok.EndOfInput())
return enif_make_atom(env, "missing_sequence");
std::string integer_string(integer_start, tok.SkipNotIn(SIP_LWS));
int sequence = 0;
if (!StringToInt(integer_string, &sequence))
return enif_make_atom(env, "invalid_sequence");
std::string::const_iterator method_start = tok.Skip(SIP_LWS);
if (tok.EndOfInput())
return enif_make_atom(env, "missing_method");
StringPiece method_name(method_start, tok.SkipNotIn(SIP_LWS));
return enif_make_tuple2(env, enif_make_int(env, sequence),
MakeLowerCaseExistingAtomOrString(env, method_name));
}
ERL_NIF_TERM ParseDate(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
TrimLWS(&values_begin, &values_end);
if (values_begin == values_end)
return enif_make_atom(env, "empty_date");
PRExplodedTime result_time;
std::string time_string(StringPiece(values_begin, values_end).as_string());
PRStatus status = PR_ParseTimeStringToExplodedTime(time_string.c_str(),
PR_TRUE, &result_time);
if (PR_SUCCESS != status)
return enif_make_atom(env, "invalid_date");
PR_NormalizeTime(&result_time, &PR_GMTParameters);
ERL_NIF_TERM result = enif_make_tuple3(env,
enif_make_tuple3(env,
enif_make_int(env, result_time.tm_year),
enif_make_int(env, result_time.tm_month + 1),
enif_make_int(env, result_time.tm_mday)),
enif_make_tuple3(env,
enif_make_int(env, result_time.tm_hour),
enif_make_int(env, result_time.tm_min),
enif_make_int(env, result_time.tm_sec)),
enif_make_tuple2(env,
enif_make_int(env, result_time.tm_usec),
enif_make_int(env, result_time.tm_usec == 0 ? 0 : 5)));
return result;
}
ERL_NIF_TERM ParseTimestamp(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
Tokenizer tok(values_begin, values_end);
std::string::const_iterator timestamp_start = tok.Skip(SIP_LWS);
if (tok.EndOfInput())
return enif_make_atom(env, "missing_timestamp");
StringPiece timestamp_string(timestamp_start, tok.SkipNotIn(SIP_LWS));
double timestamp = .0;
if (!StringToDouble(timestamp_string, ×tamp))
return enif_make_atom(env, "invalid_timestamp");
// delay is optional
double delay = .0;
std::string::const_iterator delay_start = tok.Skip(SIP_LWS);
if (!tok.EndOfInput()) {
StringPiece delay_string(delay_start, tok.SkipNotIn(SIP_LWS));
StringToDouble(delay_string, &delay);
// ignore errors parsing the optional delay
}
return enif_make_tuple2(env, enif_make_double(env, timestamp),
enif_make_double(env, delay));
}
ERL_NIF_TERM ParseMimeVersion(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
Tokenizer tok(values_begin, values_end);
std::string::const_iterator major_start = tok.Skip(SIP_LWS);
if (tok.EndOfInput())
return enif_make_atom(env, "missing_major");
StringPiece major_string(major_start, tok.SkipTo('.'));
int major = 0;
if (major_string.empty()
|| !StringToInt(major_string, &major))
return enif_make_atom(env, "missing_or_invalid_major");
tok.Skip();
std::string::const_iterator minor_start = tok.Skip(SIP_LWS);
StringPiece minor_string(minor_start, tok.end());
int minor = 0;
if (minor_string.empty()
|| !StringToInt(minor_string, &minor))
return enif_make_atom(env, "invalid_minor");
return enif_make_tuple2(env, enif_make_int(env, major),
enif_make_int(env, minor));
}
ERL_NIF_TERM ParseRetryAfter(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
Tokenizer tok(values_begin, values_end);
std::string::const_iterator delta_start = tok.Skip(SIP_LWS);
if (tok.EndOfInput())
return enif_make_atom(env, "missing_delta_secs");
StringPiece delta_string(delta_start, tok.SkipNotIn(SIP_LWS "(;"));
int delta_seconds = 0;
if (delta_string.empty()
|| !StringToInt(delta_string, &delta_seconds))
return enif_make_atom(env, "missing_or_invalid_delta_secs");
ERL_NIF_TERM comment;
StringPiece remaining(tok.current(), tok.end());
if (remaining.find('(') < remaining.find(';')) {
comment = ParseComment(env, &tok);
if (enif_is_atom(env, comment))
return comment;
} else {
comment = MakeString(env, "");
}
ERL_NIF_TERM parameters;
tok.SkipTo(';');
if (!tok.EndOfInput()) {
parameters = ParseParameters(env, &tok);
if (enif_is_atom(env, parameters))
return parameters;
} else {
parameters = enif_make_new_map(env);
}
return enif_make_tuple3(env, enif_make_int(env, delta_seconds), comment,
parameters);
}
ERL_NIF_TERM ParseMultipleWarnings(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
ERL_NIF_TERM result = enif_make_list(env, 0);
ValuesIterator it(values_begin, values_end, ',');
while (it.GetNext()) {
Tokenizer tok(it.value_begin(), it.value_end());
ERL_NIF_TERM value = ParseWarning(env, &tok);
if (enif_is_atom(env, value))
return value;
result = enif_make_list_cell(env, value, result);
}
enif_make_reverse_list(env, result, &result);
return result;
}
ERL_NIF_TERM ParseMultipleVias(ErlNifEnv* env,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
ERL_NIF_TERM result = enif_make_list(env, 0);
ValuesIterator it(values_begin, values_end, ',');
while (it.GetNext()) {
Tokenizer tok(it.value_begin(), it.value_end());
ERL_NIF_TERM value = ParseVia(env, &tok);
if (enif_is_atom(env, value))
return value;
ERL_NIF_TERM parameters = ParseParameters(env, &tok);
if (enif_is_atom(env, parameters))
return parameters;
int arity;
const ERL_NIF_TERM *via;
enif_get_tuple(env, value, &arity, &via);
ERL_NIF_TERM term = enif_make_tuple4(env, via[0], via[1], via[2],
parameters);
result = enif_make_list_cell(env, term, result);
}
enif_make_reverse_list(env, result, &result);
return result;
}
ERL_NIF_TERM ParseHeader(ErlNifEnv* env,
std::string::const_iterator name_begin,
std::string::const_iterator name_end,
std::string::const_iterator values_begin,
std::string::const_iterator values_end) {
ERL_NIF_TERM header_name_term = 0, header_values_term;
StringPiece header_name(name_begin, name_end);
StringPiece header_values(values_begin, values_end);
if (header_name.size() == 1) {
auto alias = g_aliases.find(ToLowerASCII(header_name[0]));
if (alias != g_aliases.end()) {
header_name_term = alias->second;
}
}
if (header_name_term == 0) {
MakeLowerCaseExistingAtom(env, header_name, &header_name_term);
}
auto f = g_parsers.find(header_name_term);
if (f != g_parsers.end()) {
header_values_term = (f->second)(env, values_begin, values_end);
if (enif_is_atom(env, header_values_term))
return header_values_term;
} else {
header_name_term = MakeString(env, header_name);
header_values_term = enif_make_list1(env, MakeString(env, header_values));
}
return enif_make_tuple2(env, header_name_term, header_values_term);
}
bool AssembleRawHeaders(const char* input, size_t length,
std::string *output) {
const char* line_start;
const char* line_end;
output->reserve(length);
for (size_t i = 0; i < length; i++) {
line_start = input + i;
do {
char c = *(input + i);
if (c == '\r' || c == '\n')
break;
i++;
} while (i < length);
line_end = input + i;
if (line_start != line_end)
output->append(line_start, line_end);
if (i == length)
break;
// now inspect the next character
char c = *(input + i);
if (c == '\n') {
i++; // accept single LF
} else if (c == '\r') {
i++;
if (i < length && *(input + i) == '\n')
i++; // default CRLF sequence
else
return false; // invalid CRLF sequence
}
if (i == length)
break;
if (!IsLWS(*(input + i)))
output->append(1, '\n'); // not line folding
i--; // return next character back
}
return true;
}
ERL_NIF_TERM Parse(ErlNifEnv* env, const char* raw_message, size_t length) {
std::string input;
if (!AssembleRawHeaders(raw_message, length, &input)) {
return enif_make_tuple2(env, enif_make_atom(env, "error"),
enif_make_atom(env, "invalid_line_break"));
}
ERL_NIF_TERM message = enif_make_new_map(env);
std::string::const_iterator i = input.begin();
std::string::const_iterator end = input.end();
std::string::const_iterator start = i;
ERL_NIF_TERM start_line;
i = FindLineEnd(start, end);
if (IsStatusLine(start, i)) {
start_line = ParseStatusLine(env, start, i);
} else {
start_line = ParseRequestLine(env, start, i);
}
if (enif_is_atom(env, start_line))
return start_line;
enif_make_map_put(env, message, enif_make_atom(env, "start_line"),
start_line, &message);
// Jump over next CRLF
if (i != end) {
if (*i == '\r')
++i;
if (i != end && *i == '\n')
++i;
}
HeadersIterator it(i, end, "\r\n");
ERL_NIF_TERM headers = enif_make_new_map(env);
while (it.GetNext()) {
ERL_NIF_TERM header = ParseHeader(env, it.name_begin(), it.name_end(),
it.values_begin(), it.values_end());
if (enif_is_atom(env, header))
return enif_make_tuple2(env, enif_make_atom(env, "error"),
header);
int arity;
const ERL_NIF_TERM* pair;
if (enif_get_tuple(env, header, &arity, &pair) && arity == 2) {
ERL_NIF_TERM header_name = pair[0], header_values = pair[1];
ERL_NIF_TERM adding_header_values;
if (enif_get_map_value(env, headers, header_name,
&adding_header_values)) {
if (!enif_is_list(env, adding_header_values)) {
return enif_make_tuple2(env, enif_make_atom(env, "error"),
enif_make_atom(env, "multiple_definition"));
}
enif_make_reverse_list(env, adding_header_values,
&adding_header_values);
ERL_NIF_TERM head, tail = header_values;
while (enif_get_list_cell(env, tail, &head, &tail)) {
adding_header_values = enif_make_list_cell(env, head,
adding_header_values);
}
enif_make_reverse_list(env, adding_header_values,
&adding_header_values);
} else {
adding_header_values = header_values;
}
enif_make_map_put(env, headers, header_name, adding_header_values,
&headers);
}
}
enif_make_reverse_list(env, headers, &headers);
enif_make_map_put(env, message, enif_make_atom(env, "headers"), headers,
&message);
return enif_make_tuple2(env, enif_make_atom(env, "ok"), message);
}
void LoadMethodAtoms(ErlNifEnv* env) {
#define SIP_METHOD(x) \
enif_make_atom(env, ToLowerASCII(#x).c_str());
#include "method_list.h"
#undef SIP_METHOD
}
void LoadHeaderNameAtoms(ErlNifEnv* env) {
ERL_NIF_TERM atom;
#define X(header_name, compact_name, atom_name, format) \
atom = enif_make_atom(env, #atom_name); \
g_parsers.insert(std::make_pair(atom, &Parse##format)); \
if (compact_name != 0) \
g_aliases.insert(std::make_pair(compact_name, atom));
#include "header_list.h"
#undef X
}
void LoadProtocolAtoms(ErlNifEnv* env) {
#define SIP_PROTOCOL(x) \
enif_make_atom(env, ToLowerASCII(#x).c_str());
#include "protocol_list.h"
#undef SIP_PROTOCOL
}
} // namespace
extern "C" {
static ERL_NIF_TERM parse_wrapper(ErlNifEnv* env, int argc,
const ERL_NIF_TERM argv[]) {
if (argc != 1) {
return enif_make_badarg(env);
} else if (enif_is_binary(env, argv[0])) {
ErlNifBinary bin;
enif_inspect_binary(env, argv[0], &bin);
return Parse(env, reinterpret_cast<const char*>(bin.data),
static_cast<size_t>(bin.size));
} else {
return enif_make_badarg(env);
}
}
int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) {
LoadMethodAtoms(env);
LoadHeaderNameAtoms(env);
LoadProtocolAtoms(env);
return 0;
}
static ErlNifFunc nif_funcs[] = {
{"parse", 1, parse_wrapper},
};
ERL_NIF_INIT(Elixir.Sippet.Parser, nif_funcs, on_load, NULL, NULL, NULL)
} // extern "C"