Mercurial > pidgin
annotate libgaim/protocols/simple/simple.c @ 15113:4a8c368df4ea
[gaim-migrate @ 17899]
Some touchups:
* If one of the parallel connection attempts fails immediately (i.e.
does not time out) then don't cancel the other one.
* Make sure we don't continue on to step 2 of the peer connection
process after we kick off the parallel gaim_proxy_connects(). It
looks like this would happen most of the time, because the
connect_timeout_timer would be added for the verified ip, so it
would NOT be added for the client ip, and so we wouldn't hit the
"return" call because it happens to be in the same block as the
second gaim_timeout_add() call.
* Add the connection timeout timer even if the gaim_proxy_connect() to
the verified ip returns NULL for some crazy reason.
I didn't actually test any of this. I should probably do that when
I get home.
committer: Tailor Script <tailor@pidgin.im>
| author | Mark Doliner <mark@kingant.net> |
|---|---|
| date | Wed, 06 Dec 2006 01:29:59 +0000 |
| parents | 89ae31668a9c |
| children | c8957b9c6202 |
| rev | line source |
|---|---|
| 14192 | 1 /** |
| 2 * @file simple.c | |
| 3 * | |
| 4 * gaim | |
| 5 * | |
| 6 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de> | |
| 7 * | |
| 8 * *** | |
| 9 * Thanks to Google's Summer of Code Program and the helpful mentors | |
| 10 * *** | |
| 11 * | |
| 12 * This program is free software; you can redistribute it and/or modify | |
| 13 * it under the terms of the GNU General Public License as published by | |
| 14 * the Free Software Foundation; either version 2 of the License, or | |
| 15 * (at your option) any later version. | |
| 16 * | |
| 17 * This program is distributed in the hope that it will be useful, | |
| 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 20 * GNU General Public License for more details. | |
| 21 * | |
| 22 * You should have received a copy of the GNU General Public License | |
| 23 * along with this program; if not, write to the Free Software | |
| 24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 25 */ | |
| 26 | |
| 27 #include "internal.h" | |
| 28 | |
| 29 #include "accountopt.h" | |
| 30 #include "blist.h" | |
| 31 #include "conversation.h" | |
| 32 #include "dnsquery.h" | |
| 33 #include "debug.h" | |
| 34 #include "notify.h" | |
| 35 #include "privacy.h" | |
| 36 #include "prpl.h" | |
| 37 #include "plugin.h" | |
| 38 #include "util.h" | |
| 39 #include "version.h" | |
| 40 #include "network.h" | |
| 41 #include "xmlnode.h" | |
| 42 | |
| 43 #include "simple.h" | |
| 44 #include "sipmsg.h" | |
| 45 #include "dnssrv.h" | |
| 46 #include "ntlm.h" | |
| 47 | |
| 48 static char *gentag() { | |
| 49 return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF); | |
| 50 } | |
| 51 | |
| 52 static char *genbranch() { | |
| 53 return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X", | |
| 54 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, | |
| 55 rand() & 0xFFFF, rand() & 0xFFFF); | |
| 56 } | |
| 57 | |
| 58 static char *gencallid() { | |
| 59 return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx", | |
| 60 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, | |
| 61 rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, | |
| 62 rand() & 0xFFFF, rand() & 0xFFFF); | |
| 63 } | |
| 64 | |
| 65 static const char *simple_list_icon(GaimAccount *a, GaimBuddy *b) { | |
| 66 return "simple"; | |
| 67 } | |
| 68 | |
| 69 static void simple_keep_alive(GaimConnection *gc) { | |
| 70 struct simple_account_data *sip = gc->proto_data; | |
| 71 if(sip->udp) { /* in case of UDP send a packet only with a 0 byte to | |
| 72 remain in the NAT table */ | |
| 73 gchar buf[2] = {0, 0}; | |
| 74 gaim_debug_info("simple", "sending keep alive\n"); | |
| 75 sendto(sip->fd, buf, 1, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)); | |
| 76 } | |
| 77 return; | |
| 78 } | |
| 79 | |
| 80 static gboolean process_register_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc); | |
| 81 static void send_notify(struct simple_account_data *sip, struct simple_watcher *); | |
| 82 | |
| 83 static void send_publish(struct simple_account_data *sip); | |
| 84 | |
| 85 static void do_notifies(struct simple_account_data *sip) { | |
| 86 GSList *tmp = sip->watcher; | |
| 87 gaim_debug_info("simple", "do_notifies()\n"); | |
| 88 if((sip->republish != -1) || sip->republish < time(NULL)) { | |
| 89 if(gaim_account_get_bool(sip->account, "dopublish", TRUE)) { | |
| 90 send_publish(sip); | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 while(tmp) { | |
| 95 gaim_debug_info("simple", "notifying %s\n", ((struct simple_watcher*)tmp->data)->name); | |
| 96 send_notify(sip, tmp->data); | |
| 97 tmp = tmp->next; | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 static void simple_set_status(GaimAccount *account, GaimStatus *status) { | |
| 102 GaimStatusPrimitive primitive = gaim_status_type_get_primitive(gaim_status_get_type(status)); | |
| 103 struct simple_account_data *sip = NULL; | |
| 104 | |
| 105 if (!gaim_status_is_active(status)) | |
| 106 return; | |
| 107 | |
| 108 if (account->gc) | |
| 109 sip = account->gc->proto_data; | |
| 110 | |
| 111 if (sip) | |
| 112 { | |
| 113 g_free(sip->status); | |
| 114 if (primitive == GAIM_STATUS_AVAILABLE) | |
| 115 sip->status = g_strdup("available"); | |
| 116 else | |
| 117 sip->status = g_strdup("busy"); | |
| 118 | |
| 119 do_notifies(sip); | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 static struct sip_connection *connection_find(struct simple_account_data *sip, int fd) { | |
| 124 struct sip_connection *ret = NULL; | |
| 125 GSList *entry = sip->openconns; | |
| 126 while(entry) { | |
| 127 ret = entry->data; | |
| 128 if(ret->fd == fd) return ret; | |
| 129 entry = entry->next; | |
| 130 } | |
| 131 return NULL; | |
| 132 } | |
| 133 | |
| 134 static struct simple_watcher *watcher_find(struct simple_account_data *sip, | |
| 135 const gchar *name) { | |
| 136 struct simple_watcher *watcher; | |
| 137 GSList *entry = sip->watcher; | |
| 138 while(entry) { | |
| 139 watcher = entry->data; | |
| 140 if(!strcmp(name, watcher->name)) return watcher; | |
| 141 entry = entry->next; | |
| 142 } | |
| 143 return NULL; | |
| 144 } | |
| 145 | |
| 146 static struct simple_watcher *watcher_create(struct simple_account_data *sip, | |
| 147 const gchar *name, const gchar *callid, const gchar *ourtag, | |
| 148 const gchar *theirtag, gboolean needsxpidf) { | |
| 149 struct simple_watcher *watcher = g_new0(struct simple_watcher, 1); | |
| 150 watcher->name = g_strdup(name); | |
| 151 watcher->dialog.callid = g_strdup(callid); | |
| 152 watcher->dialog.ourtag = g_strdup(ourtag); | |
| 153 watcher->dialog.theirtag = g_strdup(theirtag); | |
| 154 watcher->needsxpidf = needsxpidf; | |
| 155 sip->watcher = g_slist_append(sip->watcher, watcher); | |
| 156 return watcher; | |
| 157 } | |
| 158 | |
| 159 static void watcher_remove(struct simple_account_data *sip, const gchar *name) { | |
| 160 struct simple_watcher *watcher = watcher_find(sip, name); | |
| 161 sip->watcher = g_slist_remove(sip->watcher, watcher); | |
| 162 g_free(watcher->name); | |
| 163 g_free(watcher->dialog.callid); | |
| 164 g_free(watcher->dialog.ourtag); | |
| 165 g_free(watcher->dialog.theirtag); | |
| 166 g_free(watcher); | |
| 167 } | |
| 168 | |
| 169 static struct sip_connection *connection_create(struct simple_account_data *sip, int fd) { | |
| 170 struct sip_connection *ret = g_new0(struct sip_connection, 1); | |
| 171 ret->fd = fd; | |
| 172 sip->openconns = g_slist_append(sip->openconns, ret); | |
| 173 return ret; | |
| 174 } | |
| 175 | |
| 176 static void connection_remove(struct simple_account_data *sip, int fd) { | |
| 177 struct sip_connection *conn = connection_find(sip, fd); | |
| 178 sip->openconns = g_slist_remove(sip->openconns, conn); | |
| 179 if(conn->inputhandler) gaim_input_remove(conn->inputhandler); | |
| 180 g_free(conn->inbuf); | |
| 181 g_free(conn); | |
| 182 } | |
| 183 | |
| 184 static void connection_free_all(struct simple_account_data *sip) { | |
| 185 struct sip_connection *ret = NULL; | |
| 186 GSList *entry = sip->openconns; | |
| 187 while(entry) { | |
| 188 ret = entry->data; | |
| 189 connection_remove(sip, ret->fd); | |
| 190 entry = sip->openconns; | |
| 191 } | |
| 192 } | |
| 193 | |
| 194 static void simple_add_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group) | |
| 195 { | |
| 196 struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data; | |
| 197 struct simple_buddy *b; | |
| 198 if(strncmp("sip:", buddy->name, 4)) { | |
| 199 gchar *buf = g_strdup_printf("sip:%s", buddy->name); | |
| 200 gaim_blist_rename_buddy(buddy, buf); | |
| 201 g_free(buf); | |
| 202 } | |
| 203 if(!g_hash_table_lookup(sip->buddies, buddy->name)) { | |
| 204 b = g_new0(struct simple_buddy, 1); | |
| 205 gaim_debug_info("simple", "simple_add_buddy %s\n", buddy->name); | |
| 206 b->name = g_strdup(buddy->name); | |
| 207 g_hash_table_insert(sip->buddies, b->name, b); | |
| 208 } else { | |
| 209 gaim_debug_info("simple", "buddy %s already in internal list\n", buddy->name); | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 static void simple_get_buddies(GaimConnection *gc) { | |
| 214 GaimBlistNode *gnode, *cnode, *bnode; | |
| 215 | |
| 216 gaim_debug_info("simple", "simple_get_buddies\n"); | |
| 217 | |
| 218 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) { | |
| 219 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) continue; | |
| 220 for(cnode = gnode->child; cnode; cnode = cnode->next) { | |
| 221 if(!GAIM_BLIST_NODE_IS_CONTACT(cnode)) continue; | |
| 222 for(bnode = cnode->child; bnode; bnode = bnode->next) { | |
| 223 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) continue; | |
| 224 if(((GaimBuddy*)bnode)->account == gc->account) | |
| 225 simple_add_buddy(gc, (GaimBuddy*)bnode, (GaimGroup *)gnode); | |
| 226 } | |
| 227 } | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 static void simple_remove_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group) | |
| 232 { | |
| 233 struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data; | |
| 234 struct simple_buddy *b = g_hash_table_lookup(sip->buddies, buddy->name); | |
| 235 g_hash_table_remove(sip->buddies, buddy->name); | |
| 236 g_free(b->name); | |
| 237 g_free(b); | |
| 238 } | |
| 239 | |
| 240 static GList *simple_status_types(GaimAccount *acc) { | |
| 241 GaimStatusType *type; | |
| 242 GList *types = NULL; | |
| 243 | |
| 244 type = gaim_status_type_new_with_attrs( | |
| 245 GAIM_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE, | |
| 246 "message", _("Message"), gaim_value_new(GAIM_TYPE_STRING), | |
| 247 NULL); | |
| 248 types = g_list_append(types, type); | |
| 249 | |
| 250 type = gaim_status_type_new_full( | |
| 251 GAIM_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE); | |
| 252 types = g_list_append(types, type); | |
| 253 | |
| 254 return types; | |
| 255 } | |
| 256 | |
| 257 static gchar *auth_header(struct simple_account_data *sip, | |
| 258 struct sip_auth *auth, const gchar *method, const gchar *target) { | |
| 259 gchar noncecount[9]; | |
| 260 gchar *response; | |
| 261 gchar *ret; | |
| 262 gchar *tmp; | |
| 263 const char *authdomain; | |
| 264 const char *authuser; | |
| 265 | |
| 266 authdomain = gaim_account_get_string(sip->account, "authdomain", ""); | |
| 267 authuser = gaim_account_get_string(sip->account, "authuser", sip->username); | |
| 268 | |
| 269 if(!authuser || strlen(authuser) < 1) { | |
| 270 authuser = sip->username; | |
| 271 } | |
| 272 | |
| 273 if(auth->type == 1) { /* Digest */ | |
| 274 sprintf(noncecount, "%08d", auth->nc++); | |
| 275 response = gaim_cipher_http_digest_calculate_response( | |
| 276 "md5", method, target, NULL, NULL, | |
| 277 auth->nonce, noncecount, NULL, auth->digest_session_key); | |
| 278 gaim_debug(GAIM_DEBUG_MISC, "simple", "response %s\n", response); | |
| 279 | |
| 280 ret = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"\r\n", authuser, auth->realm, auth->nonce, target, noncecount, response); | |
| 281 g_free(response); | |
| 282 return ret; | |
| 283 } else if(auth->type == 2) { /* NTLM */ | |
| 284 if(auth->nc == 3 && auth->nonce) { | |
| 285 /* TODO: Don't hardcode "gaim" as the hostname */ | |
| 286 ret = gaim_ntlm_gen_type3(authuser, sip->password, "gaim", authdomain, (const guint8 *)auth->nonce, &auth->flags); | |
| 287 tmp = g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"\r\n", auth->opaque, auth->realm, auth->target, ret); | |
| 288 g_free(ret); | |
| 289 return tmp; | |
| 290 } | |
| 291 tmp = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"\r\n", auth->realm, auth->target); | |
| 292 return tmp; | |
| 293 } | |
| 294 | |
| 295 sprintf(noncecount, "%08d", auth->nc++); | |
| 296 response = gaim_cipher_http_digest_calculate_response( | |
| 297 "md5", method, target, NULL, NULL, | |
| 298 auth->nonce, noncecount, NULL, auth->digest_session_key); | |
| 299 gaim_debug(GAIM_DEBUG_MISC, "simple", "response %s\n", response); | |
| 300 | |
| 301 ret = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"\r\n", authuser, auth->realm, auth->nonce, target, noncecount, response); | |
| 302 g_free(response); | |
| 303 return ret; | |
| 304 } | |
| 305 | |
| 306 static char *parse_attribute(const char *attrname, const char *source) { | |
| 307 const char *tmp, *tmp2; | |
| 308 char *retval = NULL; | |
| 309 int len = strlen(attrname); | |
| 310 | |
| 311 if(!strncmp(source, attrname, len)) { | |
| 312 tmp = source + len; | |
| 313 tmp2 = g_strstr_len(tmp, strlen(tmp), "\""); | |
| 314 if(tmp2) | |
| 315 retval = g_strndup(tmp, tmp2 - tmp); | |
| 316 else | |
| 317 retval = g_strdup(tmp); | |
| 318 } | |
| 319 | |
| 320 return retval; | |
| 321 } | |
| 322 | |
| 323 static void fill_auth(struct simple_account_data *sip, gchar *hdr, struct sip_auth *auth) { | |
| 324 int i = 0; | |
| 325 const char *authuser; | |
| 326 char *tmp; | |
| 327 gchar **parts; | |
| 328 | |
| 329 authuser = gaim_account_get_string(sip->account, "authuser", sip->username); | |
| 330 | |
| 331 if(!authuser || strlen(authuser) < 1) { | |
| 332 authuser = sip->username; | |
| 333 } | |
| 334 | |
| 335 if(!hdr) { | |
| 336 gaim_debug_error("simple", "fill_auth: hdr==NULL\n"); | |
| 337 return; | |
| 338 } | |
| 339 | |
| 340 if(!g_strncasecmp(hdr, "NTLM", 4)) { | |
| 341 gaim_debug_info("simple", "found NTLM\n"); | |
| 342 auth->type = 2; | |
| 343 parts = g_strsplit(hdr+5, "\", ", 0); | |
| 344 i = 0; | |
| 345 while(parts[i]) { | |
| 346 gaim_debug_info("simple", "parts[i] %s\n", parts[i]); | |
| 347 if((tmp = parse_attribute("gssapi-data=\"", parts[i]))) { | |
| 348 auth->nonce = g_memdup(gaim_ntlm_parse_type2(tmp, &auth->flags), 8); | |
| 349 g_free(tmp); | |
| 350 } | |
| 351 if((tmp = parse_attribute("targetname=\"", | |
| 352 parts[i]))) { | |
| 353 auth->target = tmp; | |
| 354 } | |
| 355 else if((tmp = parse_attribute("realm=\"", | |
| 356 parts[i]))) { | |
| 357 auth->realm = tmp; | |
| 358 } | |
| 359 else if((tmp = parse_attribute("opaque=\"", parts[i]))) { | |
| 360 auth->opaque = tmp; | |
| 361 } | |
| 362 i++; | |
| 363 } | |
| 364 g_strfreev(parts); | |
| 365 auth->nc = 1; | |
| 366 if(!strstr(hdr, "gssapi-data")) { | |
| 367 auth->nc = 1; | |
| 368 } else { | |
| 369 auth->nc = 3; | |
| 370 } | |
| 371 return; | |
| 372 } | |
| 373 | |
| 374 auth->type = 1; | |
| 375 parts = g_strsplit(hdr, " ", 0); | |
| 376 while(parts[i]) { | |
| 377 if((tmp = parse_attribute("nonce=\"", parts[i]))) { | |
| 378 auth->nonce = tmp; | |
| 379 } | |
| 380 else if((tmp = parse_attribute("realm=\"", parts[i]))) { | |
| 381 auth->realm = tmp; | |
| 382 } | |
| 383 i++; | |
| 384 } | |
| 385 g_strfreev(parts); | |
| 386 | |
| 387 gaim_debug(GAIM_DEBUG_MISC, "simple", "nonce: %s realm: %s\n", auth->nonce ? auth->nonce : "(null)", auth->realm ? auth->realm : "(null)"); | |
| 388 if(auth->realm) { | |
| 389 auth->digest_session_key = gaim_cipher_http_digest_calculate_session_key( | |
| 390 "md5", authuser, auth->realm, sip->password, auth->nonce, NULL); | |
| 391 | |
| 392 auth->nc = 1; | |
| 393 } | |
| 394 } | |
| 395 | |
| 396 static void simple_canwrite_cb(gpointer data, gint source, GaimInputCondition cond) { | |
| 397 GaimConnection *gc = data; | |
| 398 struct simple_account_data *sip = gc->proto_data; | |
| 399 gsize max_write; | |
| 400 gssize written; | |
| 401 | |
| 402 max_write = gaim_circ_buffer_get_max_read(sip->txbuf); | |
| 403 | |
| 404 if(max_write == 0) { | |
| 405 gaim_input_remove(sip->tx_handler); | |
| 406 sip->tx_handler = 0; | |
| 407 return; | |
| 408 } | |
| 409 | |
| 410 written = write(sip->fd, sip->txbuf->outptr, max_write); | |
| 411 | |
| 412 if(written < 0 && errno == EAGAIN) | |
| 413 written = 0; | |
| 414 else if(written <= 0) { | |
| 415 /*TODO: do we really want to disconnect on a failure to write?*/ | |
| 416 gaim_connection_error(gc, _("Could not write")); | |
| 417 return; | |
| 418 } | |
| 419 | |
| 420 gaim_circ_buffer_mark_read(sip->txbuf, written); | |
| 421 } | |
| 422 | |
| 423 static void simple_input_cb(gpointer data, gint source, GaimInputCondition cond); | |
| 424 | |
| 425 static void send_later_cb(gpointer data, gint source, const gchar *error) { | |
| 426 GaimConnection *gc = data; | |
| 427 struct simple_account_data *sip; | |
| 428 struct sip_connection *conn; | |
| 429 | |
| 430 if (!GAIM_CONNECTION_IS_VALID(gc)) | |
| 431 { | |
| 432 if (source >= 0) | |
| 433 close(source); | |
| 434 return; | |
| 435 } | |
| 436 | |
| 437 if(source < 0) { | |
| 438 gaim_connection_error(gc, _("Could not connect")); | |
| 439 return; | |
| 440 } | |
| 441 | |
| 442 sip = gc->proto_data; | |
| 443 sip->fd = source; | |
| 444 sip->connecting = FALSE; | |
| 445 | |
| 446 simple_canwrite_cb(gc, sip->fd, GAIM_INPUT_WRITE); | |
| 447 | |
| 448 /* If there is more to write now, we need to register a handler */ | |
| 449 if(sip->txbuf->bufused > 0) | |
| 450 sip->tx_handler = gaim_input_add(sip->fd, GAIM_INPUT_WRITE, | |
| 451 simple_canwrite_cb, gc); | |
| 452 | |
| 453 conn = connection_create(sip, source); | |
| 454 conn->inputhandler = gaim_input_add(sip->fd, GAIM_INPUT_READ, simple_input_cb, gc); | |
| 455 } | |
| 456 | |
| 457 | |
| 458 static void sendlater(GaimConnection *gc, const char *buf) { | |
| 459 struct simple_account_data *sip = gc->proto_data; | |
| 460 | |
| 461 if(!sip->connecting) { | |
| 462 gaim_debug_info("simple", "connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport); | |
| 14837 | 463 if (gaim_proxy_connect(gc, sip->account, sip->realhostname, sip->realport, send_later_cb, gc) == NULL) { |
| 14192 | 464 gaim_connection_error(gc, _("Couldn't create socket")); |
| 465 } | |
| 466 sip->connecting = TRUE; | |
| 467 } | |
| 468 | |
| 469 if(gaim_circ_buffer_get_max_read(sip->txbuf) > 0) | |
| 470 gaim_circ_buffer_append(sip->txbuf, "\r\n", 2); | |
| 471 | |
| 472 gaim_circ_buffer_append(sip->txbuf, buf, strlen(buf)); | |
| 473 } | |
| 474 | |
| 475 static void sendout_pkt(GaimConnection *gc, const char *buf) { | |
| 476 struct simple_account_data *sip = gc->proto_data; | |
| 477 time_t currtime = time(NULL); | |
| 478 int writelen = strlen(buf); | |
| 479 | |
| 480 gaim_debug(GAIM_DEBUG_MISC, "simple", "\n\nsending - %s\n######\n%s\n######\n\n", ctime(&currtime), buf); | |
| 481 if(sip->udp) { | |
| 482 if(sendto(sip->fd, buf, writelen, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)) < writelen) { | |
| 483 gaim_debug_info("simple", "could not send packet\n"); | |
| 484 } | |
| 485 } else { | |
| 486 int ret; | |
| 487 if(sip->fd < 0) { | |
| 488 sendlater(gc, buf); | |
| 489 return; | |
| 490 } | |
| 491 | |
| 492 if(sip->tx_handler) { | |
| 493 ret = -1; | |
| 494 errno = EAGAIN; | |
| 495 } else | |
| 496 ret = write(sip->fd, buf, writelen); | |
| 497 | |
| 498 if (ret < 0 && errno == EAGAIN) | |
| 499 ret = 0; | |
| 500 else if(ret <= 0) { /* XXX: When does this happen legitimately? */ | |
| 501 sendlater(gc, buf); | |
| 502 return; | |
| 503 } | |
| 504 | |
| 505 if (ret < writelen) { | |
| 506 if(!sip->tx_handler) | |
| 507 sip->tx_handler = gaim_input_add(sip->fd, | |
| 508 GAIM_INPUT_WRITE, simple_canwrite_cb, | |
| 509 gc); | |
| 510 | |
| 511 /* XXX: is it OK to do this? You might get part of a request sent | |
| 512 with part of another. */ | |
| 513 if(sip->txbuf->bufused > 0) | |
| 514 gaim_circ_buffer_append(sip->txbuf, "\r\n", 2); | |
| 515 | |
| 516 gaim_circ_buffer_append(sip->txbuf, buf + ret, | |
| 517 writelen - ret); | |
| 518 } | |
| 519 } | |
| 520 } | |
| 521 | |
|
14634
eb2152e14df9
[gaim-migrate @ 17380]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14542
diff
changeset
|
522 static int simple_send_raw(GaimConnection *gc, const char *buf, int len) |
| 14542 | 523 { |
| 524 sendout_pkt(gc, buf); | |
| 525 return len; | |
| 526 } | |
| 527 | |
| 14192 | 528 static void sendout_sipmsg(struct simple_account_data *sip, struct sipmsg *msg) { |
| 529 GSList *tmp = msg->headers; | |
| 530 gchar *name; | |
| 531 gchar *value; | |
| 532 GString *outstr = g_string_new(""); | |
| 533 g_string_append_printf(outstr, "%s %s SIP/2.0\r\n", msg->method, msg->target); | |
| 534 while(tmp) { | |
| 535 name = ((struct siphdrelement*) (tmp->data))->name; | |
| 536 value = ((struct siphdrelement*) (tmp->data))->value; | |
| 537 g_string_append_printf(outstr, "%s: %s\r\n", name, value); | |
| 538 tmp = g_slist_next(tmp); | |
| 539 } | |
| 540 g_string_append_printf(outstr, "\r\n%s", msg->body ? msg->body : ""); | |
| 541 sendout_pkt(sip->gc, outstr->str); | |
| 542 g_string_free(outstr, TRUE); | |
| 543 } | |
| 544 | |
| 545 static void send_sip_response(GaimConnection *gc, struct sipmsg *msg, int code, | |
| 546 const char *text, const char *body) { | |
| 547 GSList *tmp = msg->headers; | |
| 548 gchar *name; | |
| 549 gchar *value; | |
| 550 GString *outstr = g_string_new(""); | |
| 551 | |
| 552 /* When sending the acknowlegements and errors, the content length from the original | |
| 553 message is still here, but there is no body; we need to make sure we're sending the | |
| 554 correct content length */ | |
| 555 sipmsg_remove_header(msg, "Content-Length"); | |
| 556 if(body) { | |
| 557 gchar len[12]; | |
| 558 sprintf(len, "%" G_GSIZE_FORMAT , strlen(body)); | |
| 559 sipmsg_add_header(msg, "Content-Length", len); | |
| 560 } | |
| 561 else | |
| 562 sipmsg_add_header(msg, "Content-Length", "0"); | |
| 563 g_string_append_printf(outstr, "SIP/2.0 %d %s\r\n", code, text); | |
| 564 while(tmp) { | |
| 565 name = ((struct siphdrelement*) (tmp->data))->name; | |
| 566 value = ((struct siphdrelement*) (tmp->data))->value; | |
| 567 | |
| 568 g_string_append_printf(outstr, "%s: %s\r\n", name, value); | |
| 569 tmp = g_slist_next(tmp); | |
| 570 } | |
| 571 g_string_append_printf(outstr, "\r\n%s", body ? body : ""); | |
| 572 sendout_pkt(gc, outstr->str); | |
| 573 g_string_free(outstr, TRUE); | |
| 574 } | |
| 575 | |
| 576 static void transactions_remove(struct simple_account_data *sip, struct transaction *trans) { | |
| 577 if(trans->msg) sipmsg_free(trans->msg); | |
| 578 sip->transactions = g_slist_remove(sip->transactions, trans); | |
| 579 g_free(trans); | |
| 580 } | |
| 581 | |
| 582 static void transactions_add_buf(struct simple_account_data *sip, const gchar *buf, void *callback) { | |
| 583 struct transaction *trans = g_new0(struct transaction, 1); | |
| 584 trans->time = time(NULL); | |
| 585 trans->msg = sipmsg_parse_msg(buf); | |
| 586 trans->cseq = sipmsg_find_header(trans->msg, "CSeq"); | |
| 587 trans->callback = callback; | |
| 588 sip->transactions = g_slist_append(sip->transactions, trans); | |
| 589 } | |
| 590 | |
| 591 static struct transaction *transactions_find(struct simple_account_data *sip, struct sipmsg *msg) { | |
| 592 struct transaction *trans; | |
| 593 GSList *transactions = sip->transactions; | |
| 594 gchar *cseq = sipmsg_find_header(msg, "CSeq"); | |
| 595 | |
| 596 while(transactions) { | |
| 597 trans = transactions->data; | |
| 598 if(!strcmp(trans->cseq, cseq)) { | |
| 599 return trans; | |
| 600 } | |
| 601 transactions = transactions->next; | |
| 602 } | |
| 603 | |
| 604 return NULL; | |
| 605 } | |
| 606 | |
| 607 static void send_sip_request(GaimConnection *gc, const gchar *method, | |
| 608 const gchar *url, const gchar *to, const gchar *addheaders, | |
| 609 const gchar *body, struct sip_dialog *dialog, TransCallback tc) { | |
| 610 struct simple_account_data *sip = gc->proto_data; | |
| 611 char *callid = dialog ? g_strdup(dialog->callid) : gencallid(); | |
| 612 char *auth = NULL; | |
| 613 const char *addh = ""; | |
| 614 gchar *branch = genbranch(); | |
| 615 gchar *tag = NULL; | |
| 616 char *buf; | |
| 617 | |
| 618 if(!strcmp(method, "REGISTER")) { | |
| 619 if(sip->regcallid) { | |
| 620 g_free(callid); | |
| 621 callid = g_strdup(sip->regcallid); | |
| 622 } | |
| 623 else sip->regcallid = g_strdup(callid); | |
| 624 } | |
| 625 | |
| 626 if(addheaders) addh = addheaders; | |
| 627 if(sip->registrar.type && !strcmp(method, "REGISTER")) { | |
| 628 buf = auth_header(sip, &sip->registrar, method, url); | |
| 629 auth = g_strdup_printf("Authorization: %s", buf); | |
| 630 g_free(buf); | |
| 631 gaim_debug(GAIM_DEBUG_MISC, "simple", "header %s", auth); | |
| 632 } | |
| 633 | |
| 634 if(sip->proxy.type && strcmp(method, "REGISTER")) { | |
| 635 buf = auth_header(sip, &sip->proxy, method, url); | |
| 636 auth = g_strdup_printf("Proxy-Authorization: %s", buf); | |
| 637 g_free(buf); | |
| 638 gaim_debug(GAIM_DEBUG_MISC, "simple", "header %s", auth); | |
| 639 } | |
| 640 | |
| 641 if (!dialog) | |
| 642 tag = gentag(); | |
| 643 | |
| 644 buf = g_strdup_printf("%s %s SIP/2.0\r\n" | |
| 645 "Via: SIP/2.0/%s %s:%d;branch=%s\r\n" | |
| 646 /* Don't know what epid is, but LCS wants it */ | |
| 647 "From: <sip:%s@%s>;tag=%s;epid=1234567890\r\n" | |
| 648 "To: <%s>%s%s\r\n" | |
| 649 "Max-Forwards: 10\r\n" | |
| 650 "CSeq: %d %s\r\n" | |
| 651 "User-Agent: Gaim/" VERSION "\r\n" | |
| 652 "Call-ID: %s\r\n" | |
| 653 "%s%s" | |
| 654 "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n%s", | |
| 655 method, | |
| 656 url, | |
| 657 sip->udp ? "UDP" : "TCP", | |
| 658 gaim_network_get_my_ip(-1), | |
| 659 sip->listenport, | |
| 660 branch, | |
| 661 sip->username, | |
| 662 sip->servername, | |
| 663 dialog ? dialog->ourtag : tag, | |
| 664 to, | |
| 665 dialog ? ";tag=" : "", | |
| 666 dialog ? dialog->theirtag : "", | |
| 667 ++sip->cseq, | |
| 668 method, | |
| 669 callid, | |
| 670 auth ? auth : "", | |
| 671 addh, | |
| 672 strlen(body), | |
| 673 body); | |
| 674 | |
| 675 g_free(tag); | |
| 676 g_free(auth); | |
| 677 g_free(branch); | |
| 678 g_free(callid); | |
| 679 | |
| 680 /* add to ongoing transactions */ | |
| 681 | |
| 682 transactions_add_buf(sip, buf, tc); | |
| 683 | |
| 684 sendout_pkt(gc, buf); | |
| 685 | |
| 686 g_free(buf); | |
| 687 } | |
| 688 | |
| 689 static char *get_contact(struct simple_account_data *sip) { | |
| 690 return g_strdup_printf("<sip:%s@%s:%d;transport=%s>;methods=\"MESSAGE, SUBSCRIBE, NOTIFY\"", sip->username, gaim_network_get_my_ip(-1), sip->listenport, sip->udp ? "udp" : "tcp"); | |
| 691 } | |
| 692 | |
| 693 static void do_register_exp(struct simple_account_data *sip, int expire) { | |
| 694 char *uri = g_strdup_printf("sip:%s", sip->servername); | |
| 695 char *to = g_strdup_printf("sip:%s@%s", sip->username, sip->servername); | |
| 696 char *contact = get_contact(sip); | |
| 697 char *hdr = g_strdup_printf("Contact: %s\r\nExpires: %d\r\n", contact, expire); | |
| 698 g_free(contact); | |
| 699 | |
| 700 sip->registerstatus = 1; | |
| 701 | |
| 702 if(expire) { | |
| 703 sip->reregister = time(NULL) + expire - 50; | |
| 704 } else { | |
| 705 sip->reregister = time(NULL) + 600; | |
| 706 } | |
| 707 | |
| 708 send_sip_request(sip->gc, "REGISTER", uri, to, hdr, "", NULL, | |
| 709 process_register_response); | |
| 710 | |
| 711 g_free(hdr); | |
| 712 g_free(uri); | |
| 713 g_free(to); | |
| 714 } | |
| 715 | |
| 716 static void do_register(struct simple_account_data *sip) { | |
| 717 do_register_exp(sip, sip->registerexpire); | |
| 718 } | |
| 719 | |
| 720 static gchar *parse_from(const gchar *hdr) { | |
| 721 gchar *from; | |
| 722 const gchar *tmp, *tmp2 = hdr; | |
| 723 | |
| 724 if(!hdr) return NULL; | |
| 725 gaim_debug_info("simple", "parsing address out of %s\n", hdr); | |
| 726 tmp = strchr(hdr, '<'); | |
| 727 | |
| 728 /* i hate the different SIP UA behaviours... */ | |
| 729 if(tmp) { /* sip address in <...> */ | |
| 730 tmp2 = tmp + 1; | |
| 731 tmp = strchr(tmp2, '>'); | |
| 732 if(tmp) { | |
| 733 from = g_strndup(tmp2, tmp - tmp2); | |
| 734 } else { | |
| 735 gaim_debug_info("simple", "found < without > in From\n"); | |
| 736 return NULL; | |
| 737 } | |
| 738 } else { | |
| 739 tmp = strchr(tmp2, ';'); | |
| 740 if(tmp) { | |
| 741 from = g_strndup(tmp2, tmp - tmp2); | |
| 742 } else { | |
| 743 from = g_strdup(tmp2); | |
| 744 } | |
| 745 } | |
| 746 gaim_debug_info("simple", "got %s\n", from); | |
| 747 return from; | |
| 748 } | |
| 749 | |
| 750 static gboolean process_subscribe_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) { | |
| 751 gchar *to; | |
| 752 | |
| 753 if(msg->response == 200 || msg->response == 202) { | |
| 754 return TRUE; | |
| 755 } | |
| 756 | |
| 757 to = parse_from(sipmsg_find_header(tc->msg, "To")); /* cant be NULL since it is our own msg */ | |
| 758 | |
| 759 /* we can not subscribe -> user is offline (TODO unknown status?) */ | |
| 760 | |
| 761 gaim_prpl_got_user_status(sip->account, to, "offline", NULL); | |
| 762 g_free(to); | |
| 763 return TRUE; | |
| 764 } | |
| 765 | |
| 766 static void simple_subscribe(struct simple_account_data *sip, struct simple_buddy *buddy) { | |
| 767 gchar *contact = "Expires: 1200\r\nAccept: application/pidf+xml, application/xpidf+xml\r\nEvent: presence\r\n"; | |
| 768 gchar *to; | |
| 769 gchar *tmp; | |
| 770 | |
| 771 if(strstr(buddy->name, "sip:")) | |
| 772 to = g_strdup(buddy->name); | |
| 773 else | |
| 774 to = g_strdup_printf("sip:%s", buddy->name); | |
| 775 | |
| 776 tmp = get_contact(sip); | |
| 777 contact = g_strdup_printf("%sContact: %s\r\n", contact, tmp); | |
| 778 g_free(tmp); | |
| 779 | |
| 780 /* subscribe to buddy presence | |
| 781 * we dont need to know the status so we do not need a callback */ | |
| 782 | |
| 783 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL, | |
| 784 process_subscribe_response); | |
| 785 | |
| 786 g_free(to); | |
| 787 g_free(contact); | |
| 788 | |
| 789 /* resubscribe before subscription expires */ | |
| 790 /* add some jitter */ | |
| 791 buddy->resubscribe = time(NULL)+1140+(rand()%50); | |
| 792 } | |
| 793 | |
| 794 static gboolean simple_add_lcs_contacts(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) { | |
| 795 gchar *tmp; | |
| 796 xmlnode *item, *group, *isc; | |
| 797 const char *name_group; | |
| 798 GaimBuddy *b; | |
| 799 GaimGroup *g = NULL; | |
| 800 struct simple_buddy *bs; | |
| 801 int len = msg->bodylen; | |
| 802 | |
| 803 | |
| 804 tmp = sipmsg_find_header(msg, "Event"); | |
| 805 if(tmp && !strncmp(tmp, "vnd-microsoft-roaming-contacts", 30)){ | |
| 806 | |
| 807 gaim_debug_info("simple", "simple_add_lcs_contacts->%s-%d\n", msg->body, len); | |
| 808 /*Convert the contact from XML to Gaim Buddies*/ | |
| 809 isc = xmlnode_from_str(msg->body, len); | |
| 810 | |
| 811 /* ToDo. Find for all groups */ | |
| 812 if ((group = xmlnode_get_child(isc, "group"))) { | |
| 813 name_group = xmlnode_get_attrib(group, "name"); | |
| 814 gaim_debug_info("simple", "name_group->%s\n", name_group); | |
| 815 g = gaim_find_group(name_group); | |
| 816 if(!g) | |
| 817 g = gaim_group_new(name_group); | |
| 818 } | |
| 819 | |
| 820 if (!g) { | |
| 821 g = gaim_find_group("Buddies"); | |
| 822 if(!g) | |
| 823 g = gaim_group_new("Buddies"); | |
| 824 } | |
| 825 | |
| 826 for(item = xmlnode_get_child(isc, "contact"); item; item = xmlnode_get_next_twin(item)) | |
| 827 { | |
| 828 const char *uri, *name, *groups; | |
| 829 char *buddy_name; | |
| 830 uri = xmlnode_get_attrib(item, "uri"); | |
| 831 name = xmlnode_get_attrib(item, "name"); | |
| 832 groups = xmlnode_get_attrib(item, "groups"); | |
| 833 gaim_debug_info("simple", "URI->%s\n", uri); | |
| 834 | |
| 835 buddy_name = g_strdup_printf("sip:%s", uri); | |
| 836 | |
| 837 b = gaim_find_buddy(sip->account, buddy_name); | |
| 838 if(!b){ | |
| 839 b = gaim_buddy_new(sip->account, buddy_name, uri); | |
| 840 } | |
| 841 g_free(buddy_name); | |
| 842 | |
| 843 gaim_blist_add_buddy(b, NULL, g, NULL); | |
| 844 gaim_blist_alias_buddy(b, uri); | |
| 845 bs = g_new0(struct simple_buddy, 1); | |
| 846 bs->name = g_strdup(b->name); | |
| 847 g_hash_table_insert(sip->buddies, bs->name, bs); | |
| 848 } | |
| 849 xmlnode_free(isc); | |
| 850 } | |
| 851 return 0; | |
| 852 } | |
| 853 | |
| 854 static void simple_subscribe_buddylist(struct simple_account_data *sip) { | |
| 855 gchar *contact = "Event: vnd-microsoft-roaming-contacts\r\nAccept: application/vnd-microsoft-roaming-contacts+xml\r\nSupported: com.microsoft.autoextend\r\nSupported: ms-benotify\r\nProxy-Require: ms-benotify\r\nSupported: ms-piggyback-first-notify\r\n"; | |
| 856 gchar *to; | |
| 857 gchar *tmp; | |
| 858 to = g_strdup_printf("sip:%s@%s", sip->username, sip->servername); | |
| 859 | |
| 860 tmp = get_contact(sip); | |
| 861 | |
| 862 contact = g_strdup_printf("%sContact: %s\r\n", contact, tmp); | |
| 863 g_free(tmp); | |
| 864 | |
| 865 send_sip_request(sip->gc, "SUBSCRIBE", to, to, contact, "", NULL, simple_add_lcs_contacts); | |
| 866 | |
| 867 g_free(to); | |
| 868 g_free(contact); | |
| 869 } | |
| 870 | |
| 871 | |
| 872 static void simple_buddy_resub(char *name, struct simple_buddy *buddy, struct simple_account_data *sip) { | |
| 873 time_t curtime = time(NULL); | |
| 874 gaim_debug_info("simple", "buddy resub\n"); | |
| 875 if(buddy->resubscribe < curtime) { | |
| 876 gaim_debug(GAIM_DEBUG_MISC, "simple", "simple_buddy_resub %s\n", name); | |
| 877 simple_subscribe(sip, buddy); | |
| 878 } | |
| 879 } | |
| 880 | |
| 881 static gboolean resend_timeout(struct simple_account_data *sip) { | |
| 882 GSList *tmp = sip->transactions; | |
| 883 time_t currtime = time(NULL); | |
| 884 while(tmp) { | |
| 885 struct transaction *trans = tmp->data; | |
| 886 tmp = tmp->next; | |
| 887 gaim_debug_info("simple", "have open transaction age: %d\n", currtime- trans->time); | |
| 888 if((currtime - trans->time > 5) && trans->retries >= 1) { | |
| 889 /* TODO 408 */ | |
| 890 } else { | |
| 891 if((currtime - trans->time > 2) && trans->retries == 0) { | |
| 892 trans->retries++; | |
| 893 sendout_sipmsg(sip, trans->msg); | |
| 894 } | |
| 895 } | |
| 896 } | |
| 897 return TRUE; | |
| 898 } | |
| 899 | |
| 900 static gboolean subscribe_timeout(struct simple_account_data *sip) { | |
| 901 GSList *tmp; | |
| 902 time_t curtime = time(NULL); | |
| 903 /* register again if first registration expires */ | |
| 904 if(sip->reregister < curtime) { | |
| 905 do_register(sip); | |
| 906 } | |
| 907 /* check for every subscription if we need to resubscribe */ | |
| 908 g_hash_table_foreach(sip->buddies, (GHFunc)simple_buddy_resub, (gpointer)sip); | |
| 909 | |
| 910 /* remove a timed out suscriber */ | |
| 911 | |
| 912 tmp = sip->watcher; | |
| 913 while(tmp) { | |
| 914 struct simple_watcher *watcher = tmp->data; | |
| 915 if(watcher->expire < curtime) { | |
| 916 watcher_remove(sip, watcher->name); | |
| 917 tmp = sip->watcher; | |
| 918 } | |
| 919 if(tmp) tmp = tmp->next; | |
| 920 } | |
| 921 | |
| 922 return TRUE; | |
| 923 } | |
| 924 | |
| 925 static void simple_send_message(struct simple_account_data *sip, const char *to, const char *msg, const char *type) { | |
| 926 gchar *hdr; | |
| 927 gchar *fullto; | |
| 928 if(strncmp("sip:", to, 4)) { | |
| 929 fullto = g_strdup_printf("sip:%s", to); | |
| 930 } else { | |
| 931 fullto = g_strdup(to); | |
| 932 } | |
| 933 if(type) { | |
| 934 hdr = g_strdup_printf("Content-Type: %s\r\n", type); | |
| 935 } else { | |
| 936 hdr = g_strdup("Content-Type: text/plain\r\n"); | |
| 937 } | |
| 938 send_sip_request(sip->gc, "MESSAGE", fullto, fullto, hdr, msg, NULL, NULL); | |
| 939 g_free(hdr); | |
| 940 g_free(fullto); | |
| 941 } | |
| 942 | |
| 943 static int simple_im_send(GaimConnection *gc, const char *who, const char *what, GaimMessageFlags flags) { | |
| 944 struct simple_account_data *sip = gc->proto_data; | |
| 945 char *to = g_strdup(who); | |
| 946 char *text = gaim_unescape_html(what); | |
| 947 simple_send_message(sip, to, text, NULL); | |
| 948 g_free(to); | |
| 949 g_free(text); | |
| 950 return 1; | |
| 951 } | |
| 952 | |
| 953 static void process_incoming_message(struct simple_account_data *sip, struct sipmsg *msg) { | |
| 954 gchar *from; | |
| 955 gchar *contenttype; | |
| 956 gboolean found = FALSE; | |
| 957 | |
| 958 from = parse_from(sipmsg_find_header(msg, "From")); | |
| 959 | |
| 960 if(!from) return; | |
| 961 | |
| 962 gaim_debug(GAIM_DEBUG_MISC, "simple", "got message from %s: %s\n", from, msg->body); | |
| 963 | |
| 964 contenttype = sipmsg_find_header(msg, "Content-Type"); | |
| 965 if(!contenttype || !strncmp(contenttype, "text/plain", 10) || !strncmp(contenttype, "text/html", 9)) { | |
| 966 serv_got_im(sip->gc, from, msg->body, 0, time(NULL)); | |
| 967 send_sip_response(sip->gc, msg, 200, "OK", NULL); | |
| 968 found = TRUE; | |
| 969 } | |
| 970 if(!strncmp(contenttype, "application/im-iscomposing+xml", 30)) { | |
| 971 xmlnode *isc = xmlnode_from_str(msg->body, msg->bodylen); | |
| 972 xmlnode *state; | |
| 973 gchar *statedata; | |
| 974 | |
| 975 if(!isc) { | |
| 976 gaim_debug_info("simple", "process_incoming_message: can not parse iscomposing\n"); | |
| 977 return; | |
| 978 } | |
| 979 | |
| 980 state = xmlnode_get_child(isc, "state"); | |
| 981 | |
| 982 if(!state) { | |
| 983 gaim_debug_info("simple", "process_incoming_message: no state found\n"); | |
| 984 xmlnode_free(isc); | |
| 985 return; | |
| 986 } | |
| 987 | |
| 988 statedata = xmlnode_get_data(state); | |
| 989 if(statedata) { | |
| 990 if(strstr(statedata, "active")) serv_got_typing(sip->gc, from, 0, GAIM_TYPING); | |
| 991 else serv_got_typing_stopped(sip->gc, from); | |
| 992 | |
| 993 g_free(statedata); | |
| 994 } | |
| 995 xmlnode_free(isc); | |
| 996 send_sip_response(sip->gc, msg, 200, "OK", NULL); | |
| 997 found = TRUE; | |
| 998 } | |
| 999 if(!found) { | |
| 1000 gaim_debug_info("simple", "got unknown mime-type"); | |
| 1001 send_sip_response(sip->gc, msg, 415, "Unsupported media type", NULL); | |
| 1002 } | |
| 1003 g_free(from); | |
| 1004 } | |
| 1005 | |
| 1006 | |
| 1007 gboolean process_register_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) { | |
| 1008 gchar *tmp; | |
| 1009 gaim_debug(GAIM_DEBUG_MISC, "simple", "in process register response response: %d\n", msg->response); | |
| 1010 switch (msg->response) { | |
| 1011 case 200: | |
| 1012 if(sip->registerstatus < 3) { /* registered */ | |
| 1013 if(gaim_account_get_bool(sip->account, "dopublish", TRUE)) { | |
| 1014 send_publish(sip); | |
| 1015 } | |
| 1016 } | |
| 1017 sip->registerstatus = 3; | |
| 1018 gaim_connection_set_state(sip->gc, GAIM_CONNECTED); | |
| 1019 | |
| 1020 /* get buddies from blist */ | |
| 1021 simple_get_buddies(sip->gc); | |
| 1022 | |
| 1023 subscribe_timeout(sip); | |
| 1024 tmp = sipmsg_find_header(msg, "Allow-Events"); | |
| 1025 if(tmp && strstr(tmp, "vnd-microsoft-provisioning")){ | |
| 1026 simple_subscribe_buddylist(sip); | |
| 1027 } | |
| 1028 | |
| 1029 break; | |
| 1030 case 401: | |
| 1031 if(sip->registerstatus != 2) { | |
| 1032 gaim_debug_info("simple", "REGISTER retries %d\n", sip->registrar.retries); | |
| 1033 if(sip->registrar.retries > 3) { | |
| 1034 sip->gc->wants_to_die = TRUE; | |
|
14920
478e64cf96fe
[gaim-migrate @ 17692]
Richard Laager <rlaager@wiktel.com>
parents:
14837
diff
changeset
|
1035 gaim_connection_error(sip->gc, _("Incorrect password.")); |
| 14192 | 1036 return TRUE; |
| 1037 } | |
| 1038 tmp = sipmsg_find_header(msg, "WWW-Authenticate"); | |
| 1039 fill_auth(sip, tmp, &sip->registrar); | |
| 1040 sip->registerstatus = 2; | |
| 1041 do_register(sip); | |
| 1042 } | |
| 1043 break; | |
| 1044 } | |
| 1045 return TRUE; | |
| 1046 } | |
| 1047 | |
| 1048 static void process_incoming_notify(struct simple_account_data *sip, struct sipmsg *msg) { | |
| 1049 gchar *from; | |
| 1050 gchar *fromhdr; | |
| 1051 gchar *tmp2; | |
| 1052 xmlnode *pidf; | |
| 1053 xmlnode *basicstatus = NULL, *tuple, *status; | |
| 1054 gboolean isonline = FALSE; | |
| 1055 | |
| 1056 fromhdr = sipmsg_find_header(msg, "From"); | |
| 1057 from = parse_from(fromhdr); | |
| 1058 if(!from) return; | |
| 1059 | |
| 1060 pidf = xmlnode_from_str(msg->body, msg->bodylen); | |
| 1061 | |
| 1062 if(!pidf) { | |
| 1063 gaim_debug_info("simple", "process_incoming_notify: no parseable pidf\n"); | |
| 1064 return; | |
| 1065 } | |
| 1066 | |
| 1067 if ((tuple = xmlnode_get_child(pidf, "tuple"))) | |
| 1068 if ((status = xmlnode_get_child(tuple, "status"))) | |
| 1069 basicstatus = xmlnode_get_child(status, "basic"); | |
| 1070 | |
| 1071 if(!basicstatus) { | |
| 1072 gaim_debug_info("simple", "process_incoming_notify: no basic found\n"); | |
| 1073 xmlnode_free(pidf); | |
| 1074 return; | |
| 1075 } | |
| 1076 | |
| 1077 tmp2 = xmlnode_get_data(basicstatus); | |
| 1078 | |
| 1079 if(!tmp2) { | |
| 1080 gaim_debug_info("simple", "process_incoming_notify: no basic data found\n"); | |
| 1081 xmlnode_free(pidf); | |
| 1082 return; | |
| 1083 } | |
| 1084 | |
| 1085 if(strstr(tmp2, "open")) { | |
| 1086 isonline = TRUE; | |
| 1087 } | |
| 1088 | |
| 1089 g_free(tmp2); | |
| 1090 | |
| 1091 if(isonline) gaim_prpl_got_user_status(sip->account, from, "available", NULL); | |
| 1092 else gaim_prpl_got_user_status(sip->account, from, "offline", NULL); | |
| 1093 | |
| 1094 xmlnode_free(pidf); | |
| 1095 | |
| 1096 g_free(from); | |
| 1097 send_sip_response(sip->gc, msg, 200, "OK", NULL); | |
| 1098 } | |
| 1099 | |
| 1100 static unsigned int simple_typing(GaimConnection *gc, const char *name, GaimTypingState state) { | |
| 1101 struct simple_account_data *sip = gc->proto_data; | |
| 1102 | |
| 1103 gchar *xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | |
| 1104 "<isComposing xmlns=\"urn:ietf:params:xml:ns:im-iscomposing\"\n" | |
| 1105 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" | |
| 1106 "xsi:schemaLocation=\"urn:ietf:params:xml:ns:im-composing iscomposing.xsd\">\n" | |
| 1107 "<state>%s</state>\n" | |
| 1108 "<contenttype>text/plain</contenttype>\n" | |
| 1109 "<refresh>60</refresh>\n" | |
| 1110 "</isComposing>"; | |
| 1111 gchar *recv = g_strdup(name); | |
| 1112 if(state == GAIM_TYPING) { | |
| 1113 gchar *msg = g_strdup_printf(xml, "active"); | |
| 1114 simple_send_message(sip, recv, msg, "application/im-iscomposing+xml"); | |
| 1115 g_free(msg); | |
| 1116 } else /* TODO: Only if (state == GAIM_TYPED) ? */ { | |
| 1117 gchar *msg = g_strdup_printf(xml, "idle"); | |
| 1118 simple_send_message(sip, recv, msg, "application/im-iscomposing+xml"); | |
| 1119 g_free(msg); | |
| 1120 } | |
| 1121 g_free(recv); | |
| 1122 /* | |
| 1123 * TODO: Is this right? It will cause the core to call | |
| 1124 * serv_send_typing(gc, who, GAIM_TYPING) once every second | |
| 1125 * until the user stops typing. If that's not desired, | |
| 1126 * then return 0 instead. | |
| 1127 */ | |
| 1128 return 1; | |
| 1129 } | |
| 1130 | |
| 1131 static gchar *find_tag(const gchar *hdr) { | |
| 1132 const gchar *tmp = strstr(hdr, ";tag="), *tmp2; | |
| 1133 | |
| 1134 if(!tmp) return NULL; | |
| 1135 tmp += 5; | |
| 1136 if((tmp2 = strchr(tmp, ';'))) { | |
| 1137 return g_strndup(tmp, tmp2 - tmp); | |
| 1138 } | |
| 1139 return g_strdup(tmp); | |
| 1140 } | |
| 1141 | |
| 1142 static gchar* gen_xpidf(struct simple_account_data *sip) { | |
| 1143 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | |
| 1144 "<presence>\n" | |
| 1145 "<presentity uri=\"sip:%s@%s;method=SUBSCRIBE\"/>\n" | |
| 1146 "<display name=\"sip:%s@%s\"/>\n" | |
| 1147 "<atom id=\"1234\">\n" | |
| 1148 "<address uri=\"sip:%s@%s\">\n" | |
| 1149 "<status status=\"%s\"/>\n" | |
| 1150 "</address>\n" | |
| 1151 "</atom>\n" | |
| 1152 "</presence>\n", | |
| 1153 sip->username, | |
| 1154 sip->servername, | |
| 1155 sip->username, | |
| 1156 sip->servername, | |
| 1157 sip->username, | |
| 1158 sip->servername, | |
| 1159 sip->status); | |
| 1160 return doc; | |
| 1161 } | |
| 1162 | |
| 1163 | |
| 1164 | |
| 1165 static gchar* gen_pidf(struct simple_account_data *sip) { | |
| 1166 gchar *doc = g_strdup_printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | |
| 1167 "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\"\n" | |
| 1168 "xmlns:im=\"urn:ietf:params:xml:ns:pidf:im\"\n" | |
| 1169 "entity=\"sip:%s@%s\">\n" | |
| 1170 "<tuple id=\"bs35r9f\">\n" | |
| 1171 "<status>\n" | |
| 1172 "<basic>open</basic>\n" | |
| 1173 "</status>\n" | |
| 1174 "<note>%s</note>\n" | |
| 1175 "</tuple>\n" | |
| 1176 "</presence>", | |
| 1177 sip->username, | |
| 1178 sip->servername, | |
| 1179 sip->status); | |
| 1180 return doc; | |
| 1181 } | |
| 1182 | |
| 1183 static void send_notify(struct simple_account_data *sip, struct simple_watcher *watcher) { | |
| 1184 gchar *doc = watcher->needsxpidf ? gen_xpidf(sip) : gen_pidf(sip); | |
| 1185 gchar *hdr = watcher->needsxpidf ? "Event: presence\r\nContent-Type: application/xpidf+xml\r\n" : "Event: presence\r\nContent-Type: application/pidf+xml\r\n"; | |
| 1186 send_sip_request(sip->gc, "NOTIFY", watcher->name, watcher->name, hdr, doc, &watcher->dialog, NULL); | |
| 1187 g_free(doc); | |
| 1188 } | |
| 1189 | |
| 1190 static gboolean process_publish_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc) { | |
| 1191 if(msg->response != 200 && msg->response != 408) { | |
| 1192 /* never send again */ | |
| 1193 sip->republish = -1; | |
| 1194 } | |
| 1195 return TRUE; | |
| 1196 } | |
| 1197 | |
| 1198 static void send_publish(struct simple_account_data *sip) { | |
| 1199 gchar *uri = g_strdup_printf("sip:%s@%s", sip->username, sip->servername); | |
| 1200 gchar *doc = gen_pidf(sip); | |
| 1201 send_sip_request(sip->gc, "PUBLISH", uri, uri, | |
| 1202 "Expires: 600\r\nEvent: presence\r\n" | |
| 1203 "Content-Type: application/pidf+xml\r\n", | |
| 1204 doc, NULL, process_publish_response); | |
| 1205 sip->republish = time(NULL) + 500; | |
| 1206 g_free(uri); | |
| 1207 g_free(doc); | |
| 1208 } | |
| 1209 | |
| 1210 static void process_incoming_subscribe(struct simple_account_data *sip, struct sipmsg *msg) { | |
| 1211 const char *from_hdr = sipmsg_find_header(msg, "From"); | |
| 1212 gchar *from = parse_from(from_hdr); | |
| 1213 gchar *theirtag = find_tag(from_hdr); | |
| 1214 gchar *ourtag = find_tag(sipmsg_find_header(msg, "To")); | |
| 1215 gboolean tagadded = FALSE; | |
| 1216 gchar *callid = sipmsg_find_header(msg, "Call-ID"); | |
| 1217 gchar *expire = sipmsg_find_header(msg, "Expire"); | |
| 1218 gchar *tmp; | |
| 1219 struct simple_watcher *watcher = watcher_find(sip, from); | |
| 1220 if(!ourtag) { | |
| 1221 tagadded = TRUE; | |
| 1222 ourtag = gentag(); | |
| 1223 } | |
| 1224 if(!watcher) { /* new subscription */ | |
| 1225 gchar *acceptheader = sipmsg_find_header(msg, "Accept"); | |
| 1226 gboolean needsxpidf = FALSE; | |
| 1227 if(!gaim_privacy_check(sip->account, from)) { | |
| 1228 send_sip_response(sip->gc, msg, 202, "Ok", NULL); | |
| 1229 goto privend; | |
| 1230 } | |
| 1231 if(acceptheader) { | |
| 1232 gchar *tmp = acceptheader; | |
| 1233 gboolean foundpidf = FALSE; | |
| 1234 gboolean foundxpidf = FALSE; | |
| 1235 while(tmp && tmp < acceptheader + strlen(acceptheader)) { | |
| 1236 gchar *tmp2 = strchr(tmp, ','); | |
| 1237 if(tmp2) *tmp2 = '\0'; | |
| 1238 if(!strcmp("application/pidf+xml", tmp)) | |
| 1239 foundpidf = TRUE; | |
| 1240 if(!strcmp("application/xpidf+xml", tmp)) | |
| 1241 foundxpidf = TRUE; | |
| 1242 if(tmp2) { | |
| 1243 *tmp2 = ','; | |
| 1244 tmp = tmp2; | |
| 1245 while(*tmp == ' ') tmp++; | |
| 1246 } else | |
| 1247 tmp = 0; | |
| 1248 } | |
| 1249 if(!foundpidf && foundxpidf) needsxpidf = TRUE; | |
| 1250 g_free(acceptheader); | |
| 1251 } | |
| 1252 watcher = watcher_create(sip, from, callid, ourtag, theirtag, needsxpidf); | |
| 1253 } | |
| 1254 if(tagadded) { | |
| 1255 gchar *to = g_strdup_printf("%s;tag=%s", sipmsg_find_header(msg, "To"), ourtag); | |
| 1256 sipmsg_remove_header(msg, "To"); | |
| 1257 sipmsg_add_header(msg, "To", to); | |
| 1258 g_free(to); | |
| 1259 } | |
| 1260 if(expire) | |
| 1261 watcher->expire = time(NULL) + strtol(expire, NULL, 10); | |
| 1262 else | |
| 1263 watcher->expire = time(NULL) + 600; | |
| 1264 sipmsg_remove_header(msg, "Contact"); | |
| 1265 tmp = get_contact(sip); | |
| 1266 sipmsg_add_header(msg, "Contact", tmp); | |
| 1267 g_free(tmp); | |
| 1268 gaim_debug_info("simple", "got subscribe: name %s ourtag %s theirtag %s callid %s\n", watcher->name, watcher->dialog.ourtag, watcher->dialog.theirtag, watcher->dialog.callid); | |
| 1269 send_sip_response(sip->gc, msg, 200, "Ok", NULL); | |
| 1270 send_notify(sip, watcher); | |
| 1271 privend: | |
| 1272 g_free(from); | |
| 1273 g_free(theirtag); | |
| 1274 g_free(ourtag); | |
| 1275 g_free(callid); | |
| 1276 g_free(expire); | |
| 1277 } | |
| 1278 | |
| 1279 static void process_input_message(struct simple_account_data *sip, struct sipmsg *msg) { | |
| 1280 gboolean found = FALSE; | |
| 1281 if(msg->response == 0) { /* request */ | |
| 1282 if(!strcmp(msg->method, "MESSAGE")) { | |
| 1283 process_incoming_message(sip, msg); | |
| 1284 found = TRUE; | |
| 1285 } else if(!strcmp(msg->method, "NOTIFY")) { | |
| 1286 process_incoming_notify(sip, msg); | |
| 1287 found = TRUE; | |
| 1288 } else if(!strcmp(msg->method, "SUBSCRIBE")) { | |
| 1289 process_incoming_subscribe(sip, msg); | |
| 1290 found = TRUE; | |
| 1291 } else { | |
| 1292 send_sip_response(sip->gc, msg, 501, "Not implemented", NULL); | |
| 1293 } | |
| 1294 } else { /* response */ | |
| 1295 struct transaction *trans = transactions_find(sip, msg); | |
| 1296 if(trans) { | |
| 1297 if(msg->response == 407) { | |
| 1298 gchar *resend, *auth, *ptmp; | |
| 1299 | |
| 1300 if(sip->proxy.retries > 3) return; | |
| 1301 sip->proxy.retries++; | |
| 1302 /* do proxy authentication */ | |
| 1303 | |
| 1304 ptmp = sipmsg_find_header(msg, "Proxy-Authenticate"); | |
| 1305 | |
| 1306 fill_auth(sip, ptmp, &sip->proxy); | |
| 1307 auth = auth_header(sip, &sip->proxy, trans->msg->method, trans->msg->target); | |
| 1308 sipmsg_remove_header(trans->msg, "Proxy-Authorization"); | |
| 1309 sipmsg_add_header(trans->msg, "Proxy-Authorization", auth); | |
| 1310 g_free(auth); | |
| 1311 resend = sipmsg_to_string(trans->msg); | |
| 1312 /* resend request */ | |
| 1313 sendout_pkt(sip->gc, resend); | |
| 1314 g_free(resend); | |
| 1315 } else { | |
| 1316 if(msg->response == 100) { | |
| 1317 /* ignore provisional response */ | |
| 1318 gaim_debug_info("simple", "got trying response\n"); | |
| 1319 } else { | |
| 1320 sip->proxy.retries = 0; | |
| 1321 if(!strcmp(trans->msg->method, "REGISTER")) { | |
| 1322 if(msg->response == 401) sip->registrar.retries++; | |
| 1323 else sip->registrar.retries = 0; | |
| 1324 } else { | |
| 1325 if(msg->response == 401) { | |
| 1326 gchar *resend, *auth, *ptmp; | |
| 1327 | |
| 1328 if(sip->registrar.retries > 4) return; | |
| 1329 sip->registrar.retries++; | |
| 1330 | |
| 1331 ptmp = sipmsg_find_header(msg, "WWW-Authenticate"); | |
| 1332 | |
| 1333 fill_auth(sip, ptmp, &sip->registrar); | |
| 1334 auth = auth_header(sip, &sip->registrar, trans->msg->method, trans->msg->target); | |
| 1335 sipmsg_remove_header(trans->msg, "Authorization"); | |
| 1336 sipmsg_add_header(trans->msg, "Authorization", auth); | |
| 1337 g_free(auth); | |
| 1338 resend = sipmsg_to_string(trans->msg); | |
| 1339 /* resend request */ | |
| 1340 sendout_pkt(sip->gc, resend); | |
| 1341 g_free(resend); | |
| 1342 } | |
| 1343 } | |
| 1344 if(trans->callback) { | |
| 1345 /* call the callback to process response*/ | |
| 1346 (trans->callback)(sip, msg, trans); | |
| 1347 } | |
| 1348 transactions_remove(sip, trans); | |
| 1349 } | |
| 1350 } | |
| 1351 found = TRUE; | |
| 1352 } else { | |
| 1353 gaim_debug(GAIM_DEBUG_MISC, "simple", "received response to unknown transaction"); | |
| 1354 } | |
| 1355 } | |
| 1356 if(!found) { | |
| 1357 gaim_debug(GAIM_DEBUG_MISC, "simple", "received a unknown sip message with method %s and response %d\n", msg->method, msg->response); | |
| 1358 } | |
| 1359 } | |
| 1360 | |
| 1361 static void process_input(struct simple_account_data *sip, struct sip_connection *conn) | |
| 1362 { | |
| 1363 char *cur; | |
| 1364 char *dummy; | |
| 1365 struct sipmsg *msg; | |
| 1366 int restlen; | |
| 1367 cur = conn->inbuf; | |
| 1368 | |
| 1369 /* according to the RFC remove CRLF at the beginning */ | |
| 1370 while(*cur == '\r' || *cur == '\n') { | |
| 1371 cur++; | |
| 1372 } | |
| 1373 if(cur != conn->inbuf) { | |
| 1374 memmove(conn->inbuf, cur, conn->inbufused - (cur - conn->inbuf)); | |
| 1375 conn->inbufused = strlen(conn->inbuf); | |
| 1376 } | |
| 1377 | |
| 1378 /* Received a full Header? */ | |
| 1379 if((cur = strstr(conn->inbuf, "\r\n\r\n")) != NULL) { | |
| 1380 time_t currtime = time(NULL); | |
| 1381 cur += 2; | |
| 1382 cur[0] = '\0'; | |
| 1383 gaim_debug_info("simple", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), conn->inbuf); | |
| 1384 msg = sipmsg_parse_header(conn->inbuf); | |
| 1385 cur[0] = '\r'; | |
| 1386 cur += 2; | |
| 1387 restlen = conn->inbufused - (cur - conn->inbuf); | |
| 1388 if(restlen >= msg->bodylen) { | |
| 1389 dummy = g_malloc(msg->bodylen + 1); | |
| 1390 memcpy(dummy, cur, msg->bodylen); | |
| 1391 dummy[msg->bodylen] = '\0'; | |
| 1392 msg->body = dummy; | |
| 1393 cur += msg->bodylen; | |
| 1394 memmove(conn->inbuf, cur, conn->inbuflen - (cur - conn->inbuf)); | |
| 1395 conn->inbufused = strlen(conn->inbuf); | |
| 1396 } else { | |
| 1397 sipmsg_free(msg); | |
| 1398 return; | |
| 1399 } | |
| 1400 gaim_debug(GAIM_DEBUG_MISC, "simple", "in process response response: %d\n", msg->response); | |
| 1401 process_input_message(sip, msg); | |
| 1402 } else { | |
| 1403 gaim_debug(GAIM_DEBUG_MISC, "simple", "received a incomplete sip msg: %s\n", conn->inbuf); | |
| 1404 } | |
| 1405 } | |
| 1406 | |
| 1407 static void simple_udp_process(gpointer data, gint source, GaimInputCondition con) { | |
| 1408 GaimConnection *gc = data; | |
| 1409 struct simple_account_data *sip = gc->proto_data; | |
| 1410 struct sipmsg *msg; | |
| 1411 int len; | |
| 1412 time_t currtime; | |
| 1413 | |
| 1414 static char buffer[65536]; | |
| 1415 if((len = recv(source, buffer, sizeof(buffer) - 1, 0)) > 0) { | |
| 1416 buffer[len] = '\0'; | |
| 1417 gaim_debug_info("simple", "\n\nreceived - %s\n######\n%s\n#######\n\n", ctime(&currtime), buffer); | |
| 1418 msg = sipmsg_parse_msg(buffer); | |
| 1419 if(msg) process_input_message(sip, msg); | |
| 1420 } | |
| 1421 } | |
| 1422 | |
| 1423 static void simple_input_cb(gpointer data, gint source, GaimInputCondition cond) | |
| 1424 { | |
| 1425 GaimConnection *gc = data; | |
| 1426 struct simple_account_data *sip = gc->proto_data; | |
| 1427 int len; | |
| 1428 struct sip_connection *conn = connection_find(sip, source); | |
| 1429 if(!conn) { | |
| 1430 gaim_debug_error("simple", "Connection not found!\n"); | |
| 1431 return; | |
| 1432 } | |
| 1433 | |
| 1434 if(conn->inbuflen < conn->inbufused + SIMPLE_BUF_INC) { | |
| 1435 conn->inbuflen += SIMPLE_BUF_INC; | |
| 1436 conn->inbuf = g_realloc(conn->inbuf, conn->inbuflen); | |
| 1437 } | |
| 1438 | |
| 1439 len = read(source, conn->inbuf + conn->inbufused, SIMPLE_BUF_INC - 1); | |
| 1440 | |
| 1441 if(len < 0 && errno == EAGAIN) | |
| 1442 return; | |
| 1443 else if(len <= 0) { | |
| 1444 gaim_debug_info("simple", "simple_input_cb: read error\n"); | |
| 1445 connection_remove(sip, source); | |
| 1446 if(sip->fd == source) sip->fd = -1; | |
| 1447 return; | |
| 1448 } | |
| 1449 | |
| 1450 conn->inbufused += len; | |
| 1451 conn->inbuf[conn->inbufused] = '\0'; | |
| 1452 | |
| 1453 process_input(sip, conn); | |
| 1454 } | |
| 1455 | |
| 1456 /* Callback for new connections on incoming TCP port */ | |
| 1457 static void simple_newconn_cb(gpointer data, gint source, GaimInputCondition cond) { | |
| 1458 GaimConnection *gc = data; | |
| 1459 struct simple_account_data *sip = gc->proto_data; | |
| 1460 struct sip_connection *conn; | |
| 1461 | |
| 1462 int newfd = accept(source, NULL, NULL); | |
| 1463 | |
| 1464 conn = connection_create(sip, newfd); | |
| 1465 | |
| 1466 conn->inputhandler = gaim_input_add(newfd, GAIM_INPUT_READ, simple_input_cb, gc); | |
| 1467 } | |
| 1468 | |
| 1469 static void login_cb(gpointer data, gint source, const gchar *error_message) { | |
| 1470 GaimConnection *gc = data; | |
| 1471 struct simple_account_data *sip; | |
| 1472 struct sip_connection *conn; | |
| 1473 | |
| 1474 if (!GAIM_CONNECTION_IS_VALID(gc)) | |
| 1475 { | |
| 1476 if (source >= 0) | |
| 1477 close(source); | |
| 1478 return; | |
| 1479 } | |
| 1480 | |
| 1481 if(source < 0) { | |
| 1482 gaim_connection_error(gc, _("Could not connect")); | |
| 1483 return; | |
| 1484 } | |
| 1485 | |
| 1486 sip = gc->proto_data; | |
| 1487 sip->fd = source; | |
| 1488 | |
| 1489 conn = connection_create(sip, source); | |
| 1490 | |
| 1491 sip->registertimeout = gaim_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip); | |
| 1492 | |
| 1493 do_register(sip); | |
| 1494 | |
| 1495 conn->inputhandler = gaim_input_add(sip->fd, GAIM_INPUT_READ, simple_input_cb, gc); | |
| 1496 } | |
| 1497 | |
| 1498 static guint simple_ht_hash_nick(const char *nick) { | |
| 1499 char *lc = g_utf8_strdown(nick, -1); | |
| 1500 guint bucket = g_str_hash(lc); | |
| 1501 g_free(lc); | |
| 1502 | |
| 1503 return bucket; | |
| 1504 } | |
| 1505 | |
| 1506 static gboolean simple_ht_equals_nick(const char *nick1, const char *nick2) { | |
| 1507 return (gaim_utf8_strcasecmp(nick1, nick2) == 0); | |
| 1508 } | |
| 1509 | |
| 1510 static void simple_udp_host_resolved_listen_cb(int listenfd, gpointer data) { | |
| 1511 struct simple_account_data *sip = (struct simple_account_data*) data; | |
| 1512 | |
| 14267 | 1513 sip->listen_data = NULL; |
| 1514 | |
| 14192 | 1515 if(listenfd == -1) { |
| 1516 gaim_connection_error(sip->gc, _("Could not create listen socket")); | |
| 1517 return; | |
| 1518 } | |
| 1519 | |
| 1520 sip->fd = listenfd; | |
| 1521 | |
| 1522 sip->listenport = gaim_network_get_port_from_fd(sip->fd); | |
| 1523 sip->listenfd = sip->fd; | |
| 1524 | |
| 1525 sip->listenpa = gaim_input_add(sip->fd, GAIM_INPUT_READ, simple_udp_process, sip->gc); | |
| 1526 | |
| 1527 sip->resendtimeout = gaim_timeout_add(2500, (GSourceFunc) resend_timeout, sip); | |
| 1528 sip->registertimeout = gaim_timeout_add((rand()%100)+10*1000, (GSourceFunc)subscribe_timeout, sip); | |
| 1529 do_register(sip); | |
| 1530 } | |
| 1531 | |
| 1532 static void simple_udp_host_resolved(GSList *hosts, gpointer data, const char *error_message) { | |
| 1533 struct simple_account_data *sip = (struct simple_account_data*) data; | |
| 1534 int addr_size; | |
| 1535 | |
| 14238 | 1536 sip->query_data = NULL; |
| 1537 | |
| 14192 | 1538 if (!hosts || !hosts->data) { |
| 1539 gaim_connection_error(sip->gc, _("Couldn't resolve host")); | |
| 1540 return; | |
| 1541 } | |
| 1542 | |
| 1543 addr_size = GPOINTER_TO_INT(hosts->data); | |
| 1544 hosts = g_slist_remove(hosts, hosts->data); | |
| 1545 memcpy(&(sip->serveraddr), hosts->data, addr_size); | |
| 1546 g_free(hosts->data); | |
| 1547 hosts = g_slist_remove(hosts, hosts->data); | |
| 1548 while(hosts) { | |
| 1549 hosts = g_slist_remove(hosts, hosts->data); | |
| 1550 g_free(hosts->data); | |
| 1551 hosts = g_slist_remove(hosts, hosts->data); | |
| 1552 } | |
| 1553 | |
| 1554 /* create socket for incoming connections */ | |
| 14267 | 1555 sip->listen_data = gaim_network_listen_range(5060, 5160, SOCK_DGRAM, |
| 1556 simple_udp_host_resolved_listen_cb, sip); | |
| 1557 if (sip->listen_data == NULL) { | |
| 14192 | 1558 gaim_connection_error(sip->gc, _("Could not create listen socket")); |
| 1559 return; | |
| 1560 } | |
| 1561 } | |
| 1562 | |
| 1563 static void | |
| 1564 simple_tcp_connect_listen_cb(int listenfd, gpointer data) { | |
| 1565 struct simple_account_data *sip = (struct simple_account_data*) data; | |
| 1566 | |
| 14267 | 1567 sip->listen_data = NULL; |
| 1568 | |
| 14192 | 1569 sip->listenfd = listenfd; |
| 1570 if(sip->listenfd == -1) { | |
| 1571 gaim_connection_error(sip->gc, _("Could not create listen socket")); | |
| 1572 return; | |
| 1573 } | |
| 1574 | |
| 1575 gaim_debug_info("simple", "listenfd: %d\n", sip->listenfd); | |
| 1576 sip->listenport = gaim_network_get_port_from_fd(sip->listenfd); | |
| 1577 sip->listenpa = gaim_input_add(sip->listenfd, GAIM_INPUT_READ, | |
| 1578 simple_newconn_cb, sip->gc); | |
| 1579 gaim_debug_info("simple", "connecting to %s port %d\n", | |
| 1580 sip->realhostname, sip->realport); | |
| 1581 /* open tcp connection to the server */ | |
| 14837 | 1582 if (gaim_proxy_connect(sip->gc, sip->account, sip->realhostname, |
| 1583 sip->realport, login_cb, sip->gc) == NULL) { | |
| 14192 | 1584 gaim_connection_error(sip->gc, _("Couldn't create socket")); |
| 1585 } | |
| 1586 } | |
| 1587 | |
| 1588 static void srvresolved(GaimSrvResponse *resp, int results, gpointer data) { | |
| 1589 struct simple_account_data *sip; | |
| 1590 gchar *hostname; | |
| 1591 int port; | |
| 1592 | |
| 14308 | 1593 sip = data; |
| 1594 sip->srv_query_data = NULL; | |
| 14192 | 1595 |
| 1596 port = gaim_account_get_int(sip->account, "port", 0); | |
| 1597 | |
| 1598 /* find the host to connect to */ | |
| 1599 if(results) { | |
| 1600 hostname = g_strdup(resp->hostname); | |
| 1601 if(!port) | |
| 1602 port = resp->port; | |
| 1603 g_free(resp); | |
| 1604 } else { | |
| 1605 if(!gaim_account_get_bool(sip->account, "useproxy", FALSE)) { | |
| 1606 hostname = g_strdup(sip->servername); | |
| 1607 } else { | |
| 1608 hostname = g_strdup(gaim_account_get_string(sip->account, "proxy", sip->servername)); | |
| 1609 } | |
| 1610 } | |
| 1611 | |
| 1612 sip->realhostname = hostname; | |
| 1613 sip->realport = port; | |
| 1614 if(!sip->realport) sip->realport = 5060; | |
| 1615 | |
| 1616 /* TCP case */ | |
| 1617 if(!sip->udp) { | |
| 1618 /* create socket for incoming connections */ | |
| 14267 | 1619 sip->listen_data = gaim_network_listen_range(5060, 5160, SOCK_STREAM, |
| 1620 simple_tcp_connect_listen_cb, sip); | |
| 1621 if (sip->listen_data == NULL) { | |
| 14192 | 1622 gaim_connection_error(sip->gc, _("Could not create listen socket")); |
| 1623 return; | |
| 1624 } | |
| 1625 } else { /* UDP */ | |
| 1626 gaim_debug_info("simple", "using udp with server %s and port %d\n", hostname, port); | |
| 1627 | |
| 14238 | 1628 sip->query_data = gaim_dnsquery_a(hostname, port, simple_udp_host_resolved, sip); |
| 1629 if (sip->query_data == NULL) { | |
| 1630 gaim_connection_error(sip->gc, _("Could not resolve hostname")); | |
| 1631 } | |
| 14192 | 1632 } |
| 1633 } | |
| 1634 | |
| 1635 static void simple_login(GaimAccount *account) | |
| 1636 { | |
| 1637 GaimConnection *gc; | |
| 1638 struct simple_account_data *sip; | |
| 1639 gchar **userserver; | |
| 1640 gchar *hosttoconnect; | |
| 1641 | |
| 1642 const char *username = gaim_account_get_username(account); | |
| 1643 gc = gaim_account_get_connection(account); | |
| 1644 | |
| 1645 if (strpbrk(username, " \t\v\r\n") != NULL) { | |
| 1646 gc->wants_to_die = TRUE; | |
|
15000
89ae31668a9c
[gaim-migrate @ 17779]
Richard Laager <rlaager@wiktel.com>
parents:
14920
diff
changeset
|
1647 gaim_connection_error(gc, _("SIP screen names may not contain whitespaces or @ symbols")); |
| 14192 | 1648 return; |
| 1649 } | |
| 1650 | |
| 1651 gc->proto_data = sip = g_new0(struct simple_account_data, 1); | |
| 1652 sip->gc = gc; | |
| 1653 sip->account = account; | |
| 1654 sip->registerexpire = 900; | |
| 1655 sip->udp = gaim_account_get_bool(account, "udp", FALSE); | |
| 1656 /* TODO: is there a good default grow size? */ | |
| 1657 if(!sip->udp) | |
| 1658 sip->txbuf = gaim_circ_buffer_new(0); | |
| 1659 | |
| 1660 userserver = g_strsplit(username, "@", 2); | |
| 1661 gaim_connection_set_display_name(gc, userserver[0]); | |
| 1662 sip->username = g_strdup(userserver[0]); | |
| 1663 sip->servername = g_strdup(userserver[1]); | |
| 1664 sip->password = g_strdup(gaim_connection_get_password(gc)); | |
| 1665 g_strfreev(userserver); | |
| 1666 | |
| 1667 sip->buddies = g_hash_table_new((GHashFunc)simple_ht_hash_nick, (GEqualFunc)simple_ht_equals_nick); | |
| 1668 | |
| 1669 gaim_connection_update_progress(gc, _("Connecting"), 1, 2); | |
| 1670 | |
| 1671 /* TODO: Set the status correctly. */ | |
| 1672 sip->status = g_strdup("available"); | |
| 1673 | |
| 1674 if(!gaim_account_get_bool(account, "useproxy", FALSE)) { | |
| 1675 hosttoconnect = g_strdup(sip->servername); | |
| 1676 } else { | |
| 1677 hosttoconnect = g_strdup(gaim_account_get_string(account, "proxy", sip->servername)); | |
| 1678 } | |
| 1679 | |
| 14308 | 1680 sip->srv_query_data = gaim_srv_resolve("sip", |
| 1681 sip->udp ? "udp" : "tcp", hosttoconnect, srvresolved, sip); | |
| 14192 | 1682 g_free(hosttoconnect); |
| 1683 } | |
| 1684 | |
| 1685 static void simple_close(GaimConnection *gc) | |
| 1686 { | |
| 1687 struct simple_account_data *sip = gc->proto_data; | |
| 1688 | |
| 1689 if(sip) { | |
| 1690 /* unregister */ | |
| 1691 do_register_exp(sip, 0); | |
| 1692 connection_free_all(sip); | |
| 1693 | |
| 14238 | 1694 if (sip->query_data != NULL) |
| 1695 gaim_dnsquery_destroy(sip->query_data); | |
| 1696 | |
| 14308 | 1697 if (sip->srv_query_data != NULL) |
| 1698 gaim_srv_cancel(sip->srv_query_data); | |
| 1699 | |
| 14267 | 1700 if (sip->listen_data != NULL) |
| 1701 gaim_network_listen_cancel(sip->listen_data); | |
| 1702 | |
| 14192 | 1703 g_free(sip->servername); |
| 1704 g_free(sip->username); | |
| 1705 g_free(sip->password); | |
| 1706 g_free(sip->registrar.nonce); | |
| 1707 g_free(sip->registrar.opaque); | |
| 1708 g_free(sip->registrar.target); | |
| 1709 g_free(sip->registrar.realm); | |
| 1710 g_free(sip->registrar.digest_session_key); | |
| 1711 g_free(sip->proxy.nonce); | |
| 1712 g_free(sip->proxy.opaque); | |
| 1713 g_free(sip->proxy.target); | |
| 1714 g_free(sip->proxy.realm); | |
| 1715 g_free(sip->proxy.digest_session_key); | |
| 1716 if(sip->txbuf) | |
| 1717 gaim_circ_buffer_destroy(sip->txbuf); | |
| 1718 g_free(sip->realhostname); | |
| 1719 if(sip->listenpa) gaim_input_remove(sip->listenpa); | |
| 1720 if(sip->tx_handler) gaim_input_remove(sip->tx_handler); | |
| 1721 if(sip->resendtimeout) gaim_timeout_remove(sip->resendtimeout); | |
| 1722 if(sip->registertimeout) gaim_timeout_remove(sip->registertimeout); | |
| 1723 } | |
| 1724 g_free(gc->proto_data); | |
| 1725 gc->proto_data = NULL; | |
| 1726 } | |
| 1727 | |
| 1728 /* not needed since privacy is checked for every subscribe */ | |
| 1729 static void dummy_add_deny(GaimConnection *gc, const char *name) { | |
| 1730 } | |
| 1731 | |
| 1732 static void dummy_permit_deny(GaimConnection *gc) { | |
| 1733 } | |
| 1734 | |
| 1735 static GaimPluginProtocolInfo prpl_info = | |
| 1736 { | |
| 1737 0, | |
| 1738 NULL, /* user_splits */ | |
| 1739 NULL, /* protocol_options */ | |
| 1740 NO_BUDDY_ICONS, /* icon_spec */ | |
| 1741 simple_list_icon, /* list_icon */ | |
| 1742 NULL, /* list_emblems */ | |
| 1743 NULL, /* status_text */ | |
| 1744 NULL, /* tooltip_text */ | |
| 1745 simple_status_types, /* away_states */ | |
| 1746 NULL, /* blist_node_menu */ | |
| 1747 NULL, /* chat_info */ | |
| 1748 NULL, /* chat_info_defaults */ | |
| 1749 simple_login, /* login */ | |
| 1750 simple_close, /* close */ | |
| 1751 simple_im_send, /* send_im */ | |
| 1752 NULL, /* set_info */ | |
| 1753 simple_typing, /* send_typing */ | |
| 1754 NULL, /* get_info */ | |
| 1755 simple_set_status, /* set_status */ | |
| 1756 NULL, /* set_idle */ | |
| 1757 NULL, /* change_passwd */ | |
| 1758 simple_add_buddy, /* add_buddy */ | |
| 1759 NULL, /* add_buddies */ | |
| 1760 simple_remove_buddy, /* remove_buddy */ | |
| 1761 NULL, /* remove_buddies */ | |
| 1762 dummy_add_deny, /* add_permit */ | |
| 1763 dummy_add_deny, /* add_deny */ | |
| 1764 dummy_add_deny, /* rem_permit */ | |
| 1765 dummy_add_deny, /* rem_deny */ | |
| 1766 dummy_permit_deny, /* set_permit_deny */ | |
| 1767 NULL, /* join_chat */ | |
| 1768 NULL, /* reject_chat */ | |
| 1769 NULL, /* get_chat_name */ | |
| 1770 NULL, /* chat_invite */ | |
| 1771 NULL, /* chat_leave */ | |
| 1772 NULL, /* chat_whisper */ | |
| 1773 NULL, /* chat_send */ | |
| 1774 simple_keep_alive, /* keepalive */ | |
| 1775 NULL, /* register_user */ | |
| 1776 NULL, /* get_cb_info */ | |
| 1777 NULL, /* get_cb_away */ | |
| 1778 NULL, /* alias_buddy */ | |
| 1779 NULL, /* group_buddy */ | |
| 1780 NULL, /* rename_group */ | |
| 1781 NULL, /* buddy_free */ | |
| 1782 NULL, /* convo_closed */ | |
| 1783 NULL, /* normalize */ | |
| 1784 NULL, /* set_buddy_icon */ | |
| 1785 NULL, /* remove_group */ | |
| 1786 NULL, /* get_cb_real_name */ | |
| 1787 NULL, /* set_chat_topic */ | |
| 1788 NULL, /* find_blist_chat */ | |
| 1789 NULL, /* roomlist_get_list */ | |
| 1790 NULL, /* roomlist_cancel */ | |
| 1791 NULL, /* roomlist_expand_category */ | |
| 1792 NULL, /* can_receive_file */ | |
| 1793 NULL, /* send_file */ | |
| 1794 NULL, /* new_xfer */ | |
| 1795 NULL, /* offline_message */ | |
| 1796 NULL, /* whiteboard_prpl_ops */ | |
| 14542 | 1797 simple_send_raw, /* send_raw */ |
| 14192 | 1798 }; |
| 1799 | |
| 1800 | |
| 1801 static GaimPluginInfo info = | |
| 1802 { | |
| 1803 GAIM_PLUGIN_MAGIC, | |
| 1804 GAIM_MAJOR_VERSION, | |
| 1805 GAIM_MINOR_VERSION, | |
| 1806 GAIM_PLUGIN_PROTOCOL, /**< type */ | |
| 1807 NULL, /**< ui_requirement */ | |
| 1808 0, /**< flags */ | |
| 1809 NULL, /**< dependencies */ | |
| 1810 GAIM_PRIORITY_DEFAULT, /**< priority */ | |
| 1811 | |
| 1812 "prpl-simple", /**< id */ | |
| 1813 "SIMPLE", /**< name */ | |
| 1814 VERSION, /**< version */ | |
| 1815 N_("SIP/SIMPLE Protocol Plugin"), /** summary */ | |
| 1816 N_("The SIP/SIMPLE Protocol Plugin"), /** description */ | |
| 1817 "Thomas Butter <butter@uni-mannheim.de>", /**< author */ | |
| 1818 GAIM_WEBSITE, /**< homepage */ | |
| 1819 | |
| 1820 NULL, /**< load */ | |
| 1821 NULL, /**< unload */ | |
| 1822 NULL, /**< destroy */ | |
| 1823 | |
| 1824 NULL, /**< ui_info */ | |
| 1825 &prpl_info, /**< extra_info */ | |
| 1826 NULL, | |
| 1827 NULL | |
| 1828 }; | |
| 1829 | |
| 1830 static void _init_plugin(GaimPlugin *plugin) | |
| 1831 { | |
| 1832 GaimAccountUserSplit *split; | |
| 1833 GaimAccountOption *option; | |
| 1834 | |
| 1835 split = gaim_account_user_split_new(_("Server"), "", '@'); | |
| 1836 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); | |
| 1837 | |
| 1838 option = gaim_account_option_bool_new(_("Publish status (note: everyone may watch you)"), "dopublish", TRUE); | |
| 1839 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
| 1840 | |
| 1841 option = gaim_account_option_int_new(_("Connect port"), "port", 0); | |
| 1842 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
| 1843 | |
| 1844 option = gaim_account_option_bool_new(_("Use UDP"), "udp", FALSE); | |
| 1845 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
| 1846 option = gaim_account_option_bool_new(_("Use proxy"), "useproxy", FALSE); | |
| 1847 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
| 1848 option = gaim_account_option_string_new(_("Proxy"), "proxy", ""); | |
| 1849 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
| 1850 option = gaim_account_option_string_new(_("Auth User"), "authuser", ""); | |
| 1851 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
| 1852 option = gaim_account_option_string_new(_("Auth Domain"), "authdomain", ""); | |
| 1853 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
| 1854 } | |
| 1855 | |
| 1856 GAIM_INIT_PLUGIN(simple, _init_plugin, info); |
