jabberd2  2.2.17
server.c
Go to the documentation of this file.
1 /*
2  * jabberd - Jabber Open Source Server
3  * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
4  * Ryan Eatmon, Robert Norris
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, MA02111-1307USA
19  */
20 
21 #include "sx.h"
22 
23 static void _sx_server_notify_header(sx_t s, void *arg) {
24  int i, ns, len;
25  nad_t nad;
26  char *c;
27  sx_buf_t buf;
28 
29  _sx_debug(ZONE, "stream established");
30 
31  /* get the plugins to setup */
32  if(s->env != NULL)
33  for(i = 0; i < s->env->nplugins; i++)
34  if(s->env->plugins[i]->stream != NULL)
35  (s->env->plugins[i]->stream)(s, s->env->plugins[i]);
36 
37  /* bump us to stream if a plugin didn't do it already */
38  if(s->state < state_STREAM) {
40  _sx_event(s, event_STREAM, NULL);
41  }
42 
43  /* next, build the features */
44  if(s->req_version != NULL && strcmp(s->req_version, "1.0") == 0) {
45  _sx_debug(ZONE, "building features nad");
46 
47  nad = nad_new();
48 
49  ns = nad_add_namespace(nad, uri_STREAMS, "stream");
50  nad_append_elem(nad, ns, "features", 0);
51 
52  /* get the plugins to populate it */
53  if(s->env != NULL)
54  for(i = 0; i < s->env->nplugins; i++)
55  if(s->env->plugins[i]->features != NULL)
56  (s->env->plugins[i]->features)(s, s->env->plugins[i], nad);
57 
58  /* new buffer for the nad */
59  nad_print(nad, 0, &c, &len);
60  buf = _sx_buffer_new(c, len, NULL, NULL);
61  nad_free(nad);
62 
63  /* send this off too */
64  /* !!! should this go via wnad/rnad? */
65  jqueue_push(s->wbufq, buf, 0);
66  s->want_write = 1;
67  }
68 
69  /* if they sent packets before the stream was established, process the now */
70  if(jqueue_size(s->rnadq) > 0 && (s->state == state_STREAM || s->state == state_OPEN)) {
71  _sx_debug(ZONE, "processing packets sent before stream, naughty them");
72  _sx_process_read(s, _sx_buffer_new(c, 0, NULL, NULL));
73  }
74 }
75 
76 static void _sx_server_element_start(void *arg, const char *name, const char **atts) {
77  sx_t s = (sx_t) arg;
78  int tflag = 0, fflag = 0, vflag = 0, len, i, r;
79  const char **attr;
80  char *c, id[41];
81  sx_buf_t buf;
82  sx_error_t sxe;
83 
84  if(s->fail) return;
85 
86  /* check element and namespace */
87  i = strlen(uri_STREAMS) + 7;
88  if(strlen(name) < i || strncmp(name, uri_STREAMS "|stream", i) != 0 || (name[i] != '\0' && name[i] != '|')) {
89  /* throw an error */
90  _sx_gen_error(sxe, SX_ERR_STREAM, "Stream error", "Expected stream start");
91  _sx_event(s, event_ERROR, (void *) &sxe);
93  s->fail = 1;
94  return;
95  }
96 
97  /* pull interesting things out of the header */
98  attr = atts;
99  while(attr[0] != NULL) {
100  if(!tflag && strcmp(attr[0], "to") == 0) {
101  if(s->req_to != NULL) free(s->req_to);
102  s->req_to = strdup(attr[1]);
103  tflag = 1;
104  }
105 
106  if(!fflag && strcmp(attr[0], "from") == 0) {
107  s->req_from = strdup(attr[1]);
108  fflag = 1;
109  }
110 
111  if(!vflag && strcmp(attr[0], "version") == 0) {
112  s->req_version = strdup(attr[1]);
113  vflag = 1;
114  }
115 
116  attr += 2;
117  }
118 
119  _sx_debug(ZONE, "stream request: to %s from %s version %s", s->req_to, s->req_from, s->req_version);
120 
121  /* check version */
122  if(s->req_version != NULL && strcmp(s->req_version, "1.0") != 0) {
123  /* throw an error */
124  _sx_gen_error(sxe, SX_ERR_STREAM, "Stream error", "Unsupported version");
125  _sx_event(s, event_ERROR, (void *) &sxe);
127  s->fail = 1;
128  return;
129  }
130 
131  /* !!! get the app to verify this stuff? */
132 
133  /* bump */
135 
136  /* response attributes */
137  if(s->req_to != NULL) s->res_from = strdup(s->req_to);
138  if(s->req_from != NULL) s->res_to = strdup(s->req_from);
139 
140  /* Only send 1.0 version if client has indicated a stream version - c/f XMPP 4.4.1 para 4 */
141  if(s->req_version != NULL) s->res_version = strdup("1.0");
142 
143  /* stream id */
144  for(i = 0; i < 40; i++) {
145  r = (int) (36.0 * rand() / RAND_MAX);
146  id[i] = (r >= 0 && r <= 9) ? (r + 48) : (r + 87);
147  }
148  id[40] = '\0';
149 
150  s->id = strdup(id);
151 
152  _sx_debug(ZONE, "stream id is %s", id);
153 
154  /* build the response */
155  len = strlen(uri_STREAMS) + 99;
156 
157  if(s->ns != NULL) len += 9 + strlen(s->ns);
158  if(s->res_to != NULL) len += 6 + strlen(s->res_to);
159  if(s->res_from != NULL) len += 8 + strlen(s->res_from);
160  if(s->res_version != NULL) len += 11 + strlen(s->res_version);
161 
162  buf = _sx_buffer_new(NULL, len, _sx_server_notify_header, NULL);
163 
164  c = buf->data;
165  strcpy(c, "<?xml version='1.0'?><stream:stream xmlns:stream='" uri_STREAMS "'");
166 
167  if(s->ns != NULL) { c = strchr(c, '\0'); sprintf(c, " xmlns='%s'", s->ns); }
168  if(s->res_to != NULL) { c = strchr(c, '\0'); sprintf(c, " to='%s'", s->res_to); }
169  if(s->res_from != NULL) { c = strchr(c, '\0'); sprintf(c, " from='%s'", s->res_from); }
170  if(s->res_version != NULL) { c = strchr(c, '\0'); sprintf(c, " version='%s'", s->res_version); }
171 
172  c = strchr(c, '\0'); sprintf(c, " id='%s'>", id);
173  assert(buf->len == strlen(buf->data) + 1); /* post-facto overrun detection */
174  buf->len --;
175 
176  /* plugins can mess with the header too */
177  if(s->env != NULL)
178  for(i = 0; i < s->env->nplugins; i++)
179  if(s->env->plugins[i]->header != NULL)
180  (s->env->plugins[i]->header)(s, s->env->plugins[i], buf);
181 
182  _sx_debug(ZONE, "prepared stream response: %.*s", buf->len, buf->data);
183 
184  /* off it goes */
185  jqueue_push(s->wbufq, buf, 0);
186 
187  s->depth++;
188 
189  /* we're alive */
190  XML_SetElementHandler(s->expat, (void *) _sx_element_start, (void *) _sx_element_end);
191  XML_SetCharacterDataHandler(s->expat, (void *) _sx_cdata);
192  XML_SetStartNamespaceDeclHandler(s->expat, (void *) _sx_namespace_start);
193 
194  /* we have stuff to write */
195  s->want_write = 1;
196 }
197 
198 static void _sx_server_element_end(void *arg, const char *name) {
199  sx_t s = (sx_t) arg;
200 
201  if(s->fail) return;
202 
203  s->depth--;
204 }
205 
207 static void _sx_server_ns_start(void *arg, const char *prefix, const char *uri) {
208  sx_t s = (sx_t) arg;
209 
210  /* only want the default namespace */
211  if(prefix != NULL)
212  return;
213 
214  /* sanity; MSXML-based clients have been known to send xmlns='' from time to time */
215  if(uri == NULL)
216  return;
217 
218  /* sanity check (should never happen if expat is doing its job) */
219  if(s->ns != NULL)
220  return;
221 
222  s->ns = strdup(uri);
223 
224  /* done */
225  XML_SetStartNamespaceDeclHandler(s->expat, NULL);
226 }
227 
228 void sx_server_init(sx_t s, unsigned int flags) {
229  int i;
230 
231  assert((int) (s != NULL));
232 
233  /* can't do anything if we're alive already */
234  if(s->state != state_NONE)
235  return;
236 
237  _sx_debug(ZONE, "doing server init for sx %d", s->tag);
238 
239  s->type = type_SERVER;
240  s->flags = flags;
241 
242  /* plugin */
243  if(s->env != NULL)
244  for(i = 0; i < s->env->nplugins; i++)
245  if(s->env->plugins[i]->server != NULL)
246  (s->env->plugins[i]->server)(s, s->env->plugins[i]);
247 
248  /* we want to read */
249  XML_SetElementHandler(s->expat, (void *) _sx_server_element_start, (void *) _sx_server_element_end);
250  XML_SetStartNamespaceDeclHandler(s->expat, (void *) _sx_server_ns_start);
251 
252  _sx_debug(ZONE, "waiting for stream header");
253 
254  s->want_read = 1;
255  _sx_event(s, event_WANT_READ, NULL);
256 }