Mercurial > pidgin
annotate src/protocols/jabber/message.c @ 8519:f8ab826e11ad
[gaim-migrate @ 9258]
" this patch will return the text for the smileys instead
of skipping them. This patch also gets rid of two
members in the gtkimhtml stuct that weren't being used
anywhere. They were gboolean smileys, comments; which
got replace with show_smileys, and show_comments. Also
an fixes the gtk_imhtml_class_init so that if it's
being compiled in c++, it will compile..." --Gary Kramlich
committer: Tailor Script <tailor@pidgin.im>
author | Luke Schierer <lschiere@pidgin.im> |
---|---|
date | Mon, 29 Mar 2004 16:20:30 +0000 |
parents | 0383e55cd658 |
children | dc818b62379e |
rev | line source |
---|---|
7014 | 1 /* |
2 * gaim - Jabber Protocol Plugin | |
3 * | |
4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> | |
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 */ | |
21 #include "internal.h" | |
22 | |
23 #include "debug.h" | |
24 #include "notify.h" | |
25 #include "server.h" | |
7095
c8bf2da398e3
[gaim-migrate @ 7660]
Christian Hammond <chipx86@chipx86.com>
parents:
7014
diff
changeset
|
26 #include "util.h" |
7014 | 27 |
28 #include "buddy.h" | |
29 #include "chat.h" | |
30 #include "message.h" | |
31 #include "xmlnode.h" | |
32 | |
33 #define JABBER_TYPING_NOTIFY_INT 15 | |
34 | |
35 void jabber_message_free(JabberMessage *jm) | |
36 { | |
37 if(jm->from) | |
38 g_free(jm->from); | |
39 if(jm->to) | |
40 g_free(jm->to); | |
41 if(jm->subject) | |
42 g_free(jm->subject); | |
43 if(jm->body) | |
44 g_free(jm->body); | |
45 if(jm->xhtml) | |
46 g_free(jm->xhtml); | |
47 if(jm->password) | |
48 g_free(jm->password); | |
7145 | 49 if(jm->etc) |
50 g_list_free(jm->etc); | |
7014 | 51 |
52 g_free(jm); | |
53 } | |
54 | |
7145 | 55 static void handle_chat(JabberMessage *jm) |
7014 | 56 { |
57 JabberID *jid = jabber_id_new(jm->from); | |
58 char *from; | |
59 | |
60 JabberBuddy *jb; | |
61 JabberBuddyResource *jbr; | |
62 | |
7310 | 63 if(!jid) |
64 return; | |
65 | |
7014 | 66 jb = jabber_buddy_find(jm->js, jm->from, TRUE); |
7306 | 67 jbr = jabber_buddy_find_resource(jb, jid->resource); |
7014 | 68 |
8043 | 69 if(jabber_find_unnormalized_conv(jm->from, jm->js->gc->account)) { |
7014 | 70 from = g_strdup(jm->from); |
7258 | 71 } else if(jid->node) { |
72 GaimConversation *conv; | |
73 | |
7014 | 74 from = g_strdup_printf("%s@%s", jid->node, jid->domain); |
8043 | 75 conv = jabber_find_unnormalized_conv(from, jm->js->gc->account); |
7258 | 76 if(conv) |
77 gaim_conversation_set_name(conv, jm->from); | |
78 g_free(from); | |
79 from = g_strdup(jm->from); | |
80 } else { | |
7014 | 81 from = g_strdup(jid->domain); |
7258 | 82 } |
7014 | 83 |
84 if(!jm->xhtml && !jm->body) { | |
85 if(jm->events & JABBER_MESSAGE_EVENT_COMPOSING) | |
86 serv_got_typing(jm->js->gc, from, 0, GAIM_TYPING); | |
87 else | |
88 serv_got_typing_stopped(jm->js->gc, from); | |
89 } else { | |
8400 | 90 if(jbr) { |
91 if(jm->events & JABBER_MESSAGE_EVENT_COMPOSING) | |
92 jbr->capabilities |= JABBER_CAP_COMPOSING; | |
93 if(jbr->thread_id) | |
94 g_free(jbr->thread_id); | |
95 jbr->thread_id = g_strdup(jbr->thread_id); | |
96 } | |
7014 | 97 serv_got_im(jm->js->gc, from, jm->xhtml ? jm->xhtml : jm->body, 0, |
98 jm->sent); | |
99 } | |
100 | |
101 g_free(from); | |
102 jabber_id_free(jid); | |
103 } | |
104 | |
7145 | 105 static void handle_headline(JabberMessage *jm) |
106 { | |
107 char *title; | |
108 GString *body = g_string_new(""); | |
109 GList *etc; | |
110 | |
111 title = g_strdup_printf(_("Message from %s"), jm->from); | |
112 | |
113 if(jm->xhtml) | |
114 g_string_append(body, jm->xhtml); | |
115 else if(jm->body) | |
116 g_string_append(body, jm->body); | |
117 | |
118 for(etc = jm->etc; etc; etc = etc->next) { | |
119 xmlnode *x = etc->data; | |
120 const char *xmlns = xmlnode_get_attrib(x, "xmlns"); | |
121 if(xmlns && !strcmp(xmlns, "jabber:x:oob")) { | |
122 xmlnode *url, *desc; | |
123 char *urltxt, *desctxt; | |
124 | |
125 url = xmlnode_get_child(x, "url"); | |
126 desc = xmlnode_get_child(x, "desc"); | |
127 | |
128 if(!url || !desc) | |
129 continue; | |
130 | |
131 urltxt = xmlnode_get_data(url); | |
132 desctxt = xmlnode_get_data(desc); | |
133 | |
134 /* I'm all about ugly hacks */ | |
135 if(body->len && !strcmp(body->str, jm->body)) | |
136 g_string_printf(body, "<a href='%s'>%s</a>", | |
137 urltxt, desctxt); | |
138 else | |
139 g_string_append_printf(body, "<br/><a href='%s'>%s</a>", | |
140 urltxt, desctxt); | |
141 | |
142 g_free(urltxt); | |
143 g_free(desctxt); | |
144 } | |
145 } | |
146 | |
147 gaim_notify_formatted(jm->js->gc, title, jm->subject ? jm->subject : title, | |
148 NULL, body->str, NULL, NULL); | |
149 | |
150 g_free(title); | |
151 g_string_free(body, TRUE); | |
152 } | |
153 | |
154 static void handle_groupchat(JabberMessage *jm) | |
7014 | 155 { |
156 JabberID *jid = jabber_id_new(jm->from); | |
7310 | 157 JabberChat *chat; |
158 | |
159 if(!jid) | |
160 return; | |
161 | |
162 chat = jabber_chat_find(jm->js, jid->node, jid->domain); | |
7014 | 163 |
164 if(!chat) | |
165 return; | |
166 | |
7971 | 167 if(jm->subject) { |
7183 | 168 gaim_conv_chat_set_topic(GAIM_CONV_CHAT(chat->conv), jid->resource, |
169 jm->subject); | |
7971 | 170 if(!jm->xhtml && !jm->body) { |
171 char *msg; | |
172 if(jid->resource) | |
173 msg = g_strdup_printf(_("%s has set the topic to: %s"), jid->resource, jm->subject); | |
174 else | |
175 msg = g_strdup_printf(_("The topic is: %s"), jm->subject); | |
176 gaim_conv_chat_write(GAIM_CONV_CHAT(chat->conv), "", msg, GAIM_MESSAGE_SYSTEM, jm->sent); | |
177 g_free(msg); | |
178 } | |
179 } | |
7014 | 180 |
7630 | 181 if(jm->xhtml || jm->body) { |
182 if(jid->resource) | |
183 serv_got_chat_in(jm->js->gc, chat->id, jid->resource, 0, | |
184 jm->xhtml ? jm->xhtml : jm->body, jm->sent); | |
185 else if(chat->muc) | |
186 gaim_conv_chat_write(GAIM_CONV_CHAT(chat->conv), "", | |
187 jm->xhtml ? jm->xhtml : jm->body, | |
188 GAIM_MESSAGE_SYSTEM, jm->sent); | |
189 } | |
190 | |
7014 | 191 jabber_id_free(jid); |
192 } | |
193 | |
7145 | 194 static void handle_groupchat_invite(JabberMessage *jm) |
7014 | 195 { |
7310 | 196 GHashTable *components; |
7014 | 197 JabberID *jid = jabber_id_new(jm->to); |
198 | |
7310 | 199 if(!jid) |
200 return; | |
201 | |
202 components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); | |
203 | |
7332 | 204 g_hash_table_replace(components, g_strdup("room"), g_strdup(jid->node)); |
7448 | 205 g_hash_table_replace(components, g_strdup("server"), g_strdup(jid->domain)); |
7332 | 206 g_hash_table_replace(components, g_strdup("handle"), |
207 g_strdup(jm->js->user->node)); | |
208 g_hash_table_replace(components, g_strdup("password"), | |
209 g_strdup(jm->password)); | |
7014 | 210 |
211 jabber_id_free(jid); | |
212 serv_got_chat_invite(jm->js->gc, jm->to, jm->from, jm->body, components); | |
213 } | |
214 | |
7145 | 215 static void handle_error(JabberMessage *jm) |
7014 | 216 { |
217 char *buf; | |
218 | |
219 if(!jm->body) | |
220 return; | |
221 | |
222 buf = g_strdup_printf(_("Message delivery to %s failed: %s"), | |
223 jm->from, jm->error); | |
224 | |
7944 | 225 gaim_notify_formatted(jm->js->gc, _("Jabber Message Error"), _("Jabber Message Error"), buf, |
226 jm->xhtml ? jm->xhtml : jm->body, NULL, NULL); | |
7014 | 227 |
228 g_free(buf); | |
229 } | |
230 | |
231 void jabber_message_parse(JabberStream *js, xmlnode *packet) | |
232 { | |
233 JabberMessage *jm; | |
234 const char *type; | |
235 xmlnode *child; | |
236 | |
237 if(strcmp(packet->name, "message")) | |
238 return; | |
239 | |
240 jm = g_new0(JabberMessage, 1); | |
241 jm->js = js; | |
242 jm->sent = time(NULL); | |
243 | |
244 type = xmlnode_get_attrib(packet, "type"); | |
245 | |
246 if(type) { | |
247 if(!strcmp(type, "normal")) | |
248 jm->type = JABBER_MESSAGE_NORMAL; | |
249 else if(!strcmp(type, "chat")) | |
250 jm->type = JABBER_MESSAGE_CHAT; | |
251 else if(!strcmp(type, "groupchat")) | |
252 jm->type = JABBER_MESSAGE_GROUPCHAT; | |
253 else if(!strcmp(type, "headline")) | |
254 jm->type = JABBER_MESSAGE_HEADLINE; | |
255 else if(!strcmp(type, "error")) | |
256 jm->type = JABBER_MESSAGE_ERROR; | |
257 else | |
258 jm->type = JABBER_MESSAGE_OTHER; | |
259 } else { | |
260 jm->type = JABBER_MESSAGE_NORMAL; | |
261 } | |
262 | |
263 jm->from = g_strdup(xmlnode_get_attrib(packet, "from")); | |
264 jm->to = g_strdup(xmlnode_get_attrib(packet, "to")); | |
265 | |
266 for(child = packet->child; child; child = child->next) { | |
8135 | 267 if(child->type != XMLNODE_TYPE_TAG) |
7014 | 268 continue; |
269 | |
270 if(!strcmp(child->name, "subject")) { | |
271 if(!jm->subject) | |
272 jm->subject = xmlnode_get_data(child); | |
8400 | 273 } else if(!strcmp(child->name, "thread")) { |
274 if(!jm->thread_id) | |
275 jm->thread_id = xmlnode_get_data(child); | |
7014 | 276 } else if(!strcmp(child->name, "body")) { |
277 if(!jm->body) | |
7894 | 278 jm->body = xmlnode_to_str(child, NULL); |
7241 | 279 } else if(!strcmp(child->name, "html")) { |
280 if(!jm->xhtml) | |
7642 | 281 jm->xhtml = xmlnode_to_str(child, NULL); |
7014 | 282 } else if(!strcmp(child->name, "error")) { |
283 const char *code = xmlnode_get_attrib(child, "code"); | |
284 char *code_txt = NULL; | |
285 char *text = xmlnode_get_data(child); | |
286 | |
287 if(code) | |
288 code_txt = g_strdup_printf(_(" (Code %s)"), code); | |
289 | |
290 if(!jm->error) | |
291 jm->error = g_strdup_printf("%s%s", text ? text : "", | |
292 code_txt ? code_txt : ""); | |
293 | |
294 g_free(code_txt); | |
295 g_free(text); | |
296 } else if(!strcmp(child->name, "x")) { | |
297 const char *xmlns = xmlnode_get_attrib(child, "xmlns"); | |
298 if(xmlns && !strcmp(xmlns, "jabber:x:event")) { | |
299 if(xmlnode_get_child(child, "composing")) | |
300 jm->events |= JABBER_MESSAGE_EVENT_COMPOSING; | |
301 } else if(xmlns && !strcmp(xmlns, "jabber:x:delay")) { | |
302 const char *timestamp = xmlnode_get_attrib(child, "stamp"); | |
303 if(timestamp) | |
304 jm->sent = str_to_time(timestamp); | |
305 } else if(xmlns && !strcmp(xmlns, "jabber:x:conference") && | |
306 jm->type != JABBER_MESSAGE_GROUPCHAT_INVITE) { | |
307 const char *jid = xmlnode_get_attrib(child, "jid"); | |
308 if(jid) { | |
309 jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE; | |
310 g_free(jm->to); | |
311 jm->to = g_strdup(jid); | |
312 } | |
313 } else if(xmlns && !strcmp(xmlns, | |
314 "http://jabber.org/protocol/muc#user")) { | |
315 xmlnode *invite = xmlnode_get_child(child, "invite"); | |
316 if(invite) { | |
317 xmlnode *reason, *password; | |
7968 | 318 const char *jid = xmlnode_get_attrib(invite, "from"); |
7014 | 319 g_free(jm->to); |
320 jm->to = jm->from; | |
321 jm->from = g_strdup(jid); | |
322 if((reason = xmlnode_get_child(invite, "reason"))) { | |
323 g_free(jm->body); | |
324 jm->body = xmlnode_get_data(reason); | |
325 } | |
326 if((password = xmlnode_get_child(invite, "password"))) | |
327 jm->password = xmlnode_get_data(password); | |
328 | |
329 jm->type = JABBER_MESSAGE_GROUPCHAT_INVITE; | |
330 } | |
7145 | 331 } else { |
332 jm->etc = g_list_append(jm->etc, child); | |
7014 | 333 } |
334 } | |
335 } | |
336 | |
337 switch(jm->type) { | |
338 case JABBER_MESSAGE_NORMAL: | |
339 case JABBER_MESSAGE_CHAT: | |
7145 | 340 handle_chat(jm); |
341 break; | |
7014 | 342 case JABBER_MESSAGE_HEADLINE: |
7145 | 343 handle_headline(jm); |
7014 | 344 break; |
345 case JABBER_MESSAGE_GROUPCHAT: | |
346 handle_groupchat(jm); | |
347 break; | |
348 case JABBER_MESSAGE_GROUPCHAT_INVITE: | |
349 handle_groupchat_invite(jm); | |
350 break; | |
351 case JABBER_MESSAGE_ERROR: | |
352 handle_error(jm); | |
353 break; | |
354 case JABBER_MESSAGE_OTHER: | |
355 gaim_debug(GAIM_DEBUG_INFO, "jabber", | |
356 "Received message of unknown type: %s\n", type); | |
357 break; | |
358 } | |
359 jabber_message_free(jm); | |
360 } | |
361 | |
362 void jabber_message_send(JabberMessage *jm) | |
363 { | |
364 xmlnode *message, *child; | |
365 const char *type = NULL; | |
366 | |
367 message = xmlnode_new("message"); | |
368 | |
369 switch(jm->type) { | |
370 case JABBER_MESSAGE_NORMAL: | |
371 type = "normal"; | |
372 break; | |
373 case JABBER_MESSAGE_CHAT: | |
374 case JABBER_MESSAGE_GROUPCHAT_INVITE: | |
375 type = "chat"; | |
376 break; | |
377 case JABBER_MESSAGE_HEADLINE: | |
378 type = "headline"; | |
379 break; | |
380 case JABBER_MESSAGE_GROUPCHAT: | |
381 type = "groupchat"; | |
382 break; | |
383 case JABBER_MESSAGE_ERROR: | |
384 type = "error"; | |
385 break; | |
386 case JABBER_MESSAGE_OTHER: | |
387 type = NULL; | |
388 break; | |
389 } | |
390 | |
391 if(type) | |
392 xmlnode_set_attrib(message, "type", type); | |
393 | |
394 xmlnode_set_attrib(message, "to", jm->to); | |
395 | |
8400 | 396 if(jm->thread_id) { |
397 child = xmlnode_new_child(message, "thread"); | |
398 xmlnode_insert_data(child, jm->thread_id, -1); | |
399 } | |
400 | |
7971 | 401 if(jm->events || (!jm->body && !jm->xhtml && !jm->subject)) { |
7014 | 402 child = xmlnode_new_child(message, "x"); |
403 xmlnode_set_attrib(child, "xmlns", "jabber:x:event"); | |
404 if(jm->events & JABBER_MESSAGE_EVENT_COMPOSING) | |
405 xmlnode_new_child(child, "composing"); | |
406 } | |
407 | |
408 if(jm->subject) { | |
409 child = xmlnode_new_child(message, "subject"); | |
410 xmlnode_insert_data(child, jm->subject, -1); | |
411 } | |
412 | |
413 if(jm->body) { | |
414 child = xmlnode_new_child(message, "body"); | |
415 xmlnode_insert_data(child, jm->body, -1); | |
416 } | |
417 | |
418 if(jm->xhtml) { | |
419 child = xmlnode_from_str(jm->xhtml, -1); | |
420 if(child) { | |
421 xmlnode_insert_child(message, child); | |
422 } else { | |
423 gaim_debug(GAIM_DEBUG_ERROR, "jabber", | |
424 "XHTML translation/validation failed, returning: %s\n", | |
425 jm->xhtml); | |
426 } | |
427 } | |
428 | |
429 jabber_send(jm->js, message); | |
430 | |
431 xmlnode_free(message); | |
432 } | |
433 | |
434 int jabber_message_send_im(GaimConnection *gc, const char *who, const char *msg, | |
7118
bf630f7dfdcd
[gaim-migrate @ 7685]
Christian Hammond <chipx86@chipx86.com>
parents:
7095
diff
changeset
|
435 GaimConvImFlags flags) |
7014 | 436 { |
437 JabberMessage *jm; | |
438 JabberBuddy *jb; | |
439 JabberBuddyResource *jbr; | |
440 char *buf; | |
7135 | 441 char *xhtml; |
7306 | 442 char *resource; |
7014 | 443 |
444 if(!who || !msg) | |
445 return 0; | |
446 | |
7306 | 447 resource = jabber_get_resource(who); |
448 | |
7014 | 449 jb = jabber_buddy_find(gc->proto_data, who, TRUE); |
7306 | 450 jbr = jabber_buddy_find_resource(jb, resource); |
451 | |
452 g_free(resource); | |
7014 | 453 |
454 jm = g_new0(JabberMessage, 1); | |
455 jm->js = gc->proto_data; | |
456 jm->type = JABBER_MESSAGE_CHAT; | |
457 jm->events = JABBER_MESSAGE_EVENT_COMPOSING; | |
458 jm->to = g_strdup(who); | |
8400 | 459 if(jbr && jbr->thread_id) |
460 jm->thread_id = jbr->thread_id; | |
7014 | 461 |
7773 | 462 buf = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html>", msg); |
7014 | 463 |
7135 | 464 gaim_markup_html_to_xhtml(buf, &xhtml, &jm->body); |
7014 | 465 g_free(buf); |
466 | |
467 if(!jbr || jbr->capabilities & JABBER_CAP_XHTML) | |
468 jm->xhtml = xhtml; | |
469 else | |
470 g_free(xhtml); | |
471 | |
472 jabber_message_send(jm); | |
473 jabber_message_free(jm); | |
474 return 1; | |
475 } | |
476 | |
7345 | 477 int jabber_message_send_chat(GaimConnection *gc, int id, const char *msg) |
7014 | 478 { |
479 JabberChat *chat; | |
480 JabberMessage *jm; | |
8042 | 481 JabberStream *js; |
7345 | 482 char *buf, *xhtml; |
7014 | 483 |
8042 | 484 if(!msg || !gc) |
7014 | 485 return 0; |
486 | |
8042 | 487 js = gc->proto_data; |
7014 | 488 chat = jabber_chat_find_by_id(js, id); |
489 | |
8043 | 490 if(!chat) |
491 return 0; | |
492 | |
7923 | 493 if(!strcmp(msg, "/configure") || !strcmp(msg, "/config")) { |
494 jabber_chat_request_room_configure(chat); | |
495 return 1; | |
7955 | 496 } else if(!strcmp(msg, "/register")) { |
497 jabber_chat_register(chat); | |
7971 | 498 return 1; |
499 } else if(!strncmp(msg, "/topic", 6)) { | |
500 jabber_chat_change_topic(chat, strlen(msg) > 7 ? msg+7 : NULL); | |
501 return 1; | |
7972 | 502 } else if(!strncmp(msg, "/nick", 5)) { |
503 if(strlen(msg) > 6) | |
504 jabber_chat_change_nick(chat, msg+6); | |
505 return 1; | |
7974 | 506 } else if(!strncmp(msg, "/part", 5)) { |
507 jabber_chat_part(chat, strlen(msg) > 6 ? msg+6 : NULL); | |
508 return 1; | |
7923 | 509 } |
510 | |
7014 | 511 jm = g_new0(JabberMessage, 1); |
512 jm->js = gc->proto_data; | |
7444 | 513 jm->type = JABBER_MESSAGE_GROUPCHAT; |
7014 | 514 jm->to = g_strdup_printf("%s@%s", chat->room, chat->server); |
515 | |
7773 | 516 buf = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body xmlns='http://www.w3.org/1999/xhtml'>%s</body></html>", msg); |
7345 | 517 |
518 gaim_markup_html_to_xhtml(buf, &xhtml, &jm->body); | |
519 g_free(buf); | |
520 | |
521 if(chat->xhtml) | |
522 jm->xhtml = xhtml; | |
523 else | |
524 g_free(xhtml); | |
7014 | 525 |
526 jabber_message_send(jm); | |
527 jabber_message_free(jm); | |
528 return 1; | |
529 } | |
530 | |
531 int jabber_send_typing(GaimConnection *gc, const char *who, int typing) | |
532 { | |
533 JabberMessage *jm; | |
534 JabberBuddy *jb; | |
535 JabberBuddyResource *jbr; | |
7306 | 536 char *resource = jabber_get_resource(who); |
7014 | 537 |
538 jb = jabber_buddy_find(gc->proto_data, who, TRUE); | |
7306 | 539 jbr = jabber_buddy_find_resource(jb, resource); |
540 | |
541 g_free(resource); | |
7014 | 542 |
7187 | 543 if(!jbr || !(jbr->capabilities & JABBER_CAP_COMPOSING)) |
7014 | 544 return 0; |
545 | |
546 jm = g_new0(JabberMessage, 1); | |
547 jm->js = gc->proto_data; | |
548 jm->type = JABBER_MESSAGE_CHAT; | |
549 jm->to = g_strdup(who); | |
550 | |
551 if(typing == GAIM_TYPING) | |
552 jm->events = JABBER_MESSAGE_EVENT_COMPOSING; | |
553 | |
554 jabber_message_send(jm); | |
555 jabber_message_free(jm); | |
556 | |
557 return JABBER_TYPING_NOTIFY_INT; | |
558 } | |
559 |