Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjsip / src / pjsip-simple / rpid.c
1 /* $Id$ */
2 /* 
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
19  */
20 #include <pjsip-simple/rpid.h>
21 #include <pjsip-simple/errno.h>
22 #include <pj/assert.h>
23 #include <pj/guid.h>
24 #include <pj/pool.h>
25 #include <pj/string.h>
26
27
28 static const pj_str_t DM_NAME = {"xmlns:dm", 8};
29 static const pj_str_t DM_VAL = {"urn:ietf:params:xml:ns:pidf:data-model", 38};
30 static const pj_str_t RPID_NAME = {"xmlns:rpid", 10};
31 static const pj_str_t RPID_VAL = {"urn:ietf:params:xml:ns:pidf:rpid", 32};
32
33 static const pj_str_t DM_NOTE = {"dm:note", 7};
34 static const pj_str_t DM_PERSON = {"dm:person", 9};
35 static const pj_str_t ID = {"id", 2};
36 static const pj_str_t NOTE = {"note", 4};
37 static const pj_str_t RPID_ACTIVITIES = {"rpid:activities", 15};
38 static const pj_str_t RPID_AWAY = {"rpid:away", 9};
39 static const pj_str_t RPID_BUSY = {"rpid:busy", 9};
40 static const pj_str_t RPID_UNKNOWN = {"rpid:unknown", 12};
41
42
43 /* Duplicate RPID element */
44 PJ_DEF(void) pjrpid_element_dup(pj_pool_t *pool, pjrpid_element *dst,
45                                 const pjrpid_element *src)
46 {
47     pj_memcpy(dst, src, sizeof(pjrpid_element));
48     pj_strdup(pool, &dst->id, &src->id);
49     pj_strdup(pool, &dst->note, &src->note);
50 }
51
52
53 /* Update RPID namespaces. */
54 static void update_namespaces(pjpidf_pres *pres,
55                               pj_pool_t *pool)
56 {
57     /* Check if namespace is already present. */
58     if (pj_xml_find_attr(pres, &DM_NAME, NULL) != NULL)
59         return;
60
61     pj_xml_add_attr(pres, pj_xml_attr_new(pool, &DM_NAME, &DM_VAL));
62     pj_xml_add_attr(pres, pj_xml_attr_new(pool, &RPID_NAME, &RPID_VAL));
63 }
64
65
66 /* Comparison function to find node name substring */
67 static pj_bool_t substring_match(const pj_xml_node *node, 
68                                  const char *part_name,
69                                  int part_len)
70 {
71     pj_str_t end_name;
72
73     if (part_len < 1)
74         part_len = pj_ansi_strlen(part_name);
75
76     if (node->name.slen < part_len)
77         return PJ_FALSE;
78
79     end_name.ptr = node->name.ptr + (node->name.slen - part_len);
80     end_name.slen = part_len;
81
82     return pj_strnicmp2(&end_name, part_name, part_len)==0;
83 }
84
85 /* Util to find child node with the specified substring */
86 static pj_xml_node *find_node(const pj_xml_node *parent, 
87                               const char *part_name)
88 {
89     const pj_xml_node *node = parent->node_head.next, 
90                       *head = (pj_xml_node*) &parent->node_head;
91     int part_len = pj_ansi_strlen(part_name);
92
93     while (node != head) {
94         if (substring_match(node, part_name, part_len))
95             return (pj_xml_node*) node;
96
97         node = node->next;
98     }
99
100     return NULL;
101 }
102
103 /*
104  * Add RPID element into existing PIDF document.
105  */
106 PJ_DEF(pj_status_t) pjrpid_add_element(pjpidf_pres *pres, 
107                                        pj_pool_t *pool,
108                                        unsigned options,
109                                        const pjrpid_element *elem)
110 {
111     pj_xml_node *nd_person, *nd_activities, *nd_activity, *nd_note;
112     pj_xml_attr *attr;
113
114     PJ_ASSERT_RETURN(pres && pool && options==0 && elem, PJ_EINVAL);
115
116     PJ_UNUSED_ARG(options);
117
118     /* Check if we need to add RPID information into the PIDF document. */
119     if (elem->id.slen==0 && 
120         elem->activity==PJRPID_ACTIVITY_UNKNOWN &&
121         elem->note.slen==0)
122     {
123         /* No RPID information to be added. */
124         return PJ_SUCCESS;
125     }
126
127     /* Add <note> to <tuple> */
128     if (elem->note.slen != 0) {
129         pj_xml_node *nd_tuple;
130
131         nd_tuple = find_node(pres, "tuple");
132
133         if (nd_tuple) {
134             nd_note = pj_xml_node_new(pool, &NOTE);
135             pj_strdup(pool, &nd_note->content, &elem->note);
136             pj_xml_add_node(nd_tuple, nd_note);
137             nd_note = NULL;
138         }
139     }
140
141     /* Update namespace */
142     update_namespaces(pres, pool);
143
144     /* Add <person> */
145     nd_person = pj_xml_node_new(pool, &DM_PERSON);
146     if (elem->id.slen != 0) {
147         attr = pj_xml_attr_new(pool, &ID, &elem->id);
148     } else {
149         pj_str_t person_id;
150         /* xs:ID must start with letter */
151         //pj_create_unique_string(pool, &person_id);
152         person_id.ptr = (char*)pj_pool_alloc(pool, PJ_GUID_STRING_LENGTH+2);
153         person_id.ptr += 2;
154         pj_generate_unique_string(&person_id);
155         person_id.ptr -= 2;
156         person_id.ptr[0] = 'p';
157         person_id.ptr[1] = 'j';
158         person_id.slen += 2;
159
160         attr = pj_xml_attr_new(pool, &ID, &person_id);
161     }
162     pj_xml_add_attr(nd_person, attr);
163     pj_xml_add_node(pres, nd_person);
164
165     /* Add <activities> */
166     nd_activities = pj_xml_node_new(pool, &RPID_ACTIVITIES);
167     pj_xml_add_node(nd_person, nd_activities);
168
169     /* Add the activity */
170     switch (elem->activity) {
171     case PJRPID_ACTIVITY_AWAY:
172         nd_activity = pj_xml_node_new(pool, &RPID_AWAY);
173         break;
174     case PJRPID_ACTIVITY_BUSY:
175         nd_activity = pj_xml_node_new(pool, &RPID_BUSY);
176         break;
177     case PJRPID_ACTIVITY_UNKNOWN:
178     default:
179         nd_activity = pj_xml_node_new(pool, &RPID_UNKNOWN);
180         break;
181     }
182     pj_xml_add_node(nd_activities, nd_activity);
183
184     /* Add custom text if required. */
185     if (elem->note.slen != 0) {
186         nd_note = pj_xml_node_new(pool, &DM_NOTE);
187         pj_strdup(pool, &nd_note->content, &elem->note);
188         pj_xml_add_node(nd_person, nd_note);
189     }
190
191     /* Done */
192     return PJ_SUCCESS;
193 }
194
195
196 /* Get <note> element from PIDF <tuple> element */
197 static pj_status_t get_tuple_note(const pjpidf_pres *pres,
198                                   pj_pool_t *pool,
199                                   pjrpid_element *elem)
200 {
201     const pj_xml_node *nd_tuple, *nd_note;
202
203     nd_tuple = find_node(pres, "tuple");
204     if (!nd_tuple)
205         return PJSIP_SIMPLE_EBADRPID;
206
207     nd_note = find_node(pres, "note");
208     if (nd_note) {
209         pj_strdup(pool, &elem->note, &nd_note->content);
210         return PJ_SUCCESS;
211     }
212
213     return PJSIP_SIMPLE_EBADRPID;
214 }
215
216 /*
217  * Get RPID element from PIDF document, if any.
218  */
219 PJ_DEF(pj_status_t) pjrpid_get_element(const pjpidf_pres *pres,
220                                        pj_pool_t *pool,
221                                        pjrpid_element *elem)
222 {
223     const pj_xml_node *nd_person, *nd_activities, *nd_note = NULL;
224     const pj_xml_attr *attr;
225
226     /* Reset */
227     pj_bzero(elem, sizeof(*elem));
228     elem->activity = PJRPID_ACTIVITY_UNKNOWN;
229
230     /* Find <person> */
231     nd_person = find_node(pres, "person");
232     if (!nd_person) {
233         /* <person> not found, try to get <note> from <tuple> */
234         return get_tuple_note(pres, pool, elem);
235     }
236
237     /* Get element id attribute */
238     attr = pj_xml_find_attr((pj_xml_node*)nd_person, &ID, NULL);
239     if (attr)
240         pj_strdup(pool, &elem->id, &attr->value);
241
242     /* Get <activities> */
243     nd_activities = find_node(nd_person, "activities");
244     if (nd_activities) {
245         const pj_xml_node *nd_activity;
246
247         /* Try to get <note> from <activities> */
248         nd_note = find_node(nd_activities, "note");
249
250         /* Get the activity */
251         nd_activity = nd_activities->node_head.next;
252         if (nd_activity == nd_note)
253             nd_activity = nd_activity->next;
254
255         if (nd_activity != (pj_xml_node*) &nd_activities->node_head) {
256             if (substring_match(nd_activity, "busy", -1))
257                 elem->activity = PJRPID_ACTIVITY_BUSY;
258             else if (substring_match(nd_activity, "away", -1))
259                 elem->activity = PJRPID_ACTIVITY_AWAY;
260             else
261                 elem->activity = PJRPID_ACTIVITY_UNKNOWN;
262
263         }
264     }
265
266     /* If <note> is not found, get <note> from <person> */
267     if (nd_note == NULL)
268         nd_note = find_node(nd_person, "note");
269
270     if (nd_note) {
271         pj_strdup(pool, &elem->note, &nd_note->content);
272     } else {
273         get_tuple_note(pres, pool, elem);
274     }
275
276     return PJ_SUCCESS;
277 }
278
279