libsyncml 0.5.4

- Main Page
- Modules
- Data Structures
- Files

- File List

sml_notification.c
1 /*
2 * libsyncml - A syncml protocol implementation
3 * Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
4 * Copyright (C) 2007-2009 Michael Bell <michael.bell@opensync.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22 #include "syncml.h"
23
24 #include "syncml_internals.h"
25 #include "sml_notification_internals.h"
26 #include "sml_transport_internals.h"
27 #include "sml_command_internals.h"
28 #include "sml_manager_internals.h"
29 #include "sml_error_internals.h"
30 #include "objects/sml_auth_internals.h"
31 #include "sml_elements_internals.h"
32
33 SmlNotification *smlNotificationNew(SmlNotificationVersion version, SmlNotificationUIMode mode, SmlNotificationInitiator init, unsigned int sessionID, const char *identifier, const char *target, SmlMimeType type, SmlError **error)
34 {
35 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);
36 CHECK_ERROR_REF
37
38 SmlNotification *san = smlTryMalloc0(sizeof(SmlNotification), error);
39 if (!san)
40 goto error;
41
42 san->version = version;
43 san->mode = mode;
44 san->init = init;
45 san->sessionID = sessionID;
46 san->identifier = g_strdup(identifier);
47 san->type = type;
48 san->cred = NULL;
49 san->sessionType = SML_SESSION_TYPE_SERVER;
50 san->manager = NULL;
51
52 san->target = smlLocationNew(target, NULL, error);
53 if (!san->target)
54 goto error_free_san;
55
56 smlTrace(TRACE_EXIT, "%s: %p", __func__, san);
57 return san;
58
59 error_free_san:
60
61 error:
62 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
63 return NULL;
64 }
65
66 void smlNotificationSetCred(SmlNotification *san, SmlCred *cred)
67 {
68 smlTrace(TRACE_ENTRY, "%s", __func__);
69 smlAssert(san);
70 smlAssert(cred);
71 if (san->cred)
72 smlCredUnref(san->cred);
73 san->cred = cred;
74 // we only set credentials if this is a client
75 // if we set no credentials then we always assume a server
76 // FIXME: this is a hack because the original implementation
77 // FIXME: only support server
78 // Only one question which client sends a notification!?
79 san->sessionType = SML_SESSION_TYPE_CLIENT;
80 smlCredRef(cred);
81 smlTrace(TRACE_EXIT, "%s", __func__);
82 }
83
84 void smlNotificationSetManager(SmlNotification *san, SmlManager *manager)
85 {
86 smlTrace(TRACE_ENTRY, "%s", __func__);
87 smlAssert(san);
88 smlAssert(manager);
89 san->manager = manager;
90 smlTrace(TRACE_EXIT, "%s", __func__);
91 }
92
93 void smlNotificationFree(SmlNotification *san)
94 {
95 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, san);
96 smlAssert(san);
97
98 while (san->alerts) {
99 SmlSanAlert *alert = san->alerts->data;
100 san->alerts = g_list_remove(san->alerts, alert);
101 smlNotificationFreeAlert(alert);
102 }
103
104 if (san->target) {
105 smlLocationUnref(san->target);
106 san->target = NULL;
107 }
108
109 if (san->cred) {
110 smlCredUnref(san->cred);
111 san->cred = NULL;
112 }
113
114 if (san->identifier)
115 smlSafeCFree(&(san->identifier));
116 smlSafeFree((gpointer *)&san);
117
118 smlTrace(TRACE_EXIT, "%s", __func__);
119 }
120
121 SmlBool smlNotificationNewAlert(SmlNotification *san, SmlAlertType type, const char *contenttype, const char *serverURI, SmlError **error)
122 {
123 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, error);
124 CHECK_ERROR_REF
125 smlAssert(type >= 206 && type <= 210);
126
127 SmlSanAlert *alert = smlTryMalloc0(sizeof(SmlSanAlert), error);
128 if (!alert)
129 goto error;
130
131 alert->type = type;
132 alert->contenttype = g_strdup(contenttype);
133 alert->serverURI = g_strdup(serverURI);
134
135 san->alerts = g_list_append(san->alerts, alert);
136
137 smlTrace(TRACE_EXIT, "%s: %p", __func__, alert);
138 return TRUE;
139
140 error:
141 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
142 return FALSE;
143 }
144
145 void smlNotificationFreeAlert(SmlSanAlert *alert)
146 {
147 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, alert);
148 smlAssert(alert);
149
150 smlSafeCFree(&(alert->contenttype));
151 smlSafeCFree(&(alert->serverURI));
152 smlSafeFree((gpointer *)&alert);
153
154 smlTrace(TRACE_EXIT, "%s", __func__);
155 }
156
157 SmlNotification *smlNotificationParse(const char *data, unsigned int size, SmlError **error)
158 {
159 smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, data, size, error);
160 CHECK_ERROR_REF
161 smlAssert(data);
162 smlAssert(size);
163
164 if (size < 25) {
165 smlErrorSet(error, SML_ERROR_GENERIC, "Size too small");
166 goto error;
167 }
168
169 unsigned int idLength = (uint8_t)data[23];
170 if (size < (25 + idLength)) {
171 smlErrorSet(error, SML_ERROR_GENERIC, "Size too small2");
172 goto error;
173 }
174
175 SmlNotification *san = smlTryMalloc0(sizeof(SmlNotification), error);
176 if (!san)
177 goto error;
178 san->type = SML_MIMETYPE_SAN;
179
180 /* Version */
181 unsigned int version = ((uint8_t)data[16]) << 2;
182 version = version | ((uint8_t)data[17]) >> 6;
183 switch (version) {
184 case 12:
185 san->version = SML_SAN_VERSION_12;
186 break;
187 default:
188 smlErrorSet(error, SML_ERROR_GENERIC, "Unknown version");
189 goto error_free_san;
190 }
191
192 /* ui mode */
193 san->mode = (((uint8_t)data[17]) & 0x30) >> 4;
194
195 /* initiator */
196 san->init = (((uint8_t)data[17]) & 0x08) >> 3;
197
198 /* session id */
199 san->sessionID = ((uint8_t)data[21]) << 8;
200 san->sessionID = san->sessionID | (uint8_t)data[22];
201
202 /* server identifier */
203 if (idLength) {
204 /* the trailing NULL byte is usually not present in the SAN */
205 san->identifier = smlTryMalloc0(idLength + 1, error);
206 if (!san->identifier)
207 goto error_free_san;
208
209 memcpy(san->identifier, data + 24, idLength);
210 }
211
212 unsigned int numSync = ((uint8_t)data[24 + idLength]) >> 4;
213 data += 25 + idLength;
214 unsigned int alertLength = 25 + idLength;
215
216 unsigned int i = 0;
217 for (i = 0; i < numSync; i++) {
218 if (size < alertLength + 5) {
219 smlErrorSet(error, SML_ERROR_GENERIC, "Size too small3");
220 goto error_free_san;
221 }
222
223 idLength = (uint8_t)data[4];
224 if (size < (alertLength + 5 + idLength)) {
225 smlErrorSet(error, SML_ERROR_GENERIC, "Size too small4");
226 goto error_free_san;
227 }
228 alertLength = alertLength + 5 + idLength;
229
230 /* Alert type + future */
231 SmlAlertType alert_type = (((uint8_t)data[0]) >> 4) + 200;
232 if (alert_type < 206 || alert_type > 210) {
233 smlErrorSet(error, SML_ERROR_GENERIC, "Wrong alert type");
234 goto error_free_san;
235 }
236
237 unsigned int contenttype = ((uint8_t)data[1]) << 16;
238 contenttype = contenttype | ((uint8_t)data[2]) << 8;
239 contenttype = contenttype | ((uint8_t)data[3]);
240
241 /* The contenttype */
242 const char *alert_ct;
243 switch (contenttype) {
244 case 0x00:
245 /* This is exactly like defined in "DS Protocol" section
246 * 12.4.13 "Content Type" of the official OMA document
247 * OMA-TS-DS_Protocol-V1_2_1-20070810-A.pdf.
248 */
249 alert_ct = NULL;
250 break;
251 case 0x07:
252 alert_ct = SML_ELEMENT_TEXT_VCARD;
253 break;
254 case 0x06:
255 alert_ct = SML_ELEMENT_TEXT_VCAL;
256 break;
257 case 0x03:
258 alert_ct = SML_ELEMENT_TEXT_PLAIN;
259 break;
260 case 0x0305:
261 alert_ct = SML_ELEMENT_TEXT_ICAL;
262 break;
263 case 0x0306:
264 alert_ct = SML_ELEMENT_APPLICATION_OMA_DS_EMAIL;
265 break;
266 case 0x0307:
267 alert_ct = SML_ELEMENT_APPLICATION_OMA_DS_FILE;
268 break;
269 case 0x0308:
270 alert_ct = SML_ELEMENT_APPLICATION_OMA_DS_FOLDER;
271 break;
272 case 0x0309:
273 alert_ct = SML_ELEMENT_TEXT_VCARD_30;
274 break;
275 default:
276 /* http://www.openmobilealliance.org/Tech/omna/omna-wsp-content-type.aspx */
277 smlErrorSet(error, SML_ERROR_GENERIC, "The OMNA WSP Content Type Number 0x%xd is not supported.", contenttype);
278 goto error_free_san;
279 }
280
281 /* The server uri */
282 char *alert_uri;
283 if (idLength) {
284 /* the trailing NULL byte is usually not present in the SAN */
285 alert_uri = smlTryMalloc0(idLength + 1, error);
286 if (!alert_uri)
287 goto error_free_san;
288
289 memcpy(alert_uri, data + 5, idLength);
290 }
291 data += 5 + idLength;
292
293 /* Create new alert */
294 if (!smlNotificationNewAlert(san, alert_type, alert_ct, alert_uri, error))
295 goto error_free_san;
296
297 /* Cleanup */
298 if (idLength)
299 smlSafeCFree(&alert_uri);
300 }
301
302 if (size > alertLength) {
303 smlErrorSet(error, SML_ERROR_GENERIC, "Extra chunk at the end of the buffer");
304 goto error_free_san;
305 }
306
307 smlTrace(TRACE_EXIT, "%s: %p", __func__, san);
308 return san;
309
310 error_free_san:
311 while (san->alerts) {
312 smlNotificationFreeAlert(san->alerts->data);
313 san->alerts = g_list_delete_link(san->alerts, san->alerts);
314 }
315 if (san->identifier)
316 smlSafeCFree(&(san->identifier));
317 smlSafeFree((gpointer *)&san);
318 error:
319 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
320 return NULL;
321 }
322
323 SmlBool smlNotificationAssemble11(SmlNotification *san, char **data, unsigned int *size, SmlProtocolVersion version, SmlError **error)
324 {
325 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %i, %p)", __func__, san, data, size, version, error);
326 CHECK_ERROR_REF
327 smlAssert(san);
328 smlAssert(data);
329 smlAssert(size);
330
331 /* If a notification is sent via a normal SyncML message
332 * then the used session must be managed to guarantee the
333 * consistency of the session.
334 */
335 smlAssert(san->manager);
336
337 SmlAssembler *assm = NULL;
338 SmlLocation *source = NULL;
339 SmlSession *session = NULL;
340 SmlSanAlert *alert = NULL;
341 SmlCommand *cmd = NULL;
342
343 source = smlLocationNew(san->identifier, NULL, error);
344 if (!source)
345 goto error;
346
347 char *sessionString = smlManagerGetNewSessionID(san->manager);
348 session = smlSessionNew(san->sessionType, san->type, version, SML_PROTOCOL_SYNCML, san->target, source, sessionString, 0, error);
349 if (!session) {
350 smlSafeCFree(&sessionString);
351 smlLocationUnref(source);
352 goto error;
353 }
354 smlSafeCFree(&sessionString);
355 smlManagerSessionAdd(san->manager, session, NULL, error);
356
357 assm = smlAssemblerNew(san->type, 0, error);
358 if (!assm)
359 goto error;
360
361 if (!smlAssemblerStart(assm, session, error))
362 goto error;
363
364 if (!smlAssemblerAddHeader(assm, session, error))
365 goto error;
366
367 GList *a = NULL;
368 int i = 1;
369 for (a = san->alerts; a; a = a->next) {
370 alert = a->data;
371
372
373 SmlLocation *loc = smlLocationNew(alert->serverURI, NULL, error);
374 if (!loc)
375 goto error;
376
377 cmd = smlCommandNewAlert(SML_ALERT_TWO_WAY_BY_SERVER, NULL, loc, NULL, NULL, alert->contenttype, error);
378 if (!cmd) {
379 smlLocationUnref(loc);
380 goto error;
381 }
382
383 cmd->cmdID = i;
384 i++;
385
386 if (!smlAssemblerStartCommand(assm, NULL, cmd, error) == SML_ASSEMBLER_RESULT_OK)
387 goto error;
388
389 if (!smlAssemblerEndCommand(assm, NULL, error))
390 goto error;
391
392 smlLocationUnref(loc);
393
394 smlCommandUnref(cmd);
395 }
396
397 if (!smlAssemblerRun(assm, data, size, NULL, TRUE, error))
398 goto error;
399
400 smlAssemblerFlush(assm);
401
402 smlSessionUnref(session);
403
404 smlLocationUnref(source);
405
406 smlAssemblerFree(assm);
407
408 char *hex = smlPrintHex(*data, *size);
409 smlTrace(TRACE_INTERNAL, "San packet assembled: %s", hex);
410 smlSafeCFree(&hex);
411
412 smlTrace(TRACE_EXIT, "%s", __func__);
413 return TRUE;
414
415 error:
416 if (cmd)
417 smlCommandUnref(cmd);
418 if (session)
419 smlSessionUnref(session);
420 if (assm)
421 smlAssemblerFree(assm);
422 *data = NULL;
423 *size = 0;
424 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
425 return FALSE;
426 }
427
428 static SmlBool _smlNotificationAssemble12(SmlNotification *san, char **data, unsigned int *size, SmlError **error)
429 {
430 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, san, data, size, error);
431 CHECK_ERROR_REF
432 smlAssert(san);
433 smlAssert(data);
434 smlAssert(*data == NULL);
435 smlAssert(size);
436 unsigned int numsync = 0;
437 SmlSanAlert *alert = NULL;
438
439 /* Calculate the length of the san data */
440 unsigned int length = 16 /* Digest */ + 8 /* Hdr */ + strlen(san->identifier) /* Identifier */ + 1 /* num-sync + future */;
441 GList *a = NULL;
442 for (a = san->alerts; a; a = a->next) {
443 alert = a->data;
444 length += 5 + strlen(alert->serverURI);
445 numsync++;
446 }
447
448 /* Malloc the necessary space */
449 char *buffer = smlTryMalloc0(length, error);
450 if (!buffer)
451 goto error;
452 *data = buffer;
453 *size = length;
454
455 /* the digest must be created at the end */
456
457 /* Create Header */
458 /* Version - 10 bit */
459 buffer[16] = 0x03;
460
461 /* ui mode */
462 buffer[17] = buffer[17] | (san->mode << 4);
463
464 /* initiator */
465 buffer[17] = buffer[17] | (san->init << 3);
466
467 /* future use is 0 for now - 27 bit */
468
469 /* session id */
470 buffer[21] = san->sessionID >> 8;
471 buffer[22] = san->sessionID;
472
473 /* server identifier without trailing NULL byte */
474 buffer[23] = strlen(san->identifier);
475 strncpy(buffer + 24, san->identifier, strlen(san->identifier));
476
477 buffer += 24 + strlen(san->identifier);
478
479 /* Num syncs + future */
480 buffer[0] = numsync << 4;
481 buffer++;
482
483 for (a = san->alerts; a; a = a->next) {
484 alert = a->data;
485 /* Alert type + future */
486 buffer[0] = (alert->type - 200) << 4;
487
488 /* The contenttype */
489 if (!strcmp(alert->contenttype, SML_ELEMENT_TEXT_VCARD)) {
490 buffer[3] = 0x07;
491 } else if (!strcmp(alert->contenttype, SML_ELEMENT_TEXT_VCAL)) {
492 buffer[3] = 0x06;
493 } else if (!strcmp(alert->contenttype, SML_ELEMENT_TEXT_PLAIN)) {
494 buffer[3] = 0x03;
495 } else if (!strcmp(alert->contenttype, SML_ELEMENT_TEXT_ICAL)) {
496 buffer[2] = 0x03;
497 buffer[3] = 0x05;
498 } else if (!strcmp(alert->contenttype, SML_ELEMENT_APPLICATION_OMA_DS_EMAIL)) {
499 buffer[2] = 0x03;
500 buffer[3] = 0x06;
501 } else if (!strcmp(alert->contenttype, SML_ELEMENT_APPLICATION_OMA_DS_FILE)) {
502 buffer[2] = 0x03;
503 buffer[3] = 0x07;
504 } else if (!strcmp(alert->contenttype, SML_ELEMENT_APPLICATION_OMA_DS_FOLDER)) {
505 buffer[2] = 0x03;
506 buffer[3] = 0x08;
507 } else if (!strcmp(alert->contenttype, SML_ELEMENT_TEXT_VCARD_30)) {
508 buffer[2] = 0x03;
509 buffer[3] = 0x09;
510 } else {
511 /* http://www.openmobilealliance.org/Tech/omna/omna-wsp-content-type.aspx */
512 smlTrace(TRACE_INTERNAL,
513 "%s: The OMNA WSP Content Type Number is unknown for %s.",
514 __func__, alert->contenttype);
515 /* All bytes are already set to zero. */
516 }
517
518 /* The server uri without a trailing NULL byte */
519 buffer[4] = strlen(alert->serverURI);
520 strncpy(buffer + 5, alert->serverURI, strlen(alert->serverURI));
521 buffer += 5 + strlen(alert->serverURI);
522 }
523
524 /* Create the digest */
525
526 /* DO NOT USE buffer because this pointer was manipulated
527 * ALWAYS USE *data.
528 */
529
530 /* base64 encoded MD5 digest of the notification */
531 unsigned char digest[16];
532 smlMD5GetDigest (*data+16, length - 16 /* digest */, digest);
533 char *b64_san = g_base64_encode(digest, 16);
534 smlTrace(TRACE_INTERNAL, "%s: b64(md5(san)) ::= %s", __func__, b64_san);
535
536 /* base64 encoded MD5 digest of username:password */
537 const char *username = "";
538 const char *password = "";
539 if (san->cred) {
540 username = san->cred->username;
541 password = san->cred->password;
542 }
543 char *auth = g_strdup_printf("%s:%s", username, password);
544 smlMD5GetDigest (auth, strlen(auth), digest);
545 smlSafeCFree(&auth);
546 char *b64_auth = g_base64_encode(digest, 16);
547 smlTrace(TRACE_INTERNAL, "%s: b64(md5(username:password)) ::= %s", __func__, b64_auth);
548
549 /* MD5 digest of auth:nonce:notification (empty nonce) */
550 char *complete = g_strdup_printf("%s::%s", b64_auth, b64_san);
551 smlSafeCFree(&b64_auth);
552 smlSafeCFree(&b64_san);
553 smlMD5GetDigest (complete, strlen(complete), digest);
554 smlSafeCFree(&complete);
555 smlTrace(TRACE_INTERNAL, "%s: md5(auth::san) complete", __func__);
556
557 /* copy digest to buffer */
558 memcpy(*data, digest, 16);
559 smlTrace(TRACE_INTERNAL, "%s: md5(auth::san) copied to san", __func__);
560
561 /* debug */
562
563 char *hex = smlPrintHex(*data, *size);
564 smlTrace(TRACE_INTERNAL, "San packet assembled: %s", hex);
565 smlSafeCFree(&hex);
566
567 smlTrace(TRACE_EXIT, "%s", __func__);
568 return TRUE;
569
570 error:
571 if (*data)
572 smlSafeCFree(data);
573 *size = 0;
574 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
575 return FALSE;
576 }
577
578 SmlBool smlNotificationAssemble(SmlNotification *san, char **data, unsigned int *size, SmlError **error)
579 {
580 CHECK_ERROR_REF
581 switch (san->version) {
582 case SML_SAN_VERSION_12:
583 return _smlNotificationAssemble12(san, data, size, error);
584 case SML_SAN_VERSION_10:
585 return smlNotificationAssemble11(san, data, size, SML_VERSION_10, error);
586 case SML_SAN_VERSION_11:
587 return smlNotificationAssemble11(san, data, size, SML_VERSION_11, error);
588 case SML_SAN_VERSION_UNKNOWN:
589 smlErrorSet(error, SML_ERROR_GENERIC, "Unknown SAN version.");
590 break;
591 }
592
593 return FALSE;
594 }
595
596 SmlBool smlNotificationSend(SmlNotification *san, SmlTransport *tsp, SmlError **error)
597 {
598 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, san, tsp, error);
599 CHECK_ERROR_REF
600 smlAssert(san);
601 smlAssert(tsp);
602
603 char *data = NULL;
604 unsigned int size = 0;
605 if (!smlNotificationAssemble(san, &data, &size, error))
606 goto error;
607
608 SmlTransportData *tspdata = NULL;
609 if (san->version == SML_SAN_VERSION_12)
610 tspdata = smlTransportDataNew(data, size, SML_MIMETYPE_SAN, TRUE, error);
611 else
612 tspdata = smlTransportDataNew(data, size, san->type, TRUE, error);
613
614 if (!tspdata)
615 goto error_free_data;
616 /* Do not free data because smlTransportDateNew does copy by reference. */
617 data = NULL;
618
619 /* For SyncML 1.2 and OBEX Transport we use SML_MIMETYPE_SAN for PUT and use the regular
620 SyncML message type (xml/wbxml) for GET. To avoid that we response after the PUT with
621 a GET SML_MIMETYPE_SAN request. */
622 if (san->version == SML_SAN_VERSION_12)
623 tspdata->type_get = san->type;
624
625 if (!smlTransportSend(tsp, NULL, tspdata, error))
626 goto error_free_tspdata;
627
628 smlTransportDataDeref(tspdata);
629
630 smlTrace(TRACE_EXIT, "%s", __func__);
631 return TRUE;
632
633 error_free_tspdata:
634 smlTransportDataDeref(tspdata);
635 error_free_data:
636 smlSafeCFree(&data);
637 error:
638 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
639 return FALSE;
640 }
641
642 SmlNotificationVersion smlNotificationGetVersion(SmlNotification *san)
643 {
644 smlAssert(san);
645 return san->version;
646 }
647
648 SmlNotificationUIMode smlNotificationGetMode(SmlNotification *san)
649 {
650 smlAssert(san);
651 return san->mode;
652 }
653
654 SmlNotificationInitiator smlNotificationGetInitiator(SmlNotification *san)
655 {
656 smlAssert(san);
657 return san->init;
658 }
659
660 unsigned int smlNotificationGetSessionID(SmlNotification *san)
661 {
662 smlAssert(san);
663 return san->sessionID;
664 }
665
666 const char *smlNotificationGetIdentifier(SmlNotification *san)
667 {
668 smlAssert(san);
669 return san->identifier;
670 }
671
672 unsigned int smlNotificationNumAlerts(SmlNotification *san)
673 {
674 smlAssert(san);
675 return g_list_length(san->alerts);
676 }
677
678 SmlSanAlert *smlNotificationNthAlert(SmlNotification *san, unsigned int nth)
679 {
680 smlAssert(san);
681 return g_list_nth_data(san->alerts, nth);
682 }
683
684 SmlAlertType smlSanAlertGetType(SmlSanAlert *alert)
685 {
686 smlAssert(alert);
687 return alert->type;
688 }
689
690 const char *smlSanAlertGetContentType(SmlSanAlert *alert)
691 {
692 smlAssert(alert);
693 return alert->contenttype;
694 }
695 const char *smlSanAlertGetServerURI(SmlSanAlert *alert)
696 {
697 smlAssert(alert);
698 return alert->serverURI;
699 }