1 /*
2  * This file is part of the Sofia-SIP package
3  *
4  * Copyright (C) 2005 Nokia Corporation.
5  *
6  * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24 
25 /**@ingroup test_msg
26  * @file test_class.c
27  *
28  * Message class for testing parser and transports.
29  *
30  * @author Pekka Pessi <Pekka.Pessi@nokia.com>
31  *
32  * @date Created: Tue Mar  5 11:57:20 2002 ppessi
33  */
34 
35 #include "config.h"
36 
37 #include <stddef.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <assert.h>
41 #include <stdio.h>
42 
43 #define TAG_NAMESPACE "tst"
44 
45 #include "test_class.h"
46 #include <sofia-sip/msg_parser.h>
47 #include <sofia-sip/msg_mclass.h>
48 #include "test_protos.h"
49 #include <sofia-sip/msg_addr.h>
50 
51 extern msg_mclass_t const msg_test_mclass[1];
52 
msg_test_default(void)53 extern msg_mclass_t const *msg_test_default(void)
54 {
55   return msg_test_mclass;
56 }
57 
58 #define msg_generic_update NULL
59 
60 /**@ingroup test_msg
61  * @defgroup msg_test_request Request Line for Testing
62  */
63 
64 static msg_xtra_f msg_request_dup_xtra;
65 static msg_dup_f msg_request_dup_one;
66 
67 msg_hclass_t msg_request_class[] =
68 MSG_HEADER_CLASS(msg_, request, NULL, "", rq_common,
69 		 single_critical, msg_request, msg_generic);
70 
71 /** Decode a request line */
msg_request_d(su_home_t * home,msg_header_t * h,char * s,isize_t slen)72 issize_t msg_request_d(su_home_t *home, msg_header_t *h, char *s, isize_t slen)
73 {
74   msg_request_t *rq = (msg_request_t *)h;
75   char *uri, *version;
76 
77   if (msg_firstline_d(s, &uri, &version) < 0 || !uri ||
78       url_d(rq->rq_url, uri) < 0)
79     return -1;
80 
81   rq->rq_method_name = s;
82   rq->rq_version = version;
83 
84   return 0;
85 }
86 
87 /**Encode a request line. */
msg_request_e(char b[],isize_t bsiz,msg_header_t const * h,int flags)88 issize_t msg_request_e(char b[], isize_t bsiz, msg_header_t const *h, int flags)
89 {
90   msg_request_t const *rq = (msg_request_t const *)h;
91 
92   return snprintf(b, bsiz, "%s " URL_FORMAT_STRING " %s" CRLF,
93 		  rq->rq_method_name,
94 		  URL_PRINT_ARGS(rq->rq_url),
95 		  rq->rq_version);
96 }
97 
msg_request_dup_xtra(msg_header_t const * h,isize_t offset)98 isize_t msg_request_dup_xtra(msg_header_t const *h, isize_t offset)
99 {
100   isize_t rv = offset;
101   msg_request_t const *rq = (msg_request_t const *)h;
102 
103   rv += url_xtra(rq->rq_url);
104   rv += MSG_STRING_SIZE(rq->rq_method_name);
105   rv += MSG_STRING_SIZE(rq->rq_version);
106 
107   return rv;
108 }
109 
110 /** Duplicate one request header. */
msg_request_dup_one(msg_header_t * dst,msg_header_t const * src,char * b,isize_t xtra)111 char *msg_request_dup_one(msg_header_t *dst, msg_header_t const *src,
112 			  char *b, isize_t xtra)
113 {
114   msg_request_t *rq = (msg_request_t *)dst;
115   msg_request_t const *o = (msg_request_t const *)src;
116   char *end = b + xtra;
117 
118   URL_DUP(b, end, rq->rq_url, o->rq_url);
119 
120   MSG_STRING_DUP(b, rq->rq_method_name, o->rq_method_name);
121   MSG_STRING_DUP(b, rq->rq_version, o->rq_version);
122 
123   assert(b <= end);
124 
125   return b;
126 }
127 
128 /**@ingroup test_msg
129  * @defgroup msg_test_status Status Line for Testing
130  */
131 
132 static msg_xtra_f msg_status_dup_xtra;
133 static msg_dup_f msg_status_dup_one;
134 
135 msg_hclass_t msg_status_class[1] =
136 MSG_HEADER_CLASS(msg_, status, NULL, "", st_common,
137 		 single_critical, msg_status, msg_generic);
138 
139 /** Parse status line */
msg_status_d(su_home_t * home,msg_header_t * h,char * s,isize_t slen)140 issize_t msg_status_d(su_home_t *home, msg_header_t *h, char *s, isize_t slen)
141 {
142   msg_status_t *st = (msg_status_t *)h;
143   char *status, *phrase;
144   unsigned long code;
145 
146   if (msg_firstline_d(s, &status, &phrase) < 0 ||
147       (code = strtoul(status, &status, 10)) >= 1000 || *status)
148     return -1;
149 
150   st->st_status = code;
151   st->st_phrase = phrase;
152   st->st_version = s;
153 
154   return 0;
155 }
156 
msg_status_e(char b[],isize_t bsiz,msg_header_t const * h,int flags)157 issize_t msg_status_e(char b[], isize_t bsiz, msg_header_t const *h, int flags)
158 {
159   msg_status_t const *st = (msg_status_t const *)h;
160   int status = st->st_status;
161 
162   if (status > 999 || status < 100)
163     status = 0;
164 
165   return snprintf(b, bsiz, "%s %03u %s" CRLF,
166 		  st->st_version, status, st->st_phrase);
167 }
168 
169 /** Extra size of a msg_status_t object. */
msg_status_dup_xtra(msg_header_t const * h,isize_t offset)170 isize_t msg_status_dup_xtra(msg_header_t const *h, isize_t offset)
171 {
172   isize_t rv = offset;
173   msg_status_t const *st = (msg_status_t const *)h;
174   rv += MSG_STRING_SIZE(st->st_version);
175   rv += MSG_STRING_SIZE(st->st_phrase);
176   return rv;
177 }
178 
179 /** Duplicate one status header. */
msg_status_dup_one(msg_header_t * dst,msg_header_t const * src,char * b,isize_t xtra)180 char *msg_status_dup_one(msg_header_t *dst, msg_header_t const *src,
181 			 char *b, isize_t xtra)
182 {
183   msg_status_t *st = (msg_status_t *)dst;
184   msg_status_t const *o = (msg_status_t const *)src;
185   char *end = b + xtra;
186 
187   MSG_STRING_DUP(b, st->st_version, o->st_version);
188   st->st_status = o->st_status;
189   MSG_STRING_DUP(b, st->st_phrase, o->st_phrase);
190 
191   assert(b <= end); (void)end;
192 
193   return b;
194 }
195 
196 msg_hclass_t test_numeric_class[] =
197   MSG_HEADER_CLASS(msg_, numeric, "Numeric", "", x_common,
198 		   single, msg_generic, msg_generic);
199 
200 msg_hclass_t test_auth_class[] =
201   MSG_HEADER_CLASS(msg_, auth, "Auth", "", au_params,
202 		   append, msg_auth, msg_generic);
203 
204 /** Extract the message body, including separator line.
205  *
206  * @param[in,out] msg  message object
207  * @param[in,out] pub  public message structure
208  * @param[in]     b    buffer containing unparsed data
209  * @param[in]     bsiz buffer size
210  * @param[in]     eos  true if buffer contains whole message
211  *
212  * @retval -1     error
213  * @retval 0      message is incomplete
214  * @retval other  number of bytes extracted
215  */
msg_test_extract_body(msg_t * msg,msg_pub_t * pub,char b[],isize_t bsiz,int eos)216 issize_t msg_test_extract_body(msg_t *msg, msg_pub_t *pub,
217 			       char b[], isize_t bsiz, int eos)
218 {
219   msg_test_t *tst = (msg_test_t *)pub;
220   ssize_t m = 0;
221   size_t body_len;
222 
223   if (!(tst->msg_flags & MSG_FLG_BODY)) {
224     /* We are looking at a potential empty line */
225     m = msg_extract_separator(msg, (msg_pub_t *)tst, b, bsiz, eos);
226     if (m == 0 || m == -1)
227       return m;
228     tst->msg_flags |= MSG_FLG_BODY;
229     b += m;
230     bsiz -= m;
231   }
232 
233   if (tst->msg_content_length)
234     body_len = tst->msg_content_length->l_length;
235   else if (MSG_IS_MAILBOX(tst->msg_flags)) /* message fragments */
236     body_len = 0;
237   else if (eos)
238     body_len = bsiz;
239   else
240     return -1;
241 
242   if (body_len == 0) {
243     tst->msg_flags |= MSG_FLG_COMPLETE;
244     return m;
245   }
246 
247   if (m)
248     return m;
249 
250   if ((m = msg_extract_payload(msg, (msg_pub_t *)tst, NULL, body_len, b, bsiz, eos) ) == -1)
251     return -1;
252 
253   tst->msg_flags |= MSG_FLG_FRAGS;
254   if (bsiz >= body_len)
255     tst->msg_flags |= MSG_FLG_COMPLETE;
256   return m;
257 }
258 
259 msg_href_t const msg_content_length_href[1] =
260   {{
261     msg_content_length_class,
262     offsetof(msg_test_t, msg_content_length)
263   }};
264 
265 #include <sofia-sip/su_tag_class.h>
266 #include <sofia-sip/su_tag_inline.h>
267 #include <sofia-sip/su_tagarg.h>
268 #include <sofia-sip/msg_tag_class.h>
269 
270 tagi_t *tsttag_filter(tagi_t *dst,
271 		      tagi_t const f[],
272 		      tagi_t const *src,
273 		      void **bb);
274 
275 /** Tag class for test header tags. @HIDE */
276 tag_class_t tsthdrtag_class[1] =
277   {{
278     sizeof(tsthdrtag_class),
279     /* tc_next */     NULL,
280     /* tc_len */      NULL,
281     /* tc_move */     NULL,
282     /* tc_xtra */     msghdrtag_xtra,
283     /* tc_dup */      msghdrtag_dup,
284     /* tc_free */     NULL,
285     /* tc_find */     NULL,
286     /* tc_snprintf */ msghdrtag_snprintf,
287     /* tc_filter */   tsttag_filter,
288     /* tc_ref_set */  t_ptr_ref_set,
289     /* tc_scan */     msghdrtag_scan,
290   }};
291 
292 /** Tag class for TST header string tags. @HIDE */
293 tag_class_t tststrtag_class[1] =
294   {{
295     sizeof(tststrtag_class),
296     /* tc_next */     NULL,
297     /* tc_len */      NULL,
298     /* tc_move */     NULL,
299     /* tc_xtra */     t_str_xtra,
300     /* tc_dup */      t_str_dup,
301     /* tc_free */     NULL,
302     /* tc_find */     NULL,
303     /* tc_snprintf */ t_str_snprintf,
304     /* tc_filter */   NULL /* msgtag_str_filter */,
305     /* tc_ref_set */  t_ptr_ref_set,
306     /* tc_scan */     t_str_scan
307   }};
308 
309 /** Tag class for TST message tags. @HIDE */
310 tag_class_t tstmsgtag_class[1] =
311   {{
312     sizeof(tstmsgtag_class),
313     /* tc_next */     NULL,
314     /* tc_len */      NULL,
315     /* tc_move */     NULL,
316     /* tc_xtra */     msgobjtag_xtra,
317     /* tc_dup */      msgobjtag_dup,
318     /* tc_free */     NULL,
319     /* tc_find */     NULL,
320     /* tc_snprintf */ msgobjtag_snprintf,
321     /* tc_filter */   NULL /* tsttag_tst_filter */,
322     /* tc_ref_set */  t_ptr_ref_set,
323   }};
324 
325 tag_typedef_t tsttag_header =
326 	{{ TAG_NAMESPACE, "header", tsthdrtag_class, 0 }};
327 
328 tag_typedef_t tsttag_header_str = STRTAG_TYPEDEF(header_str);
329 
330 /** Filter a TST header structure. */
tsttag_filter(tagi_t * dst,tagi_t const f[],tagi_t const * src,void ** bb)331 tagi_t *tsttag_filter(tagi_t *dst,
332 		      tagi_t const f[],
333 		      tagi_t const *src,
334 		      void **bb)
335 {
336   tagi_t stub[2] = {{ NULL }};
337   tag_type_t sctt, tt = f->t_tag;
338   msg_hclass_t *hc = (msg_hclass_t *)tt->tt_magic;
339 
340   assert(src);
341 
342   sctt = src->t_tag;
343 
344   if (sctt && sctt->tt_class == tstmsgtag_class) {
345     msg_test_t const *tst = (msg_test_t const *)src->t_value;
346     msg_mclass_t *mc = (msg_mclass_t *)tst->msg_ident;
347     msg_header_t const **hh = (msg_header_t const **)
348       msg_hclass_offset(mc, (msg_pub_t *)tst, hc);
349     msg_header_t const *h;
350 
351     if (tst == NULL ||
352 	(char *)hh >= ((char *)tst + tst->msg_size) ||
353 	(char *)hh < (char const *)&tst->msg_request)
354       return dst;
355 
356     h = *hh;
357 
358     if (h == NULL)
359       return dst;
360 
361     stub[0].t_tag = tt;
362     stub[0].t_value = (tag_value_t)h;
363     src = stub; sctt = tt;
364   }
365 
366   if (tt != sctt)
367     return dst;
368 
369   if (!src->t_value)
370     return dst;
371   else if (dst) {
372     return t_dup(dst, src, bb);
373   }
374   else {
375     *bb = (char *)*bb + t_xtra(src, (size_t)*bb);
376     return dst + 1;
377   }
378 }
379 
380 /** Add duplicates of headers from taglist to the TST message. */
tst_add_tl(msg_t * msg,msg_test_t * tst,tag_type_t tag,tag_value_t value,...)381 int tst_add_tl(msg_t *msg, msg_test_t *tst,
382 	       tag_type_t tag, tag_value_t value, ...)
383 {
384   tagi_t const *t;
385   ta_list ta;
386 
387   ta_start(ta, tag, value);
388 
389   for (t = ta_args(ta); t; t = tl_next(t)) {
390     if (!(tag = t->t_tag) || !(value = t->t_value))
391       continue;
392 
393     if (TSTTAG_P(tag)) {
394       msg_hclass_t *hc = (msg_hclass_t *)tag->tt_magic;
395       msg_header_t *h = (msg_header_t *)value, **hh;
396 
397       if (h == NULL)
398 	;
399       else if (h == MSG_HEADER_NONE) {	/* Remove header */
400 	hh = msg_hclass_offset(msg_mclass(msg), (msg_pub_t *)tst, hc);
401 	while (hh && *hh)
402 	  msg_header_remove(msg, (msg_pub_t *)tst, *hh);
403       } else if (msg_header_add_dup_as(msg, (msg_pub_t *)tst, hc, h) < 0)
404 	break;
405     }
406     else if (TSTTAG_STR_P(tag)) {
407       msg_hclass_t *hc = (msg_hclass_t *)tag->tt_magic;
408       char const *s = (char const *)value;
409       if (s && msg_header_add_make(msg, (msg_pub_t *)tst, hc, s) < 0)
410 	break;
411     }
412     else if (tag == tsttag_header_str) {
413       if (msg_header_add_str(msg, (msg_pub_t *)tst, (char const *)value) < 0)
414 	break;
415     }
416   }
417 
418   ta_end(ta);
419 
420   return t ? -1 : 0;
421 }
422 
423