Packages

Elixir libraries and OTP behaviours for building telecom applications with SIP protocol and media handling

Current section

Files

Jump to
parrot_platform lib parrot sip method.ex
Raw

lib/parrot/sip/method.ex

defmodule Parrot.Sip.Method do
@moduledoc """
Module for working with SIP methods as defined in RFC 3261 and extensions.
SIP methods indicate the purpose of a SIP request. This module provides
functions for handling standard SIP methods and custom method names.
References:
- RFC 3261 Section 7.1: SIP Methods
- RFC 3261 Section 8.1: UAC Behavior
- RFC 3261 Section 20.1: Method Parameter
- RFC 6665: SIP-Specific Event Notification (SUBSCRIBE, NOTIFY)
- RFC 3515: The SIP Refer Method
- RFC 3311: The SIP UPDATE Method
- RFC 3903: SIP Extension for Event State Publication (PUBLISH)
- RFC 3428: SIP Extension for Instant Messaging (MESSAGE)
- RFC 4028: Session Timers in SIP (INFO)
"""
@standard_methods [
:ack,
:bye,
:cancel,
:info,
:invite,
:message,
:notify,
:options,
:prack,
:publish,
:refer,
:register,
:subscribe,
:update
]
@type t :: atom()
@doc """
Returns a list of all standard SIP methods.
## Examples
iex> Parrot.Sip.Method.standard_methods()
[:ack, :bye, :cancel, :info, :invite, :message, :notify, :options, :prack, :publish, :refer, :register, :subscribe, :update]
"""
@spec standard_methods() :: [atom()]
def standard_methods, do: @standard_methods
@doc """
Checks if a method is a standard SIP method.
## Examples
iex> Parrot.Sip.Method.is_standard?(:invite)
true
iex> Parrot.Sip.Method.is_standard?(:custom)
false
"""
@spec is_standard?(atom()) :: boolean()
def is_standard?(method) when is_atom(method), do: method in @standard_methods
def is_standard?(_), do: false
@doc """
Converts a string to a method atom. For standard methods, returns a
lowercase atom. For custom methods, returns an uppercase atom.
## Examples
iex> Parrot.Sip.Method.parse("INVITE")
{:ok, :invite}
iex> Parrot.Sip.Method.parse("CUSTOM")
{:ok, :CUSTOM}
iex> Parrot.Sip.Method.parse(123)
{:error, :invalid_method}
"""
@spec parse(String.t()) :: {:ok, atom()} | {:error, atom()}
def parse(method_str) when is_binary(method_str) do
method_atom =
method_str
|> String.downcase()
|> String.to_atom()
if is_standard?(method_atom) do
{:ok, method_atom}
else
# Custom method - preserve as uppercase atom
{:ok, String.to_atom(method_str)}
end
end
def parse(_), do: {:error, :invalid_method}
@doc """
Same as `parse/1` but raises an error for invalid methods.
## Examples
iex> Parrot.Sip.Method.parse!("INVITE")
:invite
iex> Parrot.Sip.Method.parse!("CUSTOM")
:CUSTOM
"""
@spec parse!(String.t()) :: atom()
def parse!(method_str) do
case parse(method_str) do
{:ok, method} ->
method
{:error, reason} ->
raise ArgumentError, "Invalid method: #{inspect(method_str)}, reason: #{reason}"
end
end
@doc """
Converts a method to its string representation.
## Examples
iex> Parrot.Sip.Method.to_string(:invite)
"INVITE"
iex> Parrot.Sip.Method.to_string(:CUSTOM)
"CUSTOM"
"""
@spec to_string(atom()) :: String.t()
def to_string(method) when is_atom(method) do
if is_standard?(method) do
method
|> Atom.to_string()
|> String.upcase()
else
Atom.to_string(method)
end
end
@doc """
Checks if a method is allowed to have a body.
## Examples
iex> Parrot.Sip.Method.allows_body?(:invite)
true
iex> Parrot.Sip.Method.allows_body?(:ack)
true
"""
@spec allows_body?(atom()) :: boolean()
def allows_body?(method) when is_atom(method) do
# According to RFC 3261, only REGISTER, OPTIONS, and ACK don't typically contain bodies,
# but they're technically allowed to have them.
true
end
@doc """
Checks if a method establishes a dialog.
## Examples
iex> Parrot.Sip.Method.creates_dialog?(:invite)
true
iex> Parrot.Sip.Method.creates_dialog?(:register)
false
"""
@spec creates_dialog?(atom()) :: boolean()
def creates_dialog?(:invite), do: true
def creates_dialog?(:subscribe), do: true
def creates_dialog?(:refer), do: true
def creates_dialog?(_), do: false
@doc """
Checks if a method requires Contact header.
## Examples
iex> Parrot.Sip.Method.requires_contact?(:invite)
true
iex> Parrot.Sip.Method.requires_contact?(:options)
false
"""
@spec requires_contact?(atom()) :: boolean()
def requires_contact?(:invite), do: true
def requires_contact?(:register), do: true
def requires_contact?(:subscribe), do: true
def requires_contact?(:notify), do: true
def requires_contact?(:refer), do: true
def requires_contact?(_), do: false
@doc """
Checks if a method can be canceled.
## Examples
iex> Parrot.Sip.Method.can_cancel?(:invite)
true
iex> Parrot.Sip.Method.can_cancel?(:ack)
false
"""
@spec can_cancel?(atom()) :: boolean()
def can_cancel?(:invite), do: true
def can_cancel?(:subscribe), do: true
def can_cancel?(:notify), do: true
def can_cancel?(:register), do: true
def can_cancel?(:update), do: true
def can_cancel?(:publish), do: true
def can_cancel?(:message), do: true
def can_cancel?(:info), do: true
def can_cancel?(_), do: false
end