sml_notification.c
/*
* libsyncml - A syncml protocol implementation
* Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
* Copyright (C) 2007-2009 Michael Bell <michael.bell@opensync.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "syncml.h"
#include "syncml_internals.h"
#include "sml_notification_internals.h"
#include "sml_transport_internals.h"
#include "sml_command_internals.h"
#include "sml_manager_internals.h"
#include "sml_error_internals.h"
#include "objects/sml_auth_internals.h"
#include "sml_elements_internals.h"
SmlNotification *smlNotificationNew(SmlNotificationVersion version, SmlNotificationUIMode mode, SmlNotificationInitiator init, unsigned int sessionID, const char *identifier, const char *target, SmlMimeType type, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%i, %i, %i, %i, %s, %s, %i, %p)", __func__, version, mode, init, sessionID, VA_STRING(identifier), VA_STRING(target), type, error);
CHECK_ERROR_REF
SmlNotification *san = smlTryMalloc0(sizeof(SmlNotification), error);
if (!san)
goto error;
san->version = version;
san->mode = mode;
san->init = init;
san->sessionID = sessionID;
san->identifier = g_strdup(identifier);
san->type = type;
san->cred = NULL;
san->sessionType = SML_SESSION_TYPE_SERVER;
san->manager = NULL;
san->target = smlLocationNew(target, NULL, error);
if (!san->target)
goto error_free_san;
smlTrace(TRACE_EXIT, "%s: %p", __func__, san);
return san;
error_free_san:
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
void smlNotificationSetCred(SmlNotification *san, SmlCred *cred)
{
smlTrace(TRACE_ENTRY, "%s", __func__);
smlAssert(san);
smlAssert(cred);
if (san->cred)
smlCredUnref(san->cred);
san->cred = cred;
// we only set credentials if this is a client
// if we set no credentials then we always assume a server
// FIXME: this is a hack because the original implementation
// FIXME: only support server
// Only one question which client sends a notification!?
san->sessionType = SML_SESSION_TYPE_CLIENT;
smlCredRef(cred);
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlNotificationSetManager(SmlNotification *san, SmlManager *manager)
{
smlTrace(TRACE_ENTRY, "%s", __func__);
smlAssert(san);
smlAssert(manager);
san->manager = manager;
smlTrace(TRACE_EXIT, "%s", __func__);
}
void smlNotificationFree(SmlNotification *san)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, san);
smlAssert(san);
while (san->alerts) {
SmlSanAlert *alert = san->alerts->data;
san->alerts = g_list_remove(san->alerts, alert);
smlNotificationFreeAlert(alert);
}
if (san->target) {
smlLocationUnref(san->target);
san->target = NULL;
}
if (san->cred) {
smlCredUnref(san->cred);
san->cred = NULL;
}
if (san->identifier)
smlSafeCFree(&(san->identifier));
smlSafeFree((gpointer *)&san);
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlBool smlNotificationNewAlert(SmlNotification *san, SmlAlertType type, const char *contenttype, const char *serverURI, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, error);
CHECK_ERROR_REF
smlAssert(type >= 206 && type <= 210);
SmlSanAlert *alert = smlTryMalloc0(sizeof(SmlSanAlert), error);
if (!alert)
goto error;
alert->type = type;
alert->contenttype = g_strdup(contenttype);
alert->serverURI = g_strdup(serverURI);
san->alerts = g_list_append(san->alerts, alert);
smlTrace(TRACE_EXIT, "%s: %p", __func__, alert);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
void smlNotificationFreeAlert(SmlSanAlert *alert)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, alert);
smlAssert(alert);
smlSafeCFree(&(alert->contenttype));
smlSafeCFree(&(alert->serverURI));
smlSafeFree((gpointer *)&alert);
smlTrace(TRACE_EXIT, "%s", __func__);
}
SmlNotification *smlNotificationParse(const char *data, unsigned int size, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, data, size, error);
CHECK_ERROR_REF
smlAssert(data);
smlAssert(size);
if (size < 25) {
smlErrorSet(error, SML_ERROR_GENERIC, "Size too small");
goto error;
}
unsigned int idLength = (uint8_t)data[23];
if (size < (25 + idLength)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Size too small2");
goto error;
}
SmlNotification *san = smlTryMalloc0(sizeof(SmlNotification), error);
if (!san)
goto error;
san->type = SML_MIMETYPE_SAN;
/* Version */
unsigned int version = ((uint8_t)data[16]) << 2;
version = version | ((uint8_t)data[17]) >> 6;
switch (version) {
case 12:
san->version = SML_SAN_VERSION_12;
break;
default:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown version");
goto error_free_san;
}
/* ui mode */
san->mode = (((uint8_t)data[17]) & 0x30) >> 4;
/* initiator */
san->init = (((uint8_t)data[17]) & 0x08) >> 3;
/* session id */
san->sessionID = ((uint8_t)data[21]) << 8;
san->sessionID = san->sessionID | (uint8_t)data[22];
/* server identifier */
if (idLength) {
/* the trailing NULL byte is usually not present in the SAN */
san->identifier = smlTryMalloc0(idLength + 1, error);
if (!san->identifier)
goto error_free_san;
memcpy(san->identifier, data + 24, idLength);
}
unsigned int numSync = ((uint8_t)data[24 + idLength]) >> 4;
data += 25 + idLength;
unsigned int alertLength = 25 + idLength;
unsigned int i = 0;
for (i = 0; i < numSync; i++) {
if (size < alertLength + 5) {
smlErrorSet(error, SML_ERROR_GENERIC, "Size too small3");
goto error_free_san;
}
idLength = (uint8_t)data[4];
if (size < (alertLength + 5 + idLength)) {
smlErrorSet(error, SML_ERROR_GENERIC, "Size too small4");
goto error_free_san;
}
alertLength = alertLength + 5 + idLength;
/* Alert type + future */
SmlAlertType alert_type = (((uint8_t)data[0]) >> 4) + 200;
if (alert_type < 206 || alert_type > 210) {
smlErrorSet(error, SML_ERROR_GENERIC, "Wrong alert type");
goto error_free_san;
}
unsigned int contenttype = ((uint8_t)data[1]) << 16;
contenttype = contenttype | ((uint8_t)data[2]) << 8;
contenttype = contenttype | ((uint8_t)data[3]);
/* The contenttype */
const char *alert_ct;
switch (contenttype) {
case 0x00:
/* This is exactly like defined in "DS Protocol" section
* 12.4.13 "Content Type" of the official OMA document
* OMA-TS-DS_Protocol-V1_2_1-20070810-A.pdf.
*/
alert_ct = NULL;
break;
case 0x07:
alert_ct = SML_ELEMENT_TEXT_VCARD;
break;
case 0x06:
alert_ct = SML_ELEMENT_TEXT_VCAL;
break;
case 0x03:
alert_ct = SML_ELEMENT_TEXT_PLAIN;
break;
case 0x0305:
alert_ct = SML_ELEMENT_TEXT_ICAL;
break;
case 0x0306:
alert_ct = SML_ELEMENT_APPLICATION_OMA_DS_EMAIL;
break;
case 0x0307:
alert_ct = SML_ELEMENT_APPLICATION_OMA_DS_FILE;
break;
case 0x0308:
alert_ct = SML_ELEMENT_APPLICATION_OMA_DS_FOLDER;
break;
case 0x0309:
alert_ct = SML_ELEMENT_TEXT_VCARD_30;
break;
default:
/* http://www.openmobilealliance.org/Tech/omna/omna-wsp-content-type.aspx */
smlErrorSet(error, SML_ERROR_GENERIC, "The OMNA WSP Content Type Number 0x%xd is not supported.", contenttype);
goto error_free_san;
}
/* The server uri */
char *alert_uri;
if (idLength) {
/* the trailing NULL byte is usually not present in the SAN */
alert_uri = smlTryMalloc0(idLength + 1, error);
if (!alert_uri)
goto error_free_san;
memcpy(alert_uri, data + 5, idLength);
}
data += 5 + idLength;
/* Create new alert */
if (!smlNotificationNewAlert(san, alert_type, alert_ct, alert_uri, error))
goto error_free_san;
/* Cleanup */
if (idLength)
smlSafeCFree(&alert_uri);
}
if (size > alertLength) {
smlErrorSet(error, SML_ERROR_GENERIC, "Extra chunk at the end of the buffer");
goto error_free_san;
}
smlTrace(TRACE_EXIT, "%s: %p", __func__, san);
return san;
error_free_san:
while (san->alerts) {
smlNotificationFreeAlert(san->alerts->data);
san->alerts = g_list_delete_link(san->alerts, san->alerts);
}
if (san->identifier)
smlSafeCFree(&(san->identifier));
smlSafeFree((gpointer *)&san);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlBool smlNotificationAssemble11(SmlNotification *san, char **data, unsigned int *size, SmlProtocolVersion version, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %i, %p)", __func__, san, data, size, version, error);
CHECK_ERROR_REF
smlAssert(san);
smlAssert(data);
smlAssert(size);
/* If a notification is sent via a normal SyncML message
* then the used session must be managed to guarantee the
* consistency of the session.
*/
smlAssert(san->manager);
SmlAssembler *assm = NULL;
SmlLocation *source = NULL;
SmlSession *session = NULL;
SmlSanAlert *alert = NULL;
SmlCommand *cmd = NULL;
source = smlLocationNew(san->identifier, NULL, error);
if (!source)
goto error;
char *sessionString = smlManagerGetNewSessionID(san->manager);
session = smlSessionNew(san->sessionType, san->type, version, SML_PROTOCOL_SYNCML, san->target, source, sessionString, 0, error);
if (!session) {
smlSafeCFree(&sessionString);
smlLocationUnref(source);
goto error;
}
smlSafeCFree(&sessionString);
smlManagerSessionAdd(san->manager, session, NULL, error);
assm = smlAssemblerNew(san->type, 0, error);
if (!assm)
goto error;
if (!smlAssemblerStart(assm, session, error))
goto error;
if (!smlAssemblerAddHeader(assm, session, error))
goto error;
GList *a = NULL;
int i = 1;
for (a = san->alerts; a; a = a->next) {
alert = a->data;
SmlLocation *loc = smlLocationNew(alert->serverURI, NULL, error);
if (!loc)
goto error;
cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY_BY_SERVER, NULL, loc, NULL, NULL, alert->contenttype, error);
if (!cmd) {
smlLocationUnref(loc);
goto error;
}
cmd->cmdID = i;
i++;
if (!smlAssemblerStartCommand(assm, NULL, cmd, error) == SML_ASSEMBLER_RESULT_OK)
goto error;
if (!smlAssemblerEndCommand(assm, NULL, error))
goto error;
smlLocationUnref(loc);
smlCommandUnref(cmd);
}
if (!smlAssemblerRun(assm, data, size, NULL, TRUE, error))
goto error;
smlAssemblerFlush(assm);
smlSessionUnref(session);
smlLocationUnref(source);
smlAssemblerFree(assm);
char *hex = smlPrintHex(*data, *size);
smlTrace(TRACE_INTERNAL, "San packet assembled: %s", hex);
smlSafeCFree(&hex);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
if (cmd)
smlCommandUnref(cmd);
if (session)
smlSessionUnref(session);
if (assm)
smlAssemblerFree(assm);
*data = NULL;
*size = 0;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlBool _smlNotificationAssemble12(SmlNotification *san, char **data, unsigned int *size, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, san, data, size, error);
CHECK_ERROR_REF
smlAssert(san);
smlAssert(data);
smlAssert(*data == NULL);
smlAssert(size);
unsigned int numsync = 0;
SmlSanAlert *alert = NULL;
/* Calculate the length of the san data */
unsigned int length = 16 /* Digest */ + 8 /* Hdr */ + strlen(san->identifier) /* Identifier */ + 1 /* num-sync + future */;
GList *a = NULL;
for (a = san->alerts; a; a = a->next) {
alert = a->data;
length += 5 + strlen(alert->serverURI);
numsync++;
}
/* Malloc the necessary space */
char *buffer = smlTryMalloc0(length, error);
if (!buffer)
goto error;
*data = buffer;
*size = length;
/* the digest must be created at the end */
/* Create Header */
/* Version - 10 bit */
buffer[16] = 0x03;
/* ui mode */
buffer[17] = buffer[17] | (san->mode << 4);
/* initiator */
buffer[17] = buffer[17] | (san->init << 3);
/* future use is 0 for now - 27 bit */
/* session id */
buffer[21] = san->sessionID >> 8;
buffer[22] = san->sessionID;
/* server identifier without trailing NULL byte */
buffer[23] = strlen(san->identifier);
strncpy(buffer + 24, san->identifier, strlen(san->identifier));
buffer += 24 + strlen(san->identifier);
/* Num syncs + future */
buffer[0] = numsync << 4;
buffer++;
for (a = san->alerts; a; a = a->next) {
alert = a->data;
/* Alert type + future */
buffer[0] = (alert->type - 200) << 4;
/* The contenttype */
if (!strcmp(alert->contenttype, SML_ELEMENT_TEXT_VCARD)) {
buffer[3] = 0x07;
} else if (!strcmp(alert->contenttype, SML_ELEMENT_TEXT_VCAL)) {
buffer[3] = 0x06;
} else if (!strcmp(alert->contenttype, SML_ELEMENT_TEXT_PLAIN)) {
buffer[3] = 0x03;
} else if (!strcmp(alert->contenttype, SML_ELEMENT_TEXT_ICAL)) {
buffer[2] = 0x03;
buffer[3] = 0x05;
} else if (!strcmp(alert->contenttype, SML_ELEMENT_APPLICATION_OMA_DS_EMAIL)) {
buffer[2] = 0x03;
buffer[3] = 0x06;
} else if (!strcmp(alert->contenttype, SML_ELEMENT_APPLICATION_OMA_DS_FILE)) {
buffer[2] = 0x03;
buffer[3] = 0x07;
} else if (!strcmp(alert->contenttype, SML_ELEMENT_APPLICATION_OMA_DS_FOLDER)) {
buffer[2] = 0x03;
buffer[3] = 0x08;
} else if (!strcmp(alert->contenttype, SML_ELEMENT_TEXT_VCARD_30)) {
buffer[2] = 0x03;
buffer[3] = 0x09;
} else {
/* http://www.openmobilealliance.org/Tech/omna/omna-wsp-content-type.aspx */
smlTrace(TRACE_INTERNAL,
"%s: The OMNA WSP Content Type Number is unknown for %s.",
__func__, alert->contenttype);
/* All bytes are already set to zero. */
}
/* The server uri without a trailing NULL byte */
buffer[4] = strlen(alert->serverURI);
strncpy(buffer + 5, alert->serverURI, strlen(alert->serverURI));
buffer += 5 + strlen(alert->serverURI);
}
/* Create the digest */
/* DO NOT USE buffer because this pointer was manipulated
* ALWAYS USE *data.
*/
/* base64 encoded MD5 digest of the notification */
unsigned char digest[16];
smlMD5GetDigest (*data+16, length - 16 /* digest */, digest);
char *b64_san = g_base64_encode(digest, 16);
smlTrace(TRACE_INTERNAL, "%s: b64(md5(san)) ::= %s", __func__, b64_san);
/* base64 encoded MD5 digest of username:password */
const char *username = "";
const char *password = "";
if (san->cred) {
username = san->cred->username;
password = san->cred->password;
}
char *auth = g_strdup_printf("%s:%s", username, password);
smlMD5GetDigest (auth, strlen(auth), digest);
smlSafeCFree(&auth);
char *b64_auth = g_base64_encode(digest, 16);
smlTrace(TRACE_INTERNAL, "%s: b64(md5(username:password)) ::= %s", __func__, b64_auth);
/* MD5 digest of auth:nonce:notification (empty nonce) */
char *complete = g_strdup_printf("%s::%s", b64_auth, b64_san);
smlSafeCFree(&b64_auth);
smlSafeCFree(&b64_san);
smlMD5GetDigest (complete, strlen(complete), digest);
smlSafeCFree(&complete);
smlTrace(TRACE_INTERNAL, "%s: md5(auth::san) complete", __func__);
/* copy digest to buffer */
memcpy(*data, digest, 16);
smlTrace(TRACE_INTERNAL, "%s: md5(auth::san) copied to san", __func__);
/* debug */
char *hex = smlPrintHex(*data, *size);
smlTrace(TRACE_INTERNAL, "San packet assembled: %s", hex);
smlSafeCFree(&hex);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
if (*data)
smlSafeCFree(data);
*size = 0;
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlBool smlNotificationAssemble(SmlNotification *san, char **data, unsigned int *size, SmlError **error)
{
CHECK_ERROR_REF
switch (san->version) {
case SML_SAN_VERSION_12:
return _smlNotificationAssemble12(san, data, size, error);
case SML_SAN_VERSION_10:
return smlNotificationAssemble11(san, data, size, SML_VERSION_10, error);
case SML_SAN_VERSION_11:
return smlNotificationAssemble11(san, data, size, SML_VERSION_11, error);
case SML_SAN_VERSION_UNKNOWN:
smlErrorSet(error, SML_ERROR_GENERIC, "Unknown SAN version.");
break;
}
return FALSE;
}
SmlBool smlNotificationSend(SmlNotification *san, SmlTransport *tsp, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, san, tsp, error);
CHECK_ERROR_REF
smlAssert(san);
smlAssert(tsp);
char *data = NULL;
unsigned int size = 0;
if (!smlNotificationAssemble(san, &data, &size, error))
goto error;
SmlTransportData *tspdata = NULL;
if (san->version == SML_SAN_VERSION_12)
tspdata = smlTransportDataNew(data, size, SML_MIMETYPE_SAN, TRUE, error);
else
tspdata = smlTransportDataNew(data, size, san->type, TRUE, error);
if (!tspdata)
goto error_free_data;
/* Do not free data because smlTransportDateNew does copy by reference. */
data = NULL;
/* For SyncML 1.2 and OBEX Transport we use SML_MIMETYPE_SAN for PUT and use the regular
SyncML message type (xml/wbxml) for GET. To avoid that we response after the PUT with
a GET SML_MIMETYPE_SAN request. */
if (san->version == SML_SAN_VERSION_12)
tspdata->type_get = san->type;
if (!smlTransportSend(tsp, NULL, tspdata, error))
goto error_free_tspdata;
smlTransportDataDeref(tspdata);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_tspdata:
smlTransportDataDeref(tspdata);
error_free_data:
smlSafeCFree(&data);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlNotificationVersion smlNotificationGetVersion(SmlNotification *san)
{
smlAssert(san);
return san->version;
}
SmlNotificationUIMode smlNotificationGetMode(SmlNotification *san)
{
smlAssert(san);
return san->mode;
}
SmlNotificationInitiator smlNotificationGetInitiator(SmlNotification *san)
{
smlAssert(san);
return san->init;
}
unsigned int smlNotificationGetSessionID(SmlNotification *san)
{
smlAssert(san);
return san->sessionID;
}
const char *smlNotificationGetIdentifier(SmlNotification *san)
{
smlAssert(san);
return san->identifier;
}
unsigned int smlNotificationNumAlerts(SmlNotification *san)
{
smlAssert(san);
return g_list_length(san->alerts);
}
SmlSanAlert *smlNotificationNthAlert(SmlNotification *san, unsigned int nth)
{
smlAssert(san);
return g_list_nth_data(san->alerts, nth);
}
SmlAlertType smlSanAlertGetType(SmlSanAlert *alert)
{
smlAssert(alert);
return alert->type;
}
const char *smlSanAlertGetContentType(SmlSanAlert *alert)
{
smlAssert(alert);
return alert->contenttype;
}
const char *smlSanAlertGetServerURI(SmlSanAlert *alert)
{
smlAssert(alert);
return alert->serverURI;
}
smlAssemblerAddHeader
SmlBool smlAssemblerAddHeader(SmlAssembler *assm, SmlSession *session, SmlError **error)
Assembles the header.
Definition: sml_parse.c:762
SmlNotification
Definition: sml_notification_internals.h:33
SmlCred
Definition: sml_elements_internals.h:87
smlErrorPrint
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
SmlAssembler
Definition: sml_parse_internals.h:33
smlPrintHex
char * smlPrintHex(const char *data, int len)
Used for printing binary data in just hex.
Definition: sml_support.c:271
smlAssemblerStartCommand
SmlAssemblerResult smlAssemblerStartCommand(SmlAssembler *assm, SmlCommand *parent, SmlCommand *cmd, SmlError **error)
Starts a parent command.
Definition: sml_parse.c:659
SmlManager
Definition: sml_manager_internals.h:35
SmlTransport
Definition: sml_transport_internals.h:69
smlAssemblerRun
SmlBool smlAssemblerRun(SmlAssembler *assm, char **data, unsigned int *size, SmlBool *end, SmlBool final, SmlError **error)
Assembles the complete message and returns the result.
Definition: sml_parse.c:456
smlAssemblerFlush
unsigned int smlAssemblerFlush(SmlAssembler *assm)
Flushes the already parsed commands.
Definition: sml_parse.c:514
smlAssemblerStart
SmlBool smlAssemblerStart(SmlAssembler *assm, SmlSession *session, SmlError **error)
Starts a new message.
Definition: sml_parse.c:428
SmlTransportData
Definition: sml_transport_internals.h:112
SmlSession
Definition: sml_session_internals.h:27
SmlCommand
Definition: sml_command_internals.h:73
smlTrace
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
smlAssemblerFree
void smlAssemblerFree(SmlAssembler *assm)
Frees a assembler.
Definition: sml_parse.c:402
smlAssemblerEndCommand
SmlBool smlAssemblerEndCommand(SmlAssembler *assm, SmlCommand *parent, SmlError **error)
Ends a parent command.
Definition: sml_parse.c:722
SmlLocation
Definition: sml_elements_internals.h:40
SmlTransportData::type_get
SmlMimeType type_get
Definition: sml_transport_internals.h:132
SmlSanAlert
Definition: sml_notification_internals.h:50
smlAssemblerNew
SmlAssembler * smlAssemblerNew(SmlMimeType type, unsigned int limit, SmlError **error)
Creates a new assembler.
Definition: sml_parse.c:352
smlSessionNew
SmlSession * smlSessionNew(SmlSessionType sessionType, SmlMimeType mimetype, SmlProtocolVersion version, SmlProtocolType protocol, SmlLocation *target, SmlLocation *source, const char *sessionID, unsigned int messageID, SmlError **error)
Creates a new session.
Definition: sml_session.c:1832
smlTryMalloc0
void * smlTryMalloc0(long n_bytes, SmlError **error)
Safely mallocs.
Definition: sml_support.c:335
smlErrorSet
void smlErrorSet(SmlError **error, SmlErrorType type, const char *format,...)
Sets the error.
Definition: sml_error.c:355
SmlError
Represent an error.
Definition: sml_error_internals.h:29