Changeset 5ebff60 for protocols/jabber/conference.c
- Timestamp:
- 2015-02-20T22:50:54Z (9 years ago)
- Branches:
- master
- Children:
- 0b9daac, 3d45471, 7733b8c
- Parents:
- af359b4
- git-author:
- Indent <please@…> (19-02-15 05:47:20)
- git-committer:
- dequis <dx@…> (20-02-15 22:50:54)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
protocols/jabber/conference.c
raf359b4 r5ebff60 25 25 #include "sha1.h" 26 26 27 static xt_status jabber_chat_join_failed( struct im_connection *ic, struct xt_node *node, struct xt_node *orig);28 29 struct groupchat *jabber_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password)27 static xt_status jabber_chat_join_failed(struct im_connection *ic, struct xt_node *node, struct xt_node *orig); 28 29 struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password) 30 30 { 31 31 struct jabber_chat *jc; … … 33 33 struct groupchat *c; 34 34 char *roomjid; 35 36 roomjid = g_strdup_printf( "%s/%s", room, nick);37 node = xt_new_node( "x", NULL, NULL);38 xt_add_attr( node, "xmlns", XMLNS_MUC);39 if ( password )40 xt_add_child( node, xt_new_node( "password", password, NULL ));41 node = jabber_make_packet( "presence", NULL, roomjid, node );42 jabber_cache_add( ic, node, jabber_chat_join_failed);43 44 if( !jabber_write_packet( ic, node ) ) 45 {46 g_free( roomjid);35 36 roomjid = g_strdup_printf("%s/%s", room, nick); 37 node = xt_new_node("x", NULL, NULL); 38 xt_add_attr(node, "xmlns", XMLNS_MUC); 39 if (password) { 40 xt_add_child(node, xt_new_node("password", password, NULL)); 41 } 42 node = jabber_make_packet("presence", NULL, roomjid, node); 43 jabber_cache_add(ic, node, jabber_chat_join_failed); 44 45 if (!jabber_write_packet(ic, node)) { 46 g_free(roomjid); 47 47 return NULL; 48 48 } 49 50 jc = g_new0( struct jabber_chat, 1 ); 51 jc->name = jabber_normalize( room ); 52 53 if( ( jc->me = jabber_buddy_add( ic, roomjid ) ) == NULL ) 54 { 55 g_free( roomjid ); 56 g_free( jc->name ); 57 g_free( jc ); 49 50 jc = g_new0(struct jabber_chat, 1); 51 jc->name = jabber_normalize(room); 52 53 if ((jc->me = jabber_buddy_add(ic, roomjid)) == NULL) { 54 g_free(roomjid); 55 g_free(jc->name); 56 g_free(jc); 58 57 return NULL; 59 58 } 60 59 61 60 /* roomjid isn't normalized yet, and we need an original version 62 61 of the nick to send a proper presence update. */ 63 62 jc->my_full_jid = roomjid; 64 65 c = imcb_chat_new( ic, room);63 64 c = imcb_chat_new(ic, room); 66 65 c->data = jc; 67 66 68 67 return c; 69 68 } 70 69 71 struct groupchat *jabber_chat_with( struct im_connection *ic, char *who)70 struct groupchat *jabber_chat_with(struct im_connection *ic, char *who) 72 71 { 73 72 struct jabber_data *jd = ic->proto_data; … … 77 76 double now = gettime(); 78 77 char *uuid, *rjid, *cserv; 79 80 sha1_init( &sum);81 sha1_append( &sum, (uint8_t*) ic->acc->user, strlen( ic->acc->user ));82 sha1_append( &sum, (uint8_t*) &now, sizeof( now ));83 sha1_append( &sum, (uint8_t*) who, strlen( who ));84 uuid = sha1_random_uuid( &sum);85 86 if ( jd->flags & JFLAG_GTALK )87 cserv = g_strdup( "groupchat.google.com");88 else78 79 sha1_init(&sum); 80 sha1_append(&sum, (uint8_t *) ic->acc->user, strlen(ic->acc->user)); 81 sha1_append(&sum, (uint8_t *) &now, sizeof(now)); 82 sha1_append(&sum, (uint8_t *) who, strlen(who)); 83 uuid = sha1_random_uuid(&sum); 84 85 if (jd->flags & JFLAG_GTALK) { 86 cserv = g_strdup("groupchat.google.com"); 87 } else { 89 88 /* Guess... */ 90 cserv = g_strdup_printf( "conference.%s", jd->server ); 91 92 rjid = g_strdup_printf( "private-chat-%s@%s", uuid, cserv ); 93 g_free( uuid ); 94 g_free( cserv ); 95 96 c = jabber_chat_join( ic, rjid, jd->username, NULL ); 97 g_free( rjid ); 98 if( c == NULL ) 89 cserv = g_strdup_printf("conference.%s", jd->server); 90 } 91 92 rjid = g_strdup_printf("private-chat-%s@%s", uuid, cserv); 93 g_free(uuid); 94 g_free(cserv); 95 96 c = jabber_chat_join(ic, rjid, jd->username, NULL); 97 g_free(rjid); 98 if (c == NULL) { 99 99 return NULL; 100 100 } 101 101 102 jc = c->data; 102 jc->invite = g_strdup( who);103 103 jc->invite = g_strdup(who); 104 104 105 return c; 105 106 } 106 107 107 static xt_status jabber_chat_join_failed( struct im_connection *ic, struct xt_node *node, struct xt_node *orig)108 static xt_status jabber_chat_join_failed(struct im_connection *ic, struct xt_node *node, struct xt_node *orig) 108 109 { 109 110 struct jabber_error *err; 110 111 struct jabber_buddy *bud; 111 112 char *room; 112 113 room = xt_find_attr( orig, "to");114 bud = jabber_buddy_by_jid( ic, room, 0);115 err = jabber_error_parse( xt_find_node( node->children, "error" ), XMLNS_STANZA_ERROR);116 if ( err )117 {118 imcb_error( ic, "Error joining groupchat %s: %s%s%s", room, err->code,119 err->text ? ": " : "", err->text ? err->text : "");120 jabber_error_free( err );121 }122 if( bud )123 jabber_chat_free( jabber_chat_by_jid( ic, bud->bare_jid ) );124 113 114 room = xt_find_attr(orig, "to"); 115 bud = jabber_buddy_by_jid(ic, room, 0); 116 err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR); 117 if (err) { 118 imcb_error(ic, "Error joining groupchat %s: %s%s%s", room, err->code, 119 err->text ? ": " : "", err->text ? err->text : ""); 120 jabber_error_free(err); 121 } 122 if (bud) { 123 jabber_chat_free(jabber_chat_by_jid(ic, bud->bare_jid)); 124 } 125 125 126 return XT_HANDLED; 126 127 } 127 128 128 struct groupchat *jabber_chat_by_jid( struct im_connection *ic, const char *name)129 { 130 char *normalized = jabber_normalize( name);129 struct groupchat *jabber_chat_by_jid(struct im_connection *ic, const char *name) 130 { 131 char *normalized = jabber_normalize(name); 131 132 GSList *l; 132 133 struct groupchat *ret; 133 134 struct jabber_chat *jc; 134 135 for( l = ic->groupchats; l; l = l->next ) 136 { 135 136 for (l = ic->groupchats; l; l = l->next) { 137 137 ret = l->data; 138 138 jc = ret->data; 139 if ( strcmp( normalized, jc->name ) == 0 )139 if (strcmp(normalized, jc->name) == 0) { 140 140 break; 141 } 142 g_free( normalized ); 143 141 } 142 } 143 g_free(normalized); 144 144 145 return l ? ret : NULL; 145 146 } 146 147 147 void jabber_chat_free( struct groupchat *c)148 void jabber_chat_free(struct groupchat *c) 148 149 { 149 150 struct jabber_chat *jc = c->data; 150 151 jabber_buddy_remove_bare( c->ic, jc->name);152 153 g_free( jc->my_full_jid);154 g_free( jc->name);155 g_free( jc->invite);156 g_free( jc);157 158 imcb_chat_free( c);159 } 160 161 int jabber_chat_msg( struct groupchat *c, char *message, int flags)151 152 jabber_buddy_remove_bare(c->ic, jc->name); 153 154 g_free(jc->my_full_jid); 155 g_free(jc->name); 156 g_free(jc->invite); 157 g_free(jc); 158 159 imcb_chat_free(c); 160 } 161 162 int jabber_chat_msg(struct groupchat *c, char *message, int flags) 162 163 { 163 164 struct im_connection *ic = c->ic; 164 165 struct jabber_chat *jc = c->data; 165 166 struct xt_node *node; 166 167 167 168 jc->flags |= JCFLAG_MESSAGE_SENT; 168 169 node = xt_new_node( "body", message, NULL ); 170 node = jabber_make_packet( "message", "groupchat", jc->name, node ); 171 172 if( !jabber_write_packet( ic, node ) ) 173 { 174 xt_free_node( node ); 169 170 node = xt_new_node("body", message, NULL); 171 node = jabber_make_packet("message", "groupchat", jc->name, node); 172 173 if (!jabber_write_packet(ic, node)) { 174 xt_free_node(node); 175 175 return 0; 176 176 } 177 xt_free_node( node);178 177 xt_free_node(node); 178 179 179 return 1; 180 180 } 181 181 182 int jabber_chat_topic( struct groupchat *c, char *topic)182 int jabber_chat_topic(struct groupchat *c, char *topic) 183 183 { 184 184 struct im_connection *ic = c->ic; 185 185 struct jabber_chat *jc = c->data; 186 186 struct xt_node *node; 187 188 node = xt_new_node( "subject", topic, NULL ); 189 node = jabber_make_packet( "message", "groupchat", jc->name, node ); 190 191 if( !jabber_write_packet( ic, node ) ) 192 { 193 xt_free_node( node ); 187 188 node = xt_new_node("subject", topic, NULL); 189 node = jabber_make_packet("message", "groupchat", jc->name, node); 190 191 if (!jabber_write_packet(ic, node)) { 192 xt_free_node(node); 194 193 return 0; 195 194 } 196 xt_free_node( node);197 195 xt_free_node(node); 196 198 197 return 1; 199 198 } 200 199 201 int jabber_chat_leave( struct groupchat *c, const char *reason)200 int jabber_chat_leave(struct groupchat *c, const char *reason) 202 201 { 203 202 struct im_connection *ic = c->ic; 204 203 struct jabber_chat *jc = c->data; 205 204 struct xt_node *node; 206 207 node = xt_new_node( "x", NULL, NULL ); 208 xt_add_attr( node, "xmlns", XMLNS_MUC ); 209 node = jabber_make_packet( "presence", "unavailable", jc->my_full_jid, node ); 210 211 if( !jabber_write_packet( ic, node ) ) 212 { 213 xt_free_node( node ); 205 206 node = xt_new_node("x", NULL, NULL); 207 xt_add_attr(node, "xmlns", XMLNS_MUC); 208 node = jabber_make_packet("presence", "unavailable", jc->my_full_jid, node); 209 210 if (!jabber_write_packet(ic, node)) { 211 xt_free_node(node); 214 212 return 0; 215 213 } 216 xt_free_node( node);217 214 xt_free_node(node); 215 218 216 return 1; 219 217 } 220 218 221 void jabber_chat_invite( struct groupchat *c, char *who, char *message)219 void jabber_chat_invite(struct groupchat *c, char *who, char *message) 222 220 { 223 221 struct xt_node *node; … … 225 223 struct jabber_chat *jc = c->data; 226 224 227 node = xt_new_node( "reason", message, NULL );228 229 node = xt_new_node( "invite", NULL, node);230 xt_add_attr( node, "to", who );231 232 node = xt_new_node( "x", NULL, node );233 xt_add_attr( node, "xmlns", XMLNS_MUC_USER );234 235 node = jabber_make_packet( "message", NULL, jc->name, node );236 237 jabber_write_packet( ic, node );238 239 xt_free_node( node);225 node = xt_new_node("reason", message, NULL); 226 227 node = xt_new_node("invite", NULL, node); 228 xt_add_attr(node, "to", who); 229 230 node = xt_new_node("x", NULL, node); 231 xt_add_attr(node, "xmlns", XMLNS_MUC_USER); 232 233 node = jabber_make_packet("message", NULL, jc->name, node); 234 235 jabber_write_packet(ic, node); 236 237 xt_free_node(node); 240 238 } 241 239 … … 244 242 parameters so we won't have to repeat too many things done by the caller 245 243 already. */ 246 void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node)244 void jabber_chat_pkt_presence(struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node) 247 245 { 248 246 struct groupchat *chat; 249 247 struct xt_node *c; 250 char *type = xt_find_attr( node, "type");248 char *type = xt_find_attr(node, "type"); 251 249 struct jabber_data *jd = ic->proto_data; 252 250 struct jabber_chat *jc; 253 251 char *s; 254 255 if( ( chat = jabber_chat_by_jid( ic, bud->bare_jid ) ) == NULL ) 256 { 252 253 if ((chat = jabber_chat_by_jid(ic, bud->bare_jid)) == NULL) { 257 254 /* How could this happen?? We could do kill( self, 11 ) 258 255 now or just wait for the OS to do it. :-) */ 259 256 return; 260 257 } 261 258 262 259 jc = chat->data; 263 264 if( type == NULL && !( bud->flags & JBFLAG_IS_CHATROOM ) ) 265 { 260 261 if (type == NULL && !(bud->flags & JBFLAG_IS_CHATROOM)) { 266 262 bud->flags |= JBFLAG_IS_CHATROOM; 267 263 /* If this one wasn't set yet, this buddy just joined the chat. 268 264 Slightly hackish way of finding out eh? ;-) */ 269 265 270 266 /* This is pretty messy... Here it sets ext_jid to the real 271 267 JID of the participant. Works for non-anonymized channels. 272 268 Might break if someone joins a chat twice, though. */ 273 for( c = node->children; ( c = xt_find_node( c, "x" ) ); c = c->next ) 274 if( ( s = xt_find_attr( c, "xmlns" ) ) && 275 ( strcmp( s, XMLNS_MUC_USER ) == 0 ) ) 276 { 269 for (c = node->children; (c = xt_find_node(c, "x")); c = c->next) { 270 if ((s = xt_find_attr(c, "xmlns")) && 271 (strcmp(s, XMLNS_MUC_USER) == 0)) { 277 272 struct xt_node *item; 278 279 item = xt_find_node( c->children, "item" ); 280 if( ( s = xt_find_attr( item, "jid" ) ) ) 281 { 273 274 item = xt_find_node(c->children, "item"); 275 if ((s = xt_find_attr(item, "jid"))) { 282 276 /* Yay, found what we need. :-) */ 283 bud->ext_jid = jabber_normalize( s);277 bud->ext_jid = jabber_normalize(s); 284 278 break; 285 279 } 286 280 } 287 281 } 282 288 283 /* Make up some other handle, if necessary. */ 289 if( bud->ext_jid == NULL ) 290 { 291 if( bud == jc->me ) 292 { 293 bud->ext_jid = g_strdup( jd->me ); 294 } 295 else 296 { 284 if (bud->ext_jid == NULL) { 285 if (bud == jc->me) { 286 bud->ext_jid = g_strdup(jd->me); 287 } else { 297 288 int i; 298 289 299 290 /* Don't want the nick to be at the end, so let's 300 291 think of some slightly different notation to use 301 292 for anonymous groupchat participants in BitlBee. */ 302 bud->ext_jid = g_strdup_printf( "%s=%s", bud->resource, bud->bare_jid);303 293 bud->ext_jid = g_strdup_printf("%s=%s", bud->resource, bud->bare_jid); 294 304 295 /* And strip any unwanted characters. */ 305 for ( i = 0; bud->resource[i]; i ++ )306 if ( bud->ext_jid[i] == '=' || bud->ext_jid[i] == '@' )296 for (i = 0; bud->resource[i]; i++) { 297 if (bud->ext_jid[i] == '=' || bud->ext_jid[i] == '@') { 307 298 bud->ext_jid[i] = '_'; 308 299 } 300 } 301 309 302 /* Some program-specific restrictions. */ 310 imcb_clean_handle( ic, bud->ext_jid);303 imcb_clean_handle(ic, bud->ext_jid); 311 304 } 312 305 bud->flags |= JBFLAG_IS_ANONYMOUS; 313 306 } 314 315 if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS ) 316 { 307 308 if (bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS) { 317 309 /* If JIDs are anonymized, add them to the local 318 310 list for the duration of this chat. */ 319 imcb_add_buddy( ic, bud->ext_jid, NULL ); 320 imcb_buddy_nick_hint( ic, bud->ext_jid, bud->resource ); 321 } 322 323 if( bud == jc->me && jc->invite != NULL ) 324 { 325 char *msg = g_strdup_printf( "Please join me in room %s", jc->name ); 326 jabber_chat_invite( chat, jc->invite, msg ); 327 g_free( jc->invite ); 328 g_free( msg ); 311 imcb_add_buddy(ic, bud->ext_jid, NULL); 312 imcb_buddy_nick_hint(ic, bud->ext_jid, bud->resource); 313 } 314 315 if (bud == jc->me && jc->invite != NULL) { 316 char *msg = g_strdup_printf("Please join me in room %s", jc->name); 317 jabber_chat_invite(chat, jc->invite, msg); 318 g_free(jc->invite); 319 g_free(msg); 329 320 jc->invite = NULL; 330 321 } 331 332 s = strchr( bud->ext_jid, '/' ); 333 if( s ) *s = 0; /* Should NEVER be NULL, but who knows... */ 334 imcb_chat_add_buddy( chat, bud->ext_jid ); 335 if( s ) *s = '/'; 336 } 337 else if( type ) /* type can only be NULL or "unavailable" in this function */ 338 { 339 if( ( bud->flags & JBFLAG_IS_CHATROOM ) && bud->ext_jid ) 340 { 341 s = strchr( bud->ext_jid, '/' ); 342 if( s ) *s = 0; 343 imcb_chat_remove_buddy( chat, bud->ext_jid, NULL ); 344 if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS ) 345 imcb_remove_buddy( ic, bud->ext_jid, NULL ); 346 if( s ) *s = '/'; 347 } 348 349 if( bud == jc->me ) 350 jabber_chat_free( chat ); 351 } 352 } 353 354 void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node ) 355 { 356 struct xt_node *subject = xt_find_node( node->children, "subject" ); 357 struct xt_node *body = xt_find_node( node->children, "body" ); 358 struct groupchat *chat = bud ? jabber_chat_by_jid( ic, bud->bare_jid ) : NULL; 322 323 s = strchr(bud->ext_jid, '/'); 324 if (s) { 325 *s = 0; /* Should NEVER be NULL, but who knows... */ 326 } 327 imcb_chat_add_buddy(chat, bud->ext_jid); 328 if (s) { 329 *s = '/'; 330 } 331 } else if (type) { /* type can only be NULL or "unavailable" in this function */ 332 if ((bud->flags & JBFLAG_IS_CHATROOM) && bud->ext_jid) { 333 s = strchr(bud->ext_jid, '/'); 334 if (s) { 335 *s = 0; 336 } 337 imcb_chat_remove_buddy(chat, bud->ext_jid, NULL); 338 if (bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS) { 339 imcb_remove_buddy(ic, bud->ext_jid, NULL); 340 } 341 if (s) { 342 *s = '/'; 343 } 344 } 345 346 if (bud == jc->me) { 347 jabber_chat_free(chat); 348 } 349 } 350 } 351 352 void jabber_chat_pkt_message(struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node) 353 { 354 struct xt_node *subject = xt_find_node(node->children, "subject"); 355 struct xt_node *body = xt_find_node(node->children, "body"); 356 struct groupchat *chat = bud ? jabber_chat_by_jid(ic, bud->bare_jid) : NULL; 359 357 struct jabber_chat *jc = chat ? chat->data : NULL; 360 358 char *s; 361 362 if( subject && chat ) 363 { 364 s = bud ? strchr( bud->ext_jid, '/' ) : NULL; 365 if( s ) *s = 0; 366 imcb_chat_topic( chat, bud ? bud->ext_jid : NULL, subject->text_len > 0 ? 367 subject->text : NULL, jabber_get_timestamp( node ) ); 368 if( s ) *s = '/'; 369 } 370 371 if( bud == NULL || ( jc && ~jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me ) ) 372 { 359 360 if (subject && chat) { 361 s = bud ? strchr(bud->ext_jid, '/') : NULL; 362 if (s) { 363 *s = 0; 364 } 365 imcb_chat_topic(chat, bud ? bud->ext_jid : NULL, subject->text_len > 0 ? 366 subject->text : NULL, jabber_get_timestamp(node)); 367 if (s) { 368 *s = '/'; 369 } 370 } 371 372 if (bud == NULL || (jc && ~jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me)) { 373 373 char *nick; 374 375 if ( body == NULL || body->text_len == 0 )374 375 if (body == NULL || body->text_len == 0) { 376 376 /* Meh. Empty messages aren't very interesting, no matter 377 377 how much some servers love to send them. */ 378 378 return; 379 380 s = xt_find_attr( node, "from" ); /* pkt_message() already NULL-checked this one. */ 381 nick = strchr( s, '/' );382 if( nick )383 {379 } 380 381 s = xt_find_attr(node, "from"); /* pkt_message() already NULL-checked this one. */ 382 nick = strchr(s, '/'); 383 if (nick) { 384 384 /* If this message included a resource/nick we don't know, 385 385 we might still know the groupchat itself. */ 386 386 *nick = 0; 387 chat = jabber_chat_by_jid( ic, s);387 chat = jabber_chat_by_jid(ic, s); 388 388 *nick = '/'; 389 390 nick ++; 391 } 392 else 393 { 389 390 nick++; 391 } else { 394 392 /* message.c uses the EXACT_JID option, so bud should 395 393 always be NULL here for bare JIDs. */ 396 chat = jabber_chat_by_jid( ic, s ); 397 } 398 399 if( nick == NULL ) 400 { 394 chat = jabber_chat_by_jid(ic, s); 395 } 396 397 if (nick == NULL) { 401 398 /* This is fine, the groupchat itself isn't in jd->buddies. */ 402 if( chat ) 403 imcb_chat_log( chat, "From conference server: %s", body->text ); 404 else 405 imcb_log( ic, "System message from unknown groupchat %s: %s", s, body->text ); 406 } 407 else 408 { 399 if (chat) { 400 imcb_chat_log(chat, "From conference server: %s", body->text); 401 } else { 402 imcb_log(ic, "System message from unknown groupchat %s: %s", s, body->text); 403 } 404 } else { 409 405 /* This can happen too, at least when receiving a backlog when 410 406 just joining a channel. */ 411 if( chat ) 412 imcb_chat_log( chat, "Message from unknown participant %s: %s", nick, body->text ); 413 else 414 imcb_log( ic, "Groupchat message from unknown JID %s: %s", s, body->text ); 415 } 416 407 if (chat) { 408 imcb_chat_log(chat, "Message from unknown participant %s: %s", nick, body->text); 409 } else { 410 imcb_log(ic, "Groupchat message from unknown JID %s: %s", s, body->text); 411 } 412 } 413 417 414 return; 418 } 419 else if( chat == NULL ) 420 { 415 } else if (chat == NULL) { 421 416 /* How could this happen?? We could do kill( self, 11 ) 422 417 now or just wait for the OS to do it. :-) */ 423 418 return; 424 419 } 425 if( body && body->text_len > 0 ) 426 { 427 s = strchr( bud->ext_jid, '/' ); 428 if( s ) *s = 0; 429 imcb_chat_msg( chat, bud->ext_jid, body->text, 0, jabber_get_timestamp( node ) ); 430 if( s ) *s = '/'; 431 } 432 } 420 if (body && body->text_len > 0) { 421 s = strchr(bud->ext_jid, '/'); 422 if (s) { 423 *s = 0; 424 } 425 imcb_chat_msg(chat, bud->ext_jid, body->text, 0, jabber_get_timestamp(node)); 426 if (s) { 427 *s = '/'; 428 } 429 } 430 }
Note: See TracChangeset
for help on using the changeset viewer.