Include "simapi.h" and link to simcore/simcore.a to use functions described in this document. You also need to link to the following libraries from their subdirectories:
Do NOT link to stock versions of crypto++ or any other of these libraries that might already be installed on the system.
To identify contacts throughout the library, each one has a contact id, which is a 64-bit non-zero integer (simnumber). These ids are locally unique and persistent for each contact and user identity. API functions that work on a specific contact take this id as their first argument.
All IP addresses and simphone addresses are in ASCII.
Time is expressed as an unsigned integer containing the number of seconds since Jan 1st 1970 (GMT), unless noted otherwise.
Functions described in this document are generally thread-safe and mostly non-blocking, unless stated otherwise.
Functions that work on specific data (this includes all simtype functions, referred to in type system, and all other utility functions) are thread-safe only as long as different threads work on different data. If you're writing the same data from more than one thread (or writing the data from one thread and reading it from another), you'd have to implement locking around the function calls.
The rest of the functions (except for sim_init_ and sim_exit_) are serialized, whereas the log_xxx_ functions are serialized separately from all other functions.
Most API functions return an int; unless otherwise indicated, this is SIM_OK on success or an error code on failure. Error codes are defined in error.h (also check the description of the sim_error_xxx functions). Some functions return a simbool; this means true on success or false on failure.
For functions that return simtypes, see type system. Any simtypes that you get from API calls are not going to be modified by background threads. These values are yours until you free them explicitly; you do not need to make copies of them.
simtypes are used throughout the library and in the API. A simtype is a pointer with a length and defines a value of one of four types:
SIMNUMBER |
a 64-bit integer (simnumber if signed or simunsigned if unsigned). |
SIMSTRING or SIMPOINTER |
array of bytes; length does not count the terminating zero byte (if any). |
SIMTABLE |
an unordered list of SIMSTRING or SIMPOINTER keys with values of any of the four types. |
SIMARRAY |
an ordered list of values; all values must have the same type. The index of the first value of an array is one, not zero. |
Type SIMNIL indicates that no value is present; SIMNIL is not allowed to be a member of either SIMTABLE or SIMARRAY, but may be returned by some functions to indicate an error. Each simtype used by the API already has a known type, so it is usually not necessary to check its type, but this might be done (if needed) by calling sim_get_type.
To free simtypes, the following functions are available:
SIMNUMBER |
sim_number_free |
SIMSTRING |
sim_string_free |
SIMTABLE |
sim_table_free |
SIMARRAY |
sim_array_free |
Freeing a table or an array also recursively frees all values stored within it. Freeing a SIMSTRING calls sim_free; freeing a SIMPOINTER does nothing (SIMPOINTER holds a pointer to user-managed memory). Freeing a SIMNIL with any of the four functions listed above is also explicitly allowed as a no-operation.
Storing a value other than SIMPOINTER into an array or a table (as a value OR as a key) causes the array/table to "consume" the value; it will be freed automatically when the array/table is freed. The caller CANNOT free it. Storing a SIMPOINTER value into an array or a table assumes that the value will "live" at least as long as the array or table does, and will be freed by the caller later.
A SIMSTRING can be converted to SIMPOINTER by calling sim_pointer_new_length, but if you store it to an array or a table, do make sure you do not free the original SIMSTRING before the array or table is freed.
The following functions are available to create simtypes:
SIMNIL |
sim_nil |
SIMNUMBER |
sim_number_new |
SIMSTRING |
sim_string_new, sim_string_copy, sim_string_copy_length |
SIMPOINTER |
sim_pointer_new, sim_pointer_new_length |
SIMTABLE |
sim_table_new |
SIMARRAY |
sim_array_new_numbers, sim_array_new_strings, sim_array_new_tables, sim_array_new_arrays |
To access a simtype value, use the following:
SIMNIL |
if sim_get_type returns SIMNIL then simtype is sim_nil; sim_get_pointer returns NULL. |
SIMNUMBER |
sim_get_number returns the value of the number. |
SIMSTRING or SIMPOINTER |
sim_get_pointer returns pointer to the string, sim_get_length returns its length. |
SIMTABLE |
use functions sim_table_get_xxx, sim_table_set_xxx, sim_table_add_xxx, sim_table_delete_xxx. To enumerate all elements, use sim_table_walk_first and sim_table_walk_next_xxx. Note that sim_table_add will fail if key already exists while sim_table_set will overwrite. sim_table_count returns the number of elements in a table. |
SIMARRAY |
use functions sim_array_get_xxx and sim_array_set_xxx to access elements (i > 0); sim_get_length returns the number of elements. The sim_array_get_type function returns the type of all values of an array. |
The following convention applies when passing simtypes as function arguments:
const simtype |
function reads the contents of a simtype. |
simtype |
function reads and writes the contents of a simtype. |
simtype * |
function creates a new simtype and writes its contents. |
For more details on simtypes please see the comments in simapi.h.
Must be called before calling any other functions. version must be 1, while ui should be the name and version number of your program.
user contains absolute pathname of a directory where files for this user identity are kept or an empty string to use the default location. If user is NULL, no files will be kept.
If sim_init_ returns anything other than SIM_OK, you can call it again later (after removing the error condition that caused the failure) to retry.
Must be called after sim_init_ has returned successfully, in order to login.
password can be an empty string (for auto-login) or contain a secret key ("seed") or password that the saved secret key is encrypted with.
To login with a key that was just generated, but not saved, call sim_init_user_ with NULL password. In this case, sim_key_generate_ must have been previously called with zero size and must have finished successfully (SIM_EVENT_KEYGEN has been received).
If sim_init_user_ fails, it can be called again later to retry.
Must be called in order to logout and notify contacts that you have gone offline.
If sim_exit_user_ returns SIM_OK, you can call sim_init_user_ if you want to login again. If sim_exit_user_ fails, the API is in an inconsistent state and you should exit your program without calling any API functions.
sim_exit_user_ might block for a long time, so it is a good idea to call it from a background thread if your program has something else to do at the same time.
Optional function that can be called as the last thing in your program. It saves any open files and then frees all allocated resources from memory.
If sim_exit_ fails, the API is in an inconsistent state and you should exit your program without calling any API functions. If sim_exit_ returns SIM_OK, the only API function you should call afterwards is sim_init_ (if necessary).
If size is not zero, generate and return a new random seed of the specified size (number of bits) and type, and do not generate a key (caller has to call sim_string_free to release the returned *seed when done with it). entropy is either sim_nil or else a sequence of random bytes (SIMSTRING or SIMPOINTER), which is used as additional entropy for seed (key) generation.
If size is zero and type is not SIM_KEY_NONE, generate a random key of the specified type. If *seed is not sim_nil, also save key encrypted with *seed (which can be any zero-terminated string, which is NOT a valid seed) or if *seed is an empty string, save key without encrypting it.
If size is zero and type is SIM_KEY_NONE, generate a new key from *seed, which must be a valid seed (such as one returned by sim_key_generate_ when called with non-zero size). entropy must be sim_nil in this case.
Currently supported key types are:
If called with zero size (to generate a key), sim_key_generate_ will return successfully before it has finished its job. SIM_EVENT_KEYGEN will be received later to report progress, success or failure.
If *password is 0, save decrypted private key (enable auto-login in future). Otherwise, save private key encrypted with the specified password (which can be a seed or any other UTF-8 character string). If password is NULL, delete previously saved private key.
If bits is SIM_PASSWORD_BIT_DEFAULT, save key only if no key exists already. SIM_PASSWORD_BIT_OVERWRITE allows overwriting an existing saved key.
Return the current status (SIM_STATUS_xxx). If flags is not NULL, also sets *flags to a bit-wise combination (OR) of:
SIM_STATUS_FLAG_DHT_OUT |
outgoing UDP peer-to-peer connectivity is available. |
SIM_STATUS_FLAG_DHT_IN |
incoming UDP peer-to-peer connectivity is available. |
SIM_STATUS_FLAG_UDP_OUT |
outgoing UDP connectivity is available. |
SIM_STATUS_FLAG_UDP_IN |
incoming UDP connectivity is available. |
SIM_STATUS_FLAG_TCP_OUT |
outgoing TCP connectivity is available. |
SIM_STATUS_FLAG_TCP_IN |
incoming TCP connectivity is available. |
SIM_STATUS_FLAG_SSL_OUT |
outgoing TCP connectivity to HTTPS port is available. |
SIM_STATUS_FLAG_SSL_IN |
incoming TCP connectivity to HTTPS port is available. |
SIM_STATUS_FLAG_DNS |
Name resolver has been answering DNS queries. |
SIM_STATUS_FLAG_BOOT |
DHT initialization servers have been contacted. |
SIM_STATUS_FLAG_UPNP |
incoming port forwarded over UPnP. |
Set the current status. status can be one of the following:
SIM_STATUS_ON |
on-line and available. |
SIM_STATUS_AWAY |
on-line but appear "idle" (set manually). |
SIM_STATUS_IDLE |
on-line but appear "idle" (set automatically on inactivity). |
SIM_STATUS_BUSY |
on-line but not to be disturbed (sounds will not be played). |
SIM_STATUS_HIDE |
on-line but not watching (GUI not visible). |
SIM_STATUS_OFF |
off-line. Not connected to the DHT and not accepting or initiating connections. |
SIM_STATUS_INVISIBLE |
connected to the DHT but appear off-line. Outgoing connections can be made to any contact. |
Call sim_contact_connect_ to try to connect immediately when opening a chat window, so that the first chat messages might be sent without delay when typed later. Call sim_contact_disconnect_ when the chat window is closed, so the library knows that this connection is no longer in active use and may close the network connection if/when necessary. Connections are reference-counted, so if you call sim_contact_connect_ for a given id, you MUST call sim_contact_disconnect_ for that id exactly the same number of times (even if sim_contact_connect_ returned an error).
Send a special message that the contact will respond to (if alive) but the human user won't see. This function is intended mainly for testing and debugging.
number must be positive (or zero if you don't care). bits is a bit-wise combination (OR) of the following:
SIM_PING_BIT_CONNECT |
first try to connect to contact if not currently connected. |
SIM_PING_BIT_TCP |
send a ping if connected to contact; SIM_EVENT_NET_PONG will be received when contact responds. |
SIM_PING_BIT_NAT |
initiate a NAT traversal attempt (only if currently connected through proxy); SIM_EVENT_NET_TRAVERSE will eventually be received. |
Return information about a contact. To get information about all contacts, call sim_contact_get_ with a zero id and CONTACT_BIT_DEFAULT. It will return an array containing all contact ids. Note that the indexes of this array are guaranteed to remain the same until sim_exit_user_ is called; if contacts are added to the list in the mean time, they will have new indexes.
bits is a bit-wise combination (OR) of the following:
CONTACT_BIT_DEFAULT |
return all keys except the ones which have their own bits defined (below). |
CONTACT_BIT_LATENCY |
also return CONTACT_KEY_LATENCY. If CONTACT_BIT_DEFAULT is set together with CONTACT_BIT_LATENCY, also send a ping to measure latency. |
CONTACT_BIT_VERIFY |
also return CONTACT_KEY_VERIFY. |
CONTACT_BIT_LINE |
also return CONTACT_KEY_LINE. |
CONTACT_BIT_STATS |
also return CONTACT_KEY_CONNECT and CONTACT_KEY_HOSTS. |
Returns sim_nil if contact doesn't exist (invalid id) or else a table with the following keys:
CONTACT_KEY_ADDRESS |
string: simphone address of contact. Not to be used for anything except displaying (if requested explicitly by human user). |
CONTACT_KEY_NICK |
string: nickname in UTF-8 encoding. |
CONTACT_KEY_OWN_NICK |
string: contact's own nickname in UTF-8 encoding. May be missing if not known. |
CONTACT_KEY_SEEN |
number: time when contact was last seen. May be zero if not known. |
CONTACT_KEY_STATUS |
number: current status (SIM_STATUS_ON, SIM_STATUS_AWAY, SIM_STATUS_BUSY, SIM_STATUS_HIDE or SIM_STATUS_OFF); SIM_STATUS_INVISIBLE means implicit logout (disappeared without logging out). |
CONTACT_KEY_FLAGS |
number: communication rights. A bit-wise combination (OR) of the following:
|
CONTACT_KEY_AUTH |
number: authorization level, which is one of the following:
|
CONTACT_KEY_EDIT |
number: number of last sent chat messages that contact allows you to edit. Zero if no editing is desired. |
CONTACT_KEY_IP |
string: current IP address. Missing if unknown or not connected. |
CONTACT_KEY_LOCATION |
string: IP address where contact was last located. Missing if never connected. |
CONTACT_KEY_CONNECT |
string: proxy IP address or/and connection type. Missing if not currently connected. |
CONTACT_KEY_LATENCY |
number: packet round-trip time in milliseconds. Zero means unknown; missing if not currently connected. |
CONTACT_KEY_HOSTS |
array of strings: list of contact's DynDNS hostnames. Missing or empty if none. |
CONTACT_KEY_CIPHER |
string: name of cipher last used by contact. Empty if never connected. |
CONTACT_KEY_KEY_SIZE |
number: contact's key size (number of bits). May be zero if not known; missing for CONTACT_VIP_TEST. |
CONTACT_KEY_VERIFY |
string: own verification token for this contact in ASCII. Missing if not currently available. |
CONTACT_KEY_LINE |
string: contact's info line in UTF-8 encoding. Empty string if no info line. |
CONTACT_KEY_AUDIO |
string: contact's call state. The following values are currently defined:
|
CONTACT_KEY_VIP |
string: type of contact, which is one of the following:
|
For contacts that return CONTACT_AUTH_FORGET, CONTACT_AUTH_REVOKED or CONTACT_AUTH_DELETED, the following keys may be missing (in addition to the already mentioned possibly missing keys): CONTACT_KEY_STATUS, CONTACT_KEY_KEY_SIZE, CONTACT_KEY_LINE.
The returned data (when called with either zero or non-zero id) is guaranteed to remain accessible until sim_contact_free_ is called (but note that network events might update it, in which case you should call sim_contact_get_ again to retrieve the new data).
Caller has to call sim_contact_free_ to release the returned value when done with it.
Must be called with a value returned by sim_contact_get_ as an argument. It will release the copy of the contact information from memory.
Add a new contact with the specified simphone address and save the contacts file. Checks the validity of the simphone address and corrects it if necessary (and possible) and (if id is not NULL) also sets *id to the new contact id.
If the contact to be added already exists, this function will authorize the contact without returning an error.
Set information about a contact to the given value and save the contacts file. If id is zero, set your own information. key is one of:
CONTACT_KEY_NICK |
set a new nickname for contact (string) in UTF-8 encoding (empty string to reset to contact's own nickname). |
CONTACT_KEY_LINE |
set a new info line for contact (string) in UTF-8 encoding (empty string to remove info line). Only with zero id. |
CONTACT_KEY_FLAGS |
modify communication rights. value (number) is a bit-wise combination (OR) of the following:
Only bits that are set in the value mask are modified. To clear both "yes" and "no" flags, you need to SET both in the flags mask. |
CONTACT_KEY_VERIFY |
set contact verification flag. value (string) contains verification token or is empty (to unverify contact). |
CONTACT_KEY_AUTH |
set authorization level of contact (not with zero id). value (number) is one of the following:
Note that forgotten contacts are unblocked (would be able to send a contact request) on next login. |
Send a chat message, which must be a text string encoded in UTF-8. An index that identifies the message (can be given to sim_msg_edit_utf_ and sim_msg_get_) is returned as *idx.
If message text contains multiple lines, they can be separated by line feed (0x0A) characters, but it is recommended to send each line as a separate message instead.
Do NOT free the text string after the function has returned; if it is a SIMSTRING, it will be freed either before sim_msg_send_utf_ has returned, or when sim_msg_remove_, sim_msg_load_ or sim_exit_user_ is called. If it is a SIMPOINTER, it is your responsibility to free it after you have called one of these three functions.
Change a previously sent chat message identified by the given index. Freeing of the message text string has to happen by the same rules as with sim_msg_send_utf_.
Permanently delete a chat message from local chat history. The message string is removed immediately from both memory and history file, an action that cannot be undone. If the message was sent but not yet received, it will not be received; if it was sent and received, it cannot be changed after this function has been called.
Read message from/to contact with the given index. Indexes start at 1 and are guaranteed to remain the same until either sim_msg_load_ or sim_exit_user_ is called.
The returned value is sim_nil if an (invalid argument) error occurred or else it is a table with the following keys:
SIM_CMD_MSG_STATUS |
number: message status
|
SIM_CMD_MSG_TYPE |
number: SIM_MSG_TYPE_UTF or SIM_MSG_TYPE_SYSTEM. |
SIM_CMD_MSG_TIME |
number: time when message was sent. |
SIM_CMD_MSG_RECEIVED |
number: time when message was received. Missing (zero) if not received. |
SIM_CMD_MSG_EDIT |
number: time when message was last edited. Missing (zero) if not edited. |
SIM_CMD_MSG_HANDLE |
number: file transfer handle. Missing (zero) if unknown. |
SIM_CMD_MSG_TEXT |
string: message text in UTF-8 encoding. Missing if message was removed. |
SIM_CMD_MSG_SENDER |
string: nickname of user who sent the message (contact or yourself). |
SIM_CMD_MSG_NICK |
string: nickname of contact when message was sent or received. |
Caller has to call sim_msg_free_ to release the returned table when done with it.
Must be called with a value returned by sim_msg_get_ as an argument. It will free the returned table but not release the message text or nickname from memory; that happens automatically when sim_msg_remove, sim_msg_load_ or sim_exit_user_ is called.
Return the highest message index that can currently be given as an argument to sim_msg_get_. More messages may arrive at any time.
Load the last number of messages from history file (and unload all others, except pending messages). To clear the message buffer, call with zero number. To load the whole chat history into the buffer, call with a very large number.
This function invalidates all message indexes and frees the text and nicknames of all messages.
sim_msg_load_ might block for a long time if there are many chat messages.
Send a file to contact; its pathname is a zero-terminated text string encoded in UTF-8. A positive number that uniquely identifies the file (can be given to sim_xfer_set_ and sim_xfer_get_) is returned as *handle.
If bits is SIM_XFER_BIT_DEFAULT, return sim_nil if an (invalid argument) error occurred or else return information about a file transfer with the given handle as a table with the following keys:
SIM_CMD_XFER_SEND_TYPE |
string: state of file transfer, which is one of the following:
|
SIM_CMD_XFER_SEND_NAME |
string: name of file (pathname for outgoing transfers). |
SIM_CMD_XFER_SEND_SIZE |
number: size of file in bytes. |
SIM_CMD_XFER_SEND_TIME |
number: time when file was sent (nanoseconds). |
SIM_CMD_XFER_OFFSET |
number: number of bytes that have been received already. |
SIM_CMD_XFER_CLOSE_ERROR |
number: SIM_OK or error code which caused file transfer failure. |
If handle is SIM_XFER_GET_RECEIVED or SIM_XFER_GET_SENT, return information about all incoming or outgoing file transfers with a contact. That information is returned as a table with the same keys, but its values are arrays. Additionally, SIM_CMD_XFER_SEND_HANDLE is present as a key; its value is an array of numbers that contains the handles of all returned file transfers. A single file transfer is thus defined by the values of all keys indexed by the same index in the returned arrays.
If handle is SIM_XFER_GET_INFO, return general information about file transfers with a contact, as a table which includes some of the following keys:
SIM_XFER_GET_INFO_PAUSE |
number: usually zero or else the same handle given as SIM_XFER_TYPE_PAUSE (or a negated handle value) to indicate the current pause or resume state with the given contact. |
SIM_XFER_GET_INFO_CURRENT |
number: handle of currently running file transfer. Missing if no current file transfer. |
SIM_XFER_GET_INFO_DURATION |
number: number of milliseconds for which the transfer has been currently running. Missing if no current file transfer or if the running time is not yet known. |
SIM_XFER_GET_INFO_TRANSFERRED |
number: number of bytes transferred during the current session. Present only if the value of SIM_XFER_GET_INFO_DURATION is also positive. |
SIM_XFER_GET_INFO_RECEIVED |
string: pathname of the received (download) subdirectory; missing if none. Present only if bits is SIM_XFER_BIT_EXTENDED. |
SIM_XFER_GET_INFO_SENT |
string: pathname of the sent (upload) subdirectory; missing if none. Present only if bits is SIM_XFER_BIT_EXTENDED. |
Unlike all other functions, sim_xfer_get_ does include the terminating zero in lengths of returned file names (SIM_CMD_XFER_SEND_NAME) and pathnames (SIM_XFER_GET_INFO_RECEIVED and SIM_XFER_GET_INFO_SENT).
Must be called with a value returned by sim_xfer_get_ as an argument. It will free the returned table from memory.
Control an already existing file transfer. type is one of:
SIM_XFER_TYPE_PAUSE |
temporarily stop file transfers with contact if handle is SIM_CMD_XFER_INIT_PAUSE_ALL or resume them if handle is SIM_CMD_XFER_INIT_PAUSE_NONE. A valid handle (previously returned by sim_xfer_send_file_ or sim_xfer_get_) resumes a specific file transfer. |
SIM_XFER_TYPE_CANCEL |
trigger an immediate and permanent failure of file transfer. handle may be zero to fail all file transfers with contact or non-zero to fail a specific file transfer. |
SIM_XFER_TYPE_FREE |
optionally release data cached in memory but not returned by sim_xfer_get_. This function can be used when future calls to sim_xfer_get_ do not seem likely. |
These functions can be called before sim_init_user_ or after sim_exit_user_ but in this case id must be either CONTACT_ID_KEYGEN or CONTACT_ID_TEST; any other id can be used only if called after sim_init_user_ and before sim_exit_user_.
Call a contact or accept an incoming call. Use CONTACT_ID_TEST to make a test call or CONTACT_ID_KEYGEN to make a test call to collect entropy for secret key generation. When using CONTACT_ID_KEYGEN to generate a seed, you need to set the crypto.entropy configuration parameter to the seed size in bits before making the call. When using CONTACT_ID_KEYGEN to generate a key, you need to reset the same parameter to its default value before making the call.
Hangup or decline call from contact, or end an outgoing call.
Return id of contact which is currently talking, or zero if nobody's talking.
Play sound with the specified name. If count is zero, play repeatedly until stop. Otherwise, play the specified number of times and stop.
Stop playing the specified sound immediately.
Return the default value and, if applicable, minimal and maximal values of the specified configuration parameter. Any of the pointers might be NULL if not interested in that value. If parameter value is a list of strings, sim_array_free must be called to release the returned *def.
The returned *description is a human-readable string that describes the parameter; it may be NULL if no description is available.
Return value of configuration parameter with the specified name. If name is NULL, return all configuration parameters as a table keyed by parameter name.
Must be called with a value returned by sim_param_get_ as an argument. It will release the copy of the parameter value from memory.
Set value of the specified configuration parameter. permanent is one of:
To unset a parameter, call with sim_nil value and SIM_PARAM_USER.
If param is NULL, set values of multiple configuration parameters (value is a table keyed by parameter name).
Return system information. bits is a bit-wise combination (OR) of the following:
SIM_INFO_BIT_NET |
return network status (SIM_INFO_NET_xxx). |
SIM_INFO_BIT_AUDIO |
return lists of currently available audio device names (SIM_INFO_AUDIO_xxx). |
SIM_INFO_BIT_CRYPT |
return list of supported ciphers (SIM_INFO_CRYPT_CIPHERS). |
Returns a table with the following keys:
SIM_INFO_NET_IP |
string: own IP address (may be an intranet address if UPnP is not in use). |
SIM_INFO_NET_DHT_GOOD |
number: number of good DHT nodes. |
SIM_INFO_NET_DHT_DUBIOUS |
number: number of dubious DHT nodes. |
SIM_INFO_NET_DHT_BLOCKED |
number: number of temporarily blacklisted DHT nodes. |
SIM_INFO_NET_DHT_BAD |
number: number of permanently blacklisted DHT nodes. |
SIM_INFO_AUDIO_INPUT |
array of strings: list of recording devices. Each element is a device name. |
SIM_INFO_AUDIO_OUTPUT |
array of strings: list of playback devices. Each element is a device name. |
SIM_INFO_CRYPT_CIPHERS |
table: supported ciphers. Each key is a cipher name, while value is a number, which is zero if that cipher is not preferred. |
Caller has to call sim_list_free to release the returned table when done with it.
Return list of library versions as a table. Each key is a name string, while value is a version string.
Caller has to call sim_list_free to release the returned table when done with it.
Must be called with a value returned by sim_list_versions or sim_list_info_ as an argument. It will release the returned table from memory.
Notification about anything that happens can be received asynchronously by registering event handlers, if interested. This means that user event handlers will be called from different threads; synchronization between these handlers and the main thread must be implemented, if necessary. You should avoid using a lot of stack space in event handlers or else you may trigger a stack overflow.
Each event can be registered multiple (or zero) times. To do that, use:
Register an event handler; the old handler for this event is stored in *oldhandler. The returned *oldhandler may be NULL if no other handler has been registered; otherwise, the new event handler MUST call the old one before it exits, and use exactly *oldhandler to do so. Do not copy or move the returned pointer.
The normal use of sim_event_register_ is to call it immediately after sim_init_.
typedef void (simevent) (simnumber id, const simtype event);
The first argument of a simevent handler callback is the contact id associated with that event (can be zero for events that are not associated with any contact). The second argument is a table containing data relevant to each event type; values within this table are generally accessible only from within the event handler.
The following event types are currently defined:
SIM_EVENT_FATAL. Fatal error has occurred. Two table keys:
SIM_EVENT_FATAL |
number: error code which is SIM_OK if the error is not recoverable. |
SIM_CMD_MSG_TEXT |
string: fatal error string in UTF-8 encoding. |
SIM_EVENT_FATAL can be handled by blocking until the fatal error condition has (hopefully) gone away. An error message (without trying to allocate memory) could be displayed to the user and after user input, you can return in order to retry or ignore the failed action. If SIM_EVENT_FATAL is not handled, the library will just _exit() you.
Note that the error string might be missing in certain circumstances, so this case needs to be handled too.
SIM_EVENT_CONTACT. List of contacts has changed. One table key:
SIM_EVENT_CONTACT |
number: error code or SIM_OK if no error. |
SIM_OK usually means that a contact with the given id has been added to the contact list either as an accepted contact, or (by default) as CONTACT_VIP_NEW. In certain cases, the contact might be missing from the contact list (because it could not be added).
SIM_EVENT_STATUS. Contact status or information has changed. One to seven table keys:
SIM_EVENT_STATUS |
number: old (previous) contact status. Missing with artificial flags events. |
CONTACT_KEY_STATUS |
number: new (current) status (SIM_STATUS_ON, SIM_STATUS_AWAY, SIM_STATUS_BUSY, SIM_STATUS_HIDE or SIM_STATUS_OFF); SIM_STATUS_INVISIBLE means implicit logout (disappeared without logging out). |
CONTACT_KEY_FLAGS |
number: current communication rights. A bit-wise combination (OR) of CONTACT_FLAG_VERIFY, CONTACT_FLAG_UTF, CONTACT_FLAG_XFER, CONTACT_FLAG_AUDIO and CONTACT_FLAG_TYPE. |
CONTACT_KEY_EDIT |
number: number of last sent chat messages that can be edited. |
CONTACT_KEY_OWN_NICK |
string: old (previous) nickname in UTF-8 encoding. |
CONTACT_KEY_NICK |
string: new (current) nickname in UTF-8 encoding. |
CONTACT_KEY_LINE |
string: contact's current info line in UTF-8 encoding; empty string means contact's info line has been removed. |
CONTACT_KEY_STATUS is present in the table only if status has changed. CONTACT_KEY_FLAGS is present only if communication rights have changed. CONTACT_KEY_EDIT is present only if the allowed number of last messages to edit has changed. CONTACT_KEY_NICK and CONTACT_KEY_OWN_NICK are present only if nickname has changed. CONTACT_KEY_LINE is present only if info line has changed.
SIM_EVENT_STATUS is received with zero id when your status has been automatically changed to SIM_STATUS_OFF in order to prevent data loss.
SIM_EVENT_MSG. Incoming chat message. Two table keys:
SIM_EVENT_MSG |
number: index of received message (can be given to sim_msg_get_). |
SIM_CMD_MSG_TEXT |
string: message text in UTF-8 encoding. |
SIM_EVENT_EDIT. Edit of a previously received chat message. Two table keys:
SIM_EVENT_EDIT |
number: index of message being edited (can be given to sim_msg_get_). |
SIM_CMD_MSG_TEXT |
string: new message text in UTF-8 encoding. |
SIM_EVENT_SENT. Outgoing chat messages have been sent. One table key:
SIM_EVENT_MSG |
array of numbers: list of sent messages (may be empty). Each element is a message index. |
SIM_EVENT_NOTSENT. Outgoing chat messages have not been sent yet. One table key:
SIM_EVENT_MSG |
array of numbers: list of not sent messages. Each element is a message index. |
SIM_EVENT_XFER. File transfer state change or file name change. Three table keys:
SIM_EVENT_XFER |
string: old file transfer state type (as previously returned by sim_xfer_get_) or old file name. |
SIM_CMD_XFER_SEND_TYPE |
string: new file transfer state type (as would be returned by sim_xfer_get_) or new file name. |
SIM_CMD_XFER_SEND_HANDLE |
number: file transfer handle (as returned by sim_xfer_get_). |
If the value of SIM_CMD_XFER_SEND_HANDLE is SIM_XFER_GET_RECEIVED or SIM_XFER_GET_SENT, this is a file name change. SIM_EVENT_XFER and SIM_CMD_XFER_SEND_TYPE contain old and new name of file in the received or sent subdirectory. If both of them are non-empty, a file has been renamed. If only SIM_CMD_XFER_SEND_TYPE is empty, a file has been deleted. If only SIM_EVENT_XFER is empty, a file has been created.
Otherwise, this event indicates a file transfer state change. If both SIM_EVENT_XFER and SIM_CMD_XFER_SEND_TYPE are non-empty, only a type change has taken place. If only SIM_EVENT_XFER is empty, the type has not changed, but something else returned by sim_xfer_get_ has. If only SIM_CMD_XFER_SEND_TYPE is empty, the position of this file transfer has changed, but nothing else has.
If both SIM_EVENT_XFER and SIM_CMD_XFER_SEND_TYPE are empty, this event indicates creation or destruction of an incoming file transfer (the value of SIM_CMD_XFER_SEND_HANDLE is negated on destruction).
SIM_EVENT_AUDIO. Call state change. Two table keys:
SIM_EVENT_AUDIO |
string: old (previous) call state (as CONTACT_KEY_AUDIO returned by sim_contact_get_). |
CONTACT_KEY_AUDIO |
string: current call state (as CONTACT_KEY_AUDIO returned by sim_contact_get_).
|
SIM_EVENT_SPEECH. Indicate current speech level. Three table keys:
SIM_EVENT_SPEECH |
number: error code or SIM_OK if no error. |
SIM_EVENT_SPEECH_PROBABILITY |
number: speech probability. |
SIM_EVENT_SPEECH_PROGRESS |
number: progress of entropy collection (per cent). |
Received with CONTACT_ID_KEYGEN or CONTACT_ID_TEST id during audio test, or with a contact id otherwise. The probability level is between SIM_EVENT_SPEECH_MIN and SIM_EVENT_SPEECH_MAX, or it is SIM_EVENT_SPEECH_END to indicate end of speech recording (only when id is CONTACT_ID_KEYGEN or CONTACT_ID_TEST).
SIM_EVENT_SPEECH_PROGRESS makes sense only when id is CONTACT_ID_KEYGEN.
SIM_EVENT_KEYGEN. Public key generation is taking place. One or two table keys:
SIM_EVENT_KEYGEN |
number: error code (if failed). |
SIM_EVENT_KEYGEN_TIME |
number: estimated key generation time in seconds. |
SIM_EVENT_KEYGEN_ADDRESS |
string: new simphone address if generated; id is non-zero only in this case. |
Received multiple times while generating an RSA key. After the initial time estimation, other non-zero (positive or negative) values of SIM_EVENT_KEYGEN_TIME are received as updates of the initial time estimation. When key generation has finished, this event is finally received without a value for SIM_EVENT_KEYGEN_TIME.
SIM_EVENT_HISTORY. System message has just been generated. Five table keys:
SIM_EVENT_HISTORY |
number: index of system message or zero if none. |
SIM_CMD_MSG_STATUS |
number: message status (as described for sim_msg_get_). |
SIM_CMD_MSG_TIME |
number: time when message was sent or zero if generated locally. Missing if unknown. |
SIM_CMD_MSG_RECEIVED |
number: time when event started to happen. |
SIM_CMD_MSG_HANDLE |
number: file transfer handle or zero. |
SIM_CMD_MSG_TEXT |
string: as defined by the following two tables. |
The following system messages are currently defined with SIM_MSG_INCOMING status:
STATUS OFF |
multiple login forced my own logout. |
STATUS ON |
multiple login tried to force the other side to logout. |
FILE SEND size name |
contact initiated a file transfer. |
FILE RECV 0 name |
file transfer to contact finished successfully. |
FILE RECV err name |
contact failed to receive file. |
FILE CANCEL 0 name |
contact cancelled a file transfer from contact. |
FILE CANCEL err name |
contact failed to send file. |
FILE REJECT 0 name |
contact cancelled a file transfer to contact. |
FILE REJECT err name |
contact failed to receive file. |
CALL START |
incoming call was answered. |
CALL FAILED err |
contact tried to make an incoming call but couldn't connect. |
CALL FAILED |
contact tried to make an incoming call but gave up before he could connect. |
CALL HANGUP err |
there was an outgoing call to contact in progress but the other side failed to initiate or start it. |
CALL HANGUP |
the other side initiated a hangup while there was an outgoing call to contact in progress. |
CALL HUNGUP err |
there was an incoming call from contact in progress but the other side failed to initiate or start it. |
CALL HUNGUP |
the other side initiated a hangup while there was an incoming call from contact in progress. |
CALL ABORT err |
there had been an incoming call from contact but socket was disconnected. |
CALL ABORT |
contact gave up on an incoming call before it could be answered. |
CALL BUSY err |
had made an outgoing call to contact but the other side failed to receive or answer it. |
CALL BUSY |
had made an outgoing call to contact but the other side declined the call. |
The following system messages are currently defined with status other than SIM_MSG_INCOMING:
STATUS OFF |
multiple login forced my own logout. |
STATUS ON |
multiple login did not force my own logout. |
FILE SEND size name |
initiated a file transfer to contact. |
FILE RECV 0 name |
file transfer from contact finished successfully. |
FILE RECV err name |
failed to receive file from contact. |
FILE CANCEL 0 name |
cancelled a file transfer to contact. |
FILE CANCEL err name |
failed to send file to contact. |
FILE REJECT 0 name |
cancelled a file transfer from contact. |
FILE REJECT err name |
failed to receive file from contact. |
CALL START |
outgoing call was answered. |
CALL FAILED err |
made an outgoing call but couldn't connect to contact. |
CALL FAILED |
had made an outgoing call but couldn't connect to contact before hangup was initiated locally. |
CALL HANGUP err |
socket was disconnected while there was an outgoing call to contact in progress. |
CALL HANGUP |
hangup was initiated locally while there was an outgoing call to contact in progress. |
CALL HUNGUP err |
socket was disconnected while there was an incoming call from contact in progress. |
CALL HUNGUP |
hangup was initiated locally while there was an incoming call from contact in progress. |
CALL ABORT err |
had made an outgoing call to contact but socket was disconnected. |
CALL ABORT |
had made an outgoing call to contact before hangup was initiated locally. |
CALL BUSY err |
failed to receive or answer incoming call. |
CALL BUSY |
declined an incoming call from contact. |
CALL ERROR err |
failed to initiate or start outgoing call. |
size is file size in bytes. Each FILE SEND message usually has a corresponding FILE RECV, FILE CANCEL or FILE REJECT message.
err (if present) is the same kind of error code which is returned everywhere else in the library. Each CALL START message usually has a corresponding CALL HANGUP or CALL HUNGUP message. CALL BUSY, CALL ABORT, CALL FAILED and CALL ERROR messages indicate events that did not succeed to start a call.
SIM_EVENT_NET. A network event of a specific type has occurred. Table keys:
SIM_EVENT_NET |
string: event type is one of:
|
SIM_EVENT_NET_STATUS |
number: network connection status (only if event type is SIM_EVENT_NET_STATUS). Positive if connected to the DHT, zero or negative if disconnected. |
SIM_EVENT_NET_PONG |
number: round-trip time in milliseconds (only if event type is SIM_EVENT_NET_PONG). Missing if not connected when pinging; zero means unknown. |
SIM_EVENT_NET_PONG_NUMBER |
number: number given to sim_contact_ping_(SIM_PING_BIT_TCP). Only if event type is SIM_EVENT_NET_PONG; missing if none. |
SIM_EVENT_NET_CONNECT |
number: error code or SIM_OK if successful (only if event type is SIM_EVENT_NET_CONNECT). |
SIM_EVENT_NET_CONNECT_ENCRYPT |
string: name of encrypt cipher (only if event type is SIM_EVENT_NET_CONNECT). |
SIM_EVENT_NET_CONNECT_DECRYPT |
string: name of decrypt cipher (only if event type is SIM_EVENT_NET_CONNECT). |
SIM_EVENT_NET_DISCONNECT |
number: error code or SIM_OK if normally disconnected (only if event type is SIM_EVENT_NET_DISCONNECT). |
SIM_EVENT_NET_TRAVERSE |
number: error code on failed NAT traversal or SIM_OK if connection no longer relayed (only if event type is SIM_EVENT_NET_TRAVERSE). |
SIM_EVENT_NET_TRAVERSE_STATUS |
string: traversal status (only if event type is SIM_EVENT_NET_TRAVERSE). |
SIM_EVENT_NET_DEVICE_INPUT |
string: name of invalid audio.input (only if event type is SIM_EVENT_NET_DEVICE). Missing if valid. |
SIM_EVENT_NET_DEVICE_OUTPUT |
string: name of invalid audio.output (only if event type is SIM_EVENT_NET_DEVICE). Missing if valid. |
SIM_EVENT_NET_DEVICE_RING |
string: name of invalid audio.ring (only if event type is SIM_EVENT_NET_DEVICE). Missing if valid. |
SIM_EVENT_ERROR. An asynchronous error (that cannot be returned by an API function) has occurred. One or two table keys:
SIM_EVENT_ERROR |
string: event type is one of:
|
SIM_EVENT_ERROR_SOCKS |
number: socket error code (only if event type is SIM_EVENT_ERROR_SOCKS). |
SIM_EVENT_ERROR_SYSTEM |
string: name of the operating system (only if event type is SIM_EVENT_ERROR_SYSTEM). Missing if system is secure. |
SIM_EVENT_ERROR_CIPHER |
string: name of the undesired cipher (only if event type is SIM_EVENT_ERROR_CIPHER). Missing if cipher is desired. |
SIM_EVENT_ERROR_PROTOCOL |
number: crypto error code (SIM_CRYPT_BAD_TABLE, SIM_CRYPT_RSA_DECRYPT or SIM_CRYPT_RSA_VERIFY). Only if event type is SIM_EVENT_ERROR_PROTOCOL. |
SIM_EVENT_ERROR_XFER |
number: file transfer error code. Only if event type is SIM_EVENT_ERROR_XFER. |
SIM_EVENT_ERROR_AUDIO |
number: audio error code. Only if event type is SIM_EVENT_ERROR_AUDIO. |
SIM_EVENT_ERROR_INIT_LOCAL |
number: socket connect error code or SIM_SERVER_PORT. Only if event type is SIM_EVENT_ERROR_INIT_LOCAL. |
SIM_EVENT_ERROR_FILE_CREATE |
number: file error code. Only if event type is SIM_EVENT_ERROR_FILE_CREATE. |
SIM_EVENT_ERROR_FILE_OPEN |
number: file error code. Only if event type is SIM_EVENT_ERROR_FILE_OPEN. |
SIM_EVENT_ERROR_FILE_CLOSE |
number: file error code. Only if event type is SIM_EVENT_ERROR_FILE_CLOSE. |
SIM_EVENT_ERROR_FILE_LOAD |
number: file error code. Only if event type is SIM_EVENT_ERROR_FILE_LOAD. |
SIM_EVENT_ERROR_FILE_SAVE |
number: file error code. Only if event type is SIM_EVENT_ERROR_FILE_SAVE. |
SIM_EVENT_ERROR_FILE_WRITE |
number: file error code. Only if event type is SIM_EVENT_ERROR_FILE_WRITE. |
SIM_EVENT_ERROR_FILE_DELETE |
number: file error code. Only if event type is SIM_EVENT_ERROR_FILE_DELETE. |
SIM_EVENT_ERROR_FILE_WIPE |
number: file error code. Only if event type is SIM_EVENT_ERROR_FILE_WIPE. |
SIM_EVENT_ERROR_FILE_RENAME |
number: file error code. Only if event type is SIM_EVENT_ERROR_FILE_RENAME. |
SIM_EVENT_ERROR_FILE_COPY |
number: file error code. Only if event type is SIM_EVENT_ERROR_FILE_COPY. |
SIM_EVENT_LOG. A new log line has been emitted; id is zero. Table key:
SIM_CMD_MSG_TEXT |
string: log line in UTF-8 encoding (not including a terminating newline character). |
SIM_EVENT_TICK. A CPU-intensive operation is taking place. One optional table key:
SIM_EVENT_TICK |
string: identifies the function which sent the event. |
When handling events, do not assume that the only table keys are the ones listed here. Also, a contact id which is always zero or always non-zero in the current version may change in future versions; always ensure it is not zero before you use it.
Event handlers (except for SIM_EVENT_FATAL and SIM_EVENT_TICK) should not block for a long time.
Return string which describes a given error code; the string is valid until the next call to the same function and CANNOT be freed by the caller.
A second invocation of sim_error_get might not report the same error string any more; if you need to call sim_error_get twice for the same error, call sim_error_peek instead of sim_error_get the first time.
These two functions return a pointer to a static buffer that contains the error string, so they are NOT thread-safe; you can call them only from a single thread of your choice.
Works just like sim_error_get or sim_error_peek but may use a user-supplied output buffer for the error string. It should be used when getting an error string from any other thread (not the thread of choice for sim_error_get and sim_error_peek). You can use sim_error_get_buffer with a single static output buffer in event handlers, as they are serialized.
Note the error string is available as a return value, which may or may not point to the user-supplied buffer.
Work like malloc() and free() but may provide debug facilities. It is explicitly allowed to call sim_free with a NULL pointer and sim_new with a zero size. The same length given to sim_new should be given to sim_free; the latter overwrites the specified number of bytes with zeros before releasing memory.
To allow memory leak debugging, it is advisable to use sim_new and sim_free for all memory allocation needs. Do not give pointers returned by sim_new to free() or realloc(), and do not give pointers returned by standard library functions to sim_free.
simtype sim_convert_utf_to_int(const char *string, unsigned *length);
simtype sim_convert_int_to_utf(const int *string, unsigned *length);
simtype sim_convert_utf_to_ucs(const char *string, unsigned *length);
simtype sim_convert_ucs_to_utf(const unsigned short *string, unsigned *length);
Convert string from UTF-8 to UTF-32/UTF-16 or vice versa. length is an input-output argument; its value does not count the terminating zero. Return value points to the converted string, which the caller must release by calling sim_string_free. Return sim_nil (and zero *length) in case of an error (input does not have a valid encoding).
To access the returned string, cast sim_get_pointer with the returned simtype according to the following table:
function |
return cast |
input *length |
output *length |
---|---|---|---|
sim_convert_utf_to_int |
int * |
number of bytes |
number of unicode characters |
sim_convert_int_to_utf |
char * |
number of unicode characters |
number of bytes |
sim_convert_utf_to_ucs |
unsigned short * |
number of bytes |
number of UTF-16 shorts |
sim_convert_ucs_to_utf |
char * |
number of UTF-16 shorts |
number of bytes |
Note sim_convert_ucs_to_utf accepts both little- and big-endian formats for any surrogate pairs of the input string, but if sim_convert_utf_to_ucs has to output surrogate pairs, they are in little-endian format ("trail" surrogate comes first, followed by "lead" surrogate). These two functions are mostly useful on Windows.
Return an ASCII string in "JSON" format, which has the contents of a given simtype (caller has to call sim_string_free to release the returned value when done with it). Works only with simtypes that do not contain binary data (only UTF-8 text, and no NUL characters), such as ones returned by API functions. Trying to convert a simtype that contains binary strings results in an "undefined" return value (binary strings are truncated at the first character that cannot be interpreted as UTF-8) but note sim_nil may be returned if the given simtype is completely invalid.
Return an UTF-8 string in "XML" format, which has the contents of a given simtype or sim_nil if the simtype cannot be converted. If tag is NULL, no outer XML tag is written. Like sim_convert_type_to_json, it works only with simtypes that contain only UTF-8 text, but this is not checked and so binary simtypes would result in bad XML output. ASCII characters not valid in XML are replaced with spaces; the '&', '<' and '>' characters are escaped, as required by XML. Caller has to call sim_string_free to release the returned value when done with it.
Return number of milliseconds elapsed since an undefined but unchanging point in time (useful for programming of timeouts).
The logger can output messages to the console, log file, and memory buffer. Each line has a:
Module name NULL outputs messages only to the console or memory.
Log an UTF-8 message from the specified module at specified level.
void log_xtra_(const char *module, const char *format, ...);
void log_debug_(const char *module, const char *format, ...);
void log_info_(const char *module, const char *format, ...);
void log_note_(const char *module, const char *format, ...);
void log_warn_(const char *module, const char *format, ...);
void log_error_(const char *module, const char *format, ...);
void log_fatal_(const char *module, int error, const char *format, ...);
Log a message from the specified module at specific level.
For all logger functions, format is the same kind of format string that would be used with printf(). Use %lld for logging of simnumber and %llu for logging of simunsigned (even on Windows). The logged message can contain a newline character at the end but not anywhere else.
For log_fatal_, error must be SIM_OK.
Read logged message with the given index. Indexes start at 1 and are guaranteed to remain the same until either sim_console_load_ or sim_exit_user_ is called.
The returned value is sim_nil if the given index is invalid or else it is a SIMPOINTER containing UTF-8 text; the data is guaranteed to remain accessible until either sim_console_load_ or sim_exit_user_ is called. Also returns time of the message as *datetime, which may be zero if message time is not available. The returned *loglevel is NULL if message has been emitted by the console.
Return the highest log message index that can currently be given as an argument to log_get_string_. More log messages may arrive at any time.
Load the last number of messages from the log file (and unload all others). To clear the log buffer, call with zero number. To load the whole log file into the buffer, call with a very large number.
sim_console_load_ might block for a long time if there are many messages in the log file.
Execute a command as if it was typed at the console. If this function returns SIM_CONSOLE_QUIT_CMD, you should close the console immediately. Else it returns either SIM_OK or error code returned by the command. There is no guaranteed backwards-compatibility of console commands between different versions, so please refrain from using this function with predefined or automatically generated commands. It is meant to execute commands entered by a human user.
The library has three main states:
Additionally, there are four locking levels:
The following table presents an overview of the states and locking levels in which each of the API functions can or cannot be called, as well as whether the functions are thread-safe.
functions |
NONE |
OUT |
IN |
LOCK |
LOG |
FATAL |
thread-safe |
---|---|---|---|---|---|---|---|
sim_init_ |
YES |
- |
- |
NO |
NO |
NO |
NO |
sim_init_user_ |
NO |
YES |
- |
NO |
NO |
NO |
YES |
sim_exit_user_ |
NO |
- |
YES |
NO |
NO |
NO |
YES |
sim_exit_ |
- |
YES |
YES |
NO |
NO |
NO |
NO |
sim_key_generate_ |
NO |
YES |
- |
NO |
NO |
NO |
YES |
sim_key_set_password_ |
NO |
- |
YES |
NO |
NO |
NO |
YES |
sim_event_register_ |
NO |
YES |
YES |
NO |
NO |
NO |
YES |
sim_contact_xxx_, sim_status_xxx_ |
NO |
- |
YES |
NO |
NO |
NO |
YES |
sim_msg_xxx_, sim_xfer_xxx_ |
NO |
- |
YES |
NO |
NO |
NO |
YES |
sim_audio_xxx_, sim_sound_xxx_ |
NO |
YES |
YES |
NO |
NO |
NO |
YES |
sim_param_xxx_, sim_list_info_ |
NO |
YES |
YES |
NO |
NO |
NO |
YES |
sim_console_xxx_ |
NO |
YES |
YES |
NO |
NO |
NO |
YES |
sim_list_versions, sim_list_free |
YES |
YES |
YES |
YES |
YES |
NO |
YES |
sim_error_get, sim_error_peek |
YES |
YES |
YES |
YES |
YES |
YES |
NO |
sim_error_get_buffer, sim_get_tick |
YES |
YES |
YES |
YES |
YES |
YES |
YES |
sim_new, sim_convert_xxx |
YES |
YES |
YES |
YES |
YES |
NO |
YES |
sim_free |
YES |
YES |
YES |
YES |
YES |
YES |
YES |
sim_string_xxx, sim_pointer_xxx |
YES |
YES |
YES |
YES |
YES |
YES |
YES |
sim_number_xxx, sim_nil |
YES |
YES |
YES |
YES |
YES |
YES |
YES |
sim_get_length, sim_get_type |
YES |
YES |
YES |
YES |
YES |
YES |
YES |
sim_table_xxx, sim_array_xxx |
YES |
YES |
YES |
YES |
YES |
YES |
YES |
log_xxx_ |
- |
YES |
YES |
YES |
NO |
NO |
YES |
The cells of the above table have the following meanings: