jabberd2  2.2.16
sm/pkt.c
Go to the documentation of this file.
00001 /*
00002  * jabberd - Jabber Open Source Server
00003  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
00004  *                    Ryan Eatmon, Robert Norris
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA
00019  */
00020 
00021 #include "sm.h"
00022 
00030 pkt_t pkt_error(pkt_t pkt, int err) {
00031     if(pkt == NULL) return NULL;
00032 
00033     /* if it's an error already, log, free, return */
00034     if(pkt->type & pkt_ERROR) {
00035         log_debug(ZONE, "dropping error pkt");
00036         pkt_free(pkt);
00037         return NULL;
00038     }
00039 
00040     stanza_error(pkt->nad, 1, err);
00041 
00042     /* update vars and attrs */
00043     pkt_tofrom(pkt);
00044     pkt->type |= pkt_ERROR;
00045 
00046     /* supplant route destination in case there was none in original packet */
00047     if(pkt->to == NULL && pkt->rto != NULL)
00048         pkt->to = jid_dup(pkt->rto);
00049 
00050     /* all done, error'd and addressed */
00051     log_debug(ZONE, "processed %d error pkt", err);
00052 
00053     return pkt;
00054 }
00055 
00057 pkt_t pkt_tofrom(pkt_t pkt) {
00058     jid_t tmp;
00059 
00060     if(pkt == NULL) return NULL;
00061 
00062     /* swap vars */
00063     tmp = pkt->from;
00064     pkt->from = pkt->to;
00065     pkt->to = tmp;
00066     tmp = pkt->rfrom;
00067     pkt->rfrom = pkt->rto;
00068     pkt->rto = tmp;
00069 
00070     /* update attrs */
00071     if(pkt->to != NULL)
00072         nad_set_attr(pkt->nad, 1, -1, "to", jid_full(pkt->to), 0);
00073     if(pkt->from != NULL)
00074         nad_set_attr(pkt->nad, 1, -1, "from", jid_full(pkt->from), 0);
00075     if(pkt->rto != NULL)
00076         nad_set_attr(pkt->nad, 0, -1, "to", jid_full(pkt->rto), 0);
00077     if(pkt->rfrom != NULL)
00078         nad_set_attr(pkt->nad, 0, -1, "from", jid_full(pkt->rfrom), 0);
00079 
00080     return pkt;
00081 }
00082 
00084 pkt_t pkt_dup(pkt_t pkt, const char *to, const char *from) {
00085     pkt_t pnew;
00086 
00087     if(pkt == NULL) return NULL;
00088 
00089     pnew = (pkt_t) calloc(1, sizeof(struct pkt_st));
00090 
00091     pnew->sm = pkt->sm;
00092     pnew->type = pkt->type;
00093     pnew->nad = nad_copy(pkt->nad);
00094 
00095     /* set replacement attrs */
00096     if(to != NULL) {
00097         pnew->to = jid_new(to, -1);
00098         nad_set_attr(pnew->nad, 1, -1, "to", jid_full(pnew->to), 0);
00099     } else if(pkt->to != NULL)
00100         pnew->to = jid_dup(pkt->to);
00101 
00102     if(from != NULL) {
00103         pnew->from = jid_new(from, -1);
00104         nad_set_attr(pnew->nad, 1, -1, "from", jid_full(pnew->from), 0);
00105     } else if(pkt->from != NULL)
00106         pnew->from = jid_dup(pkt->from);
00107 
00108     log_debug(ZONE, "duplicated packet");
00109 
00110     return pnew;
00111 }
00112 
00113 pkt_t pkt_new(sm_t sm, nad_t nad) {
00114     pkt_t pkt;
00115     int ns, attr, elem;
00116     char pri[20];
00117 
00118     log_debug(ZONE, "creating new packet");
00119 
00120     /* find the route */
00121     ns = nad_find_namespace(nad, 0, uri_COMPONENT, NULL);
00122     if(ns < 0) {
00123         log_debug(ZONE, "packet not in component namespace");
00124         nad_free(nad);
00125         return NULL;
00126     }
00127 
00128     /* create the pkt holder */
00129     pkt = (pkt_t) calloc(1, sizeof(struct pkt_st));
00130 
00131     pkt->sm = sm;
00132     pkt->nad = nad;
00133 
00134     /* routes */
00135     if(NAD_ENAME_L(nad, 0) == 5 && strncmp("route", NAD_ENAME(nad, 0), 5) == 0) {
00136         /* route element */
00137         if((attr = nad_find_attr(nad, 0, -1, "to", NULL)) >= 0)
00138             pkt->rto = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
00139         if((attr = nad_find_attr(nad, 0, -1, "from", NULL)) >= 0)
00140             pkt->rfrom = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
00141 
00142         /* route type */
00143         attr = nad_find_attr(nad, 0, -1, "type", NULL);
00144         if(attr < 0)
00145             pkt->rtype = route_UNICAST;
00146         else if(NAD_AVAL_L(nad, attr) == 9 && strncmp("broadcast", NAD_AVAL(nad, attr), 9) == 0)
00147             pkt->rtype = route_BROADCAST;
00148 
00149         /* route errors */
00150         if(nad_find_attr(nad, 0, -1, "error", NULL) >= 0)
00151             pkt->rtype |= route_ERROR;
00152 
00153         /* client packets */
00154         ns = nad_find_namespace(nad, 1, uri_CLIENT, NULL);
00155         if(ns >= 0) {
00156 
00157             /* get initial addresses */
00158             if((attr = nad_find_attr(pkt->nad, 1, -1, "to", NULL)) >= 0 && NAD_AVAL_L(pkt->nad, attr) > 0)
00159                 pkt->to = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
00160             if((attr = nad_find_attr(pkt->nad, 1, -1, "from", NULL)) >= 0 && NAD_AVAL_L(pkt->nad, attr) > 0)
00161                 pkt->from = jid_new(NAD_AVAL(pkt->nad, attr), NAD_AVAL_L(pkt->nad, attr));
00162 
00163             /* find type, if any */
00164             attr = nad_find_attr(pkt->nad, 1, -1, "type", NULL);
00165 
00166             /* messages are simple, only subtypes */
00167             if(NAD_ENAME_L(pkt->nad, 1) == 7 && strncmp("message", NAD_ENAME(pkt->nad, 1), 7) == 0) {
00168                 pkt->type = pkt_MESSAGE;
00169                 if(attr >= 0) {
00170                     if(NAD_AVAL_L(pkt->nad, attr) == 4 && strncmp("chat", NAD_AVAL(pkt->nad, attr), 4) == 0)
00171                         pkt->type = pkt_MESSAGE_CHAT;
00172                     else if(NAD_AVAL_L(pkt->nad, attr) == 8 && strncmp("headline", NAD_AVAL(pkt->nad, attr), 8) == 0)
00173                         pkt->type = pkt_MESSAGE_HEADLINE;
00174                     else if(NAD_AVAL_L(pkt->nad, attr) == 9 && strncmp("groupchat", NAD_AVAL(pkt->nad, attr), 9) == 0)
00175                         pkt->type = pkt_MESSAGE_GROUPCHAT;
00176                     else if(NAD_AVAL_L(pkt->nad, attr) == 5 && strncmp("error", NAD_AVAL(pkt->nad, attr), 5) == 0)
00177                         pkt->type = pkt_MESSAGE | pkt_ERROR;
00178                 }
00179 
00180                 return pkt;
00181             }
00182 
00183             /* presence is a mixed bag, s10ns in here too */
00184             if(NAD_ENAME_L(pkt->nad, 1) == 8 && strncmp("presence", NAD_ENAME(pkt->nad, 1), 8) == 0) {
00185                 pkt->type = pkt_PRESENCE;
00186                 if(attr >= 0) {
00187                     if(NAD_AVAL_L(pkt->nad, attr) == 11 && strncmp("unavailable", NAD_AVAL(pkt->nad, attr), 11) == 0)
00188                         pkt->type = pkt_PRESENCE_UN;
00189                     else if(NAD_AVAL_L(pkt->nad, attr) == 5 && strncmp("probe", NAD_AVAL(pkt->nad, attr), 5) == 0)
00190                         pkt->type = pkt_PRESENCE_PROBE;
00191                     else if(NAD_AVAL_L(pkt->nad, attr) == 9 && strncmp("subscribe", NAD_AVAL(pkt->nad, attr), 9) == 0)
00192                         pkt->type = pkt_S10N;
00193                     else if(NAD_AVAL_L(pkt->nad, attr) == 10 && strncmp("subscribed", NAD_AVAL(pkt->nad, attr), 10) == 0)
00194                         pkt->type = pkt_S10N_ED;
00195                     else if(NAD_AVAL_L(pkt->nad, attr) == 11 && strncmp("unsubscribe", NAD_AVAL(pkt->nad, attr), 11) == 0)
00196                         pkt->type = pkt_S10N_UN;
00197                     else if(NAD_AVAL_L(pkt->nad, attr) == 12 && strncmp("unsubscribed", NAD_AVAL(pkt->nad, attr), 12) == 0)
00198                         pkt->type = pkt_S10N_UNED;
00199                     else if(NAD_AVAL_L(pkt->nad, attr) == 5 && strncmp("error", NAD_AVAL(pkt->nad, attr), 5) == 0)
00200                         pkt->type = pkt_PRESENCE | pkt_ERROR;
00201                 }
00202 
00203                 /* priority */
00204                 if((elem = nad_find_elem(pkt->nad, 1, NAD_ENS(pkt->nad, 1), "priority", 1)) < 0)
00205                     return pkt;
00206 
00207                 if(NAD_CDATA_L(pkt->nad, elem) <= 0 || NAD_CDATA_L(pkt->nad, elem) > 19)
00208                     return pkt;
00209 
00210                 memcpy(pri, NAD_CDATA(pkt->nad, elem), NAD_CDATA_L(pkt->nad, elem));
00211                 pri[NAD_CDATA_L(pkt->nad, elem)] = '\0';
00212                 pkt->pri = atoi(pri);
00213 
00214                 if(pkt->pri > 127) pkt->pri = 127;
00215                 if(pkt->pri < -128) pkt->pri = -128;
00216 
00217                 return pkt;
00218             }
00219 
00220             /* iq's are pretty easy, but also set xmlns */
00221             if(NAD_ENAME_L(pkt->nad, 1) == 2 && strncmp("iq", NAD_ENAME(pkt->nad, 1), 2) == 0) {
00222                 pkt->type = pkt_IQ;
00223                 if (attr < 0) {
00224                     log_write(sm->log, LOG_ERR, "dropping iq without type");
00225                     log_debug(ZONE, "dropping iq without type");
00226                     pkt_free(pkt);
00227                     return NULL;
00228                 }
00229                 if (NAD_AVAL_L(pkt->nad, attr) == 6 && strncmp("result", NAD_AVAL(pkt->nad, attr), 6) == 0) pkt->type = pkt_IQ_RESULT;
00230                 else if (NAD_AVAL_L(pkt->nad, attr) == 5 && strncmp("error", NAD_AVAL(pkt->nad, attr), 5) == 0) pkt->type = pkt_IQ | pkt_ERROR;
00231                 else if (NAD_AVAL_L(pkt->nad, attr) == 3 && strncmp("set", NAD_AVAL(pkt->nad, attr), 3) == 0) pkt->type = pkt_IQ_SET;
00232                 else if (NAD_AVAL_L(pkt->nad, attr) != 3 || strncmp("get", NAD_AVAL(pkt->nad, attr), 3)) {
00233                     log_write(sm->log, LOG_ERR, "dropping iq with bad type \"%.*s\"", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
00234                     log_debug(ZONE, "dropping iq with bad type \"%.*s\"", NAD_AVAL_L(pkt->nad, attr), NAD_AVAL(pkt->nad, attr));
00235                     pkt_free(pkt);
00236                     return NULL;
00237                 }
00238 
00239                 if(pkt->nad->ecur > 2 && (ns = NAD_ENS(pkt->nad, 2)) >= 0)
00240                     pkt->ns = (int) (long) xhash_getx(pkt->sm->xmlns, NAD_NURI(pkt->nad, ns), NAD_NURI_L(pkt->nad, ns));
00241 
00242                 return pkt;
00243             }
00244 
00245             log_debug(ZONE, "unknown client namespace packet");
00246 
00247             return pkt;
00248         }
00249 
00250         /* sessions packets */
00251         ns = nad_find_namespace(nad, 1, uri_SESSION, NULL);
00252         if(ns >= 0) {
00253 
00254             /* sessions */
00255             if(NAD_ENAME_L(pkt->nad, 1) == 7 && strncmp("session", NAD_ENAME(pkt->nad, 1), 7) == 0) {
00256 
00257                 /* find action */
00258                 attr = nad_find_attr(pkt->nad, 1, -1, "action", NULL);
00259 
00260                 if(attr >= 0) {
00261                     if(NAD_AVAL_L(pkt->nad, attr) == 5 && strncmp("start", NAD_AVAL(pkt->nad, attr), 5) >= 0)
00262                         pkt->type = pkt_SESS;
00263                     else if(NAD_AVAL_L(pkt->nad, attr) == 3 && strncmp("end", NAD_AVAL(pkt->nad, attr), 3) >= 0)
00264                         pkt->type = pkt_SESS_END;
00265                     else if(NAD_AVAL_L(pkt->nad, attr) == 6 && strncmp("create", NAD_AVAL(pkt->nad, attr), 6) >= 0)
00266                         pkt->type = pkt_SESS_CREATE;
00267                     else if(NAD_AVAL_L(pkt->nad, attr) == 6 && strncmp("delete", NAD_AVAL(pkt->nad, attr), 6) >= 0)
00268                         pkt->type = pkt_SESS_DELETE;
00269                     else if(NAD_AVAL_L(pkt->nad, attr) == 7 && strncmp("started", NAD_AVAL(pkt->nad, attr), 7) >= 0)
00270                         pkt->type = pkt_SESS | pkt_SESS_FAILED;
00271                     else if(NAD_AVAL_L(pkt->nad, attr) == 5 && strncmp("ended", NAD_AVAL(pkt->nad, attr), 5) >= 0)
00272                         pkt->type = pkt_SESS_END | pkt_SESS_FAILED;
00273                     else if(NAD_AVAL_L(pkt->nad, attr) == 7 && strncmp("created", NAD_AVAL(pkt->nad, attr), 7) >= 0)
00274                         pkt->type = pkt_SESS_CREATE | pkt_SESS_FAILED;
00275                     else if(NAD_AVAL_L(pkt->nad, attr) == 7 && strncmp("deleted", NAD_AVAL(pkt->nad, attr), 7) >= 0)
00276                         pkt->type = pkt_SESS_DELETE | pkt_SESS_FAILED;
00277 
00278                     return pkt;
00279                 } else {
00280                     log_debug(ZONE, "missing action on session packet");
00281                     return pkt;
00282                 }
00283             }
00284 
00285             log_debug(ZONE, "unknown session namespace packet");
00286 
00287             return pkt;
00288         }
00289 
00290         log_debug(ZONE, "unknown packet");
00291 
00292         return pkt;
00293     }
00294 
00295     /* advertisements */
00296     if(NAD_ENAME_L(nad, 0) == 8 && strncmp("presence", NAD_ENAME(nad, 0), 8) == 0) {
00297         if(nad_find_attr(nad, 0, -1, "type", "unavailable") >= 0)
00298             pkt->rtype = route_ADV_UN;
00299         else
00300             pkt->rtype = route_ADV;
00301 
00302         attr = nad_find_attr(nad, 0, -1, "from", NULL);
00303         if(attr >= 0)
00304             pkt->from = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr));
00305 
00306         return pkt;
00307     }
00308 
00309     log_debug(ZONE, "invalid component packet");
00310 
00311     pkt_free(pkt);
00312     return NULL;
00313 }
00314 
00315 void pkt_free(pkt_t pkt) {
00316     log_debug(ZONE, "freeing pkt");
00317 
00318     if (pkt != NULL) {
00319         if(pkt->rto != NULL) jid_free(pkt->rto);
00320         if(pkt->rfrom != NULL) jid_free(pkt->rfrom);
00321         if(pkt->to != NULL) jid_free(pkt->to);
00322         if(pkt->from != NULL) jid_free(pkt->from);
00323         if(pkt->nad != NULL) nad_free(pkt->nad);
00324         free(pkt);
00325     }
00326 }
00327 
00328 pkt_t pkt_create(sm_t sm, const char *elem, const char *type, const char *to, const char *from) {
00329     nad_t nad;
00330     int ns;
00331 
00332     nad = nad_new();
00333 
00334     ns = nad_add_namespace(nad, uri_COMPONENT, NULL);
00335     nad_append_elem(nad, ns, "route", 0);
00336 
00337     nad_add_namespace(nad, uri_SESSION, "sm");
00338 
00339     ns = nad_add_namespace(nad, uri_CLIENT, NULL);
00340     nad_append_elem(nad, ns, elem, 1);
00341 
00342     if(type != NULL)
00343         nad_append_attr(nad, -1, "type", type);
00344     if(to != NULL)
00345         nad_append_attr(nad, -1, "to", to);
00346     if(from != NULL)
00347         nad_append_attr(nad, -1, "from", from);
00348 
00349     return pkt_new(sm, nad);
00350 }
00351 
00353 void pkt_id(pkt_t src, pkt_t dest) {
00354     int attr;
00355 
00356     attr = nad_find_attr(src->nad, 1, -1, "id", NULL);
00357     if(attr >= 0)
00358         nad_set_attr(dest->nad, 1, -1, "id", NAD_AVAL(src->nad, attr), NAD_AVAL_L(src->nad, attr));
00359     else
00360         nad_set_attr(dest->nad, 1, -1, "id", NULL, 0);
00361 }
00362 
00364 void pkt_id_new(pkt_t pkt) {
00365     char id[40];
00366     int i, r;
00367 
00368     /* as we are not using ids for tracking purposes, these can be generated randomly */
00369     for(i = 0; i < 40; i++) {
00370         r = (int) (36.0 * rand() / RAND_MAX);
00371         id[i] = (r >= 0 && r <= 9) ? (r + 48) : (r + 87);
00372     }
00373 
00374     nad_set_attr(pkt->nad, 1, -1, "id", id, 40);
00375 
00376     return;
00377 }
00378 
00379 void pkt_router(pkt_t pkt) {
00380     mod_ret_t ret;
00381     int ns, scan;
00382 
00383     if(pkt == NULL) return;
00384 
00385     log_debug(ZONE, "delivering pkt to router");
00386 
00387     if(pkt->to == NULL) {
00388         log_debug(ZONE, "no to address on packet, unable to route");
00389         pkt_free(pkt);
00390         return;
00391     }
00392 
00393     if(pkt->rto != NULL)
00394         jid_free(pkt->rto);
00395     pkt->rto = jid_new(pkt->to->domain, -1);
00396 
00397     if(pkt->rto == NULL) {
00398         log_debug(ZONE, "invalid to address on packet, unable to route");
00399         pkt_free(pkt);
00400         return;
00401     }
00402 
00403     nad_set_attr(pkt->nad, 0, -1, "to", pkt->rto->domain, 0);
00404 
00405     if(pkt->rfrom != NULL)
00406         jid_free(pkt->rfrom);
00407     pkt->rfrom = jid_new(pkt->sm->id, -1);
00408 
00409     if(pkt->rfrom == NULL) {
00410         log_debug(ZONE, "invalid from address on packet, unable to route");
00411         pkt_free(pkt);
00412         return;
00413     }
00414 
00415     nad_set_attr(pkt->nad, 0, -1, "from", pkt->rfrom->domain, 0);
00416 
00417     ret = mm_out_router(pkt->sm->mm, pkt);
00418     switch(ret) {
00419         case mod_HANDLED:
00420             return;
00421 
00422         case mod_PASS:
00423             
00424             /* remove sm specifics */
00425             ns = nad_find_namespace(pkt->nad, 1, uri_SESSION, NULL);
00426             /* remove them if there is no session elements in packet */
00427             if(ns >= 0 && nad_find_elem(pkt->nad, 0, ns, NULL, 1) < 0) {
00428                 nad_set_attr(pkt->nad, 1, ns, "c2s", NULL, 0);
00429                 nad_set_attr(pkt->nad, 1, ns, "sm", NULL, 0);
00430 
00431                 /* forget about the internal namespace too */
00432                 if(pkt->nad->elems[1].ns == ns)
00433                     pkt->nad->elems[1].ns = pkt->nad->nss[ns].next;
00434 
00435                 else {
00436                     for(scan = pkt->nad->elems[1].ns; pkt->nad->nss[scan].next != -1 && pkt->nad->nss[scan].next != ns; scan = pkt->nad->nss[scan].next);
00437 
00438                     /* got it */
00439                     if(pkt->nad->nss[scan].next != -1)
00440                         pkt->nad->nss[scan].next = pkt->nad->nss[ns].next;
00441                 }
00442             }
00443 
00444             sx_nad_write(pkt->sm->router, pkt->nad);
00445 
00446             /* nad already free'd, free the rest */
00447             pkt->nad = NULL;
00448             pkt_free(pkt);
00449 
00450             break;
00451 
00452         default:
00453             pkt_router(pkt_error(pkt, -ret));
00454 
00455             break;
00456     }
00457 }
00458 
00459 void pkt_sess(pkt_t pkt, sess_t sess) {
00460     mod_ret_t ret;
00461 
00462     if(pkt == NULL) return;
00463 
00464     log_debug(ZONE, "delivering pkt to session %s", jid_full(sess->jid));
00465 
00466     if(pkt->rto != NULL)
00467         jid_free(pkt->rto);
00468     pkt->rto = jid_new(sess->c2s, -1);
00469 
00470     if(pkt->rto == NULL) {
00471         log_debug(ZONE, "invalid to address on packet, unable to route");
00472         pkt_free(pkt);
00473         return;
00474     }
00475 
00476     nad_set_attr(pkt->nad, 0, -1, "to", pkt->rto->domain, 0);
00477 
00478     if(pkt->rfrom != NULL)
00479         jid_free(pkt->rfrom);
00480     pkt->rfrom = jid_new(pkt->sm->id, -1);
00481 
00482     if(pkt->rfrom == NULL) {
00483         log_debug(ZONE, "invalid from address on packet, unable to route");
00484         pkt_free(pkt);
00485         return;
00486     }
00487 
00488     nad_set_attr(pkt->nad, 0, -1, "from", pkt->rfrom->domain, 0);
00489 
00490     ret = mm_out_sess(pkt->sm->mm, sess, pkt);
00491     switch(ret) {
00492         case mod_HANDLED:
00493             return;
00494 
00495         case mod_PASS:
00496             sess_route(sess, pkt);
00497 
00498             break;
00499 
00500         default:
00501             pkt_router(pkt_error(pkt, -ret));
00502 
00503             break;
00504     }
00505 }
00506 
00508 void pkt_delay(pkt_t pkt, time_t t, const char *from) {
00509     char timestamp[21];
00510     int ns, elem;
00511 
00512 #ifdef ENABLE_SUPERSEDED
00513     datetime_out(t, dt_LEGACY, timestamp, 18);
00514     ns = nad_add_namespace(pkt->nad, uri_DELAY, NULL);
00515     elem = nad_insert_elem(pkt->nad, 1, ns, "x", NULL);
00516     nad_set_attr(pkt->nad, elem, -1, "stamp", timestamp, 0);
00517     if(from != NULL)
00518         nad_set_attr(pkt->nad, elem, -1, "from", from, 0);
00519     log_debug(ZONE, "added pkt XEP-0091 delay stamp %s", timestamp);
00520 #endif
00521     datetime_out(t, dt_DATETIME, timestamp, 21);
00522     ns = nad_add_namespace(pkt->nad, uri_URN_DELAY, NULL);
00523     elem = nad_insert_elem(pkt->nad, 1, ns, "delay", NULL);
00524     nad_set_attr(pkt->nad, elem, -1, "stamp", timestamp, 0);
00525     if(from != NULL)
00526         nad_set_attr(pkt->nad, elem, -1, "from", from, 0);
00527     log_debug(ZONE, "added pkt XEP-0203 delay stamp %s", timestamp);
00528 }