1 /*
2  * libokcupid
3  *
4  * libokcupid is the property of its developers.  See the COPYRIGHT file
5  * for more details.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "okc_connection.h"
22 
23 static void okc_attempt_connection(OkCupidConnection *);
24 static void okc_next_connection(OkCupidAccount *oca);
25 
okc_gunzip(const guchar * gzip_data,ssize_t * len_ptr)26 static gchar *okc_gunzip(const guchar *gzip_data, ssize_t *len_ptr)
27 {
28 	gsize gzip_data_len	= *len_ptr;
29 	z_stream zstr;
30 	int gzip_err = 0;
31 	gchar *data_buffer;
32 	gulong gzip_len = G_MAXUINT16;
33 	GString *output_string = NULL;
34 
35 	data_buffer = g_new0(gchar, gzip_len);
36 
37 	zstr.next_in = NULL;
38 	zstr.avail_in = 0;
39 	zstr.zalloc = Z_NULL;
40 	zstr.zfree = Z_NULL;
41 	zstr.opaque = 0;
42 	gzip_err = inflateInit2(&zstr, MAX_WBITS+32);
43 	if (gzip_err != Z_OK)
44 	{
45 		g_free(data_buffer);
46 		purple_debug_error("okcupid", "no built-in gzip support in zlib\n");
47 		return NULL;
48 	}
49 
50 	zstr.next_in = (Bytef *)gzip_data;
51 	zstr.avail_in = gzip_data_len;
52 
53 	zstr.next_out = (Bytef *)data_buffer;
54 	zstr.avail_out = gzip_len;
55 
56 	gzip_err = inflate(&zstr, Z_SYNC_FLUSH);
57 
58 	if (gzip_err == Z_DATA_ERROR)
59 	{
60 		inflateEnd(&zstr);
61 		inflateInit2(&zstr, -MAX_WBITS);
62 		if (gzip_err != Z_OK)
63 		{
64 			g_free(data_buffer);
65 			purple_debug_error("okcupid", "Cannot decode gzip header\n");
66 			return NULL;
67 		}
68 		zstr.next_in = (Bytef *)gzip_data;
69 		zstr.avail_in = gzip_data_len;
70 		zstr.next_out = (Bytef *)data_buffer;
71 		zstr.avail_out = gzip_len;
72 		gzip_err = inflate(&zstr, Z_SYNC_FLUSH);
73 	}
74 	output_string = g_string_new("");
75 	while (gzip_err == Z_OK)
76 	{
77 		//append data to buffer
78 		output_string = g_string_append_len(output_string, data_buffer, gzip_len - zstr.avail_out);
79 		//reset buffer pointer
80 		zstr.next_out = (Bytef *)data_buffer;
81 		zstr.avail_out = gzip_len;
82 		gzip_err = inflate(&zstr, Z_SYNC_FLUSH);
83 	}
84 	if (gzip_err == Z_STREAM_END)
85 	{
86 		output_string = g_string_append_len(output_string, data_buffer, gzip_len - zstr.avail_out);
87 	} else {
88 		purple_debug_error("okcupid", "gzip inflate error\n");
89 	}
90 	inflateEnd(&zstr);
91 
92 	g_free(data_buffer);
93 
94 	gchar *output_data = g_strdup(output_string->str);
95 	*len_ptr = output_string->len;
96 
97 	g_string_free(output_string, TRUE);
98 
99 	return output_data;
100 }
101 
okc_connection_destroy(OkCupidConnection * okconn)102 void okc_connection_destroy(OkCupidConnection *okconn)
103 {
104 	okconn->oca->conns = g_slist_remove(okconn->oca->conns, okconn);
105 
106 	if (okconn->request != NULL)
107 		g_string_free(okconn->request, TRUE);
108 
109 	g_free(okconn->rx_buf);
110 
111 	if (okconn->connect_data != NULL)
112 		purple_proxy_connect_cancel(okconn->connect_data);
113 
114 	if (okconn->ssl_conn != NULL)
115 		purple_ssl_close(okconn->ssl_conn);
116 
117 	if (okconn->fd >= 0) {
118 		close(okconn->fd);
119 	}
120 
121 	if (okconn->input_watcher > 0)
122 		purple_input_remove(okconn->input_watcher);
123 
124 	g_free(okconn->hostname);
125 	g_free(okconn);
126 }
127 
okc_update_cookies(OkCupidAccount * oca,const gchar * headers)128 static void okc_update_cookies(OkCupidAccount *oca, const gchar *headers)
129 {
130 	const gchar *cookie_start;
131 	const gchar *cookie_end;
132 	gchar *cookie_name;
133 	gchar *cookie_value;
134 	int header_len;
135 
136 	g_return_if_fail(headers != NULL);
137 
138 	header_len = strlen(headers);
139 
140 	/* look for the next "Set-Cookie: " */
141 	/* grab the data up until ';' */
142 	cookie_start = headers;
143 	while ((cookie_start = strstr(cookie_start, "\r\nSet-Cookie: ")) &&
144 			(headers-cookie_start) < header_len)
145 	{
146 		cookie_start += 14;
147 		cookie_end = strchr(cookie_start, '=');
148 		cookie_name = g_strndup(cookie_start, cookie_end-cookie_start);
149 		cookie_start = cookie_end + 1;
150 		cookie_end = strchr(cookie_start, ';');
151 		cookie_value= g_strndup(cookie_start, cookie_end-cookie_start);
152 		cookie_start = cookie_end;
153 
154 		purple_debug_info("okcupid", "got cookie %s=%s\n",
155 				cookie_name, cookie_value);
156 
157 		g_hash_table_replace(oca->cookie_table, cookie_name,
158 				cookie_value);
159 	}
160 }
161 
okc_connection_process_data(OkCupidConnection * okconn)162 static void okc_connection_process_data(OkCupidConnection *okconn)
163 {
164 	ssize_t len;
165 	gchar *tmp;
166 
167 	len = okconn->rx_len;
168 	tmp = g_strstr_len(okconn->rx_buf, len, "\r\n\r\n");
169 	if (tmp == NULL) {
170 		/* This is a corner case that occurs when the connection is
171 		 * prematurely closed either on the client or the server.
172 		 * This can either be no data at all or a partial set of
173 		 * headers.  We pass along the data to be good, but don't
174 		 * do any fancy massaging.  In all likelihood the result will
175 		 * be tossed by the connection callback func anyways
176 		 */
177 		tmp = g_strndup(okconn->rx_buf, len);
178 	} else {
179 		tmp += 4;
180 		len -= g_strstr_len(okconn->rx_buf, len, "\r\n\r\n") -
181 				okconn->rx_buf + 4;
182 		tmp = g_memdup(tmp, len + 1);
183 		tmp[len] = '\0';
184 		okconn->rx_buf[okconn->rx_len - len] = '\0';
185 		//purple_debug_misc("okcupid", "response headers\n%s\n",
186 		//		okconn->rx_buf);
187 		okc_update_cookies(okconn->oca, okconn->rx_buf);
188 
189 		if (strstr(okconn->rx_buf, "Content-Encoding: gzip"))
190 		{
191 			/* we've received compressed gzip data, decompress */
192 			gchar *gunzipped;
193 			gunzipped = okc_gunzip((const guchar *)tmp, &len);
194 			g_free(tmp);
195 			tmp = gunzipped;
196 		}
197 	}
198 
199 	g_free(okconn->rx_buf);
200 	okconn->rx_buf = NULL;
201 
202 	if (okconn->callback != NULL)
203 		okconn->callback(okconn->oca, tmp, len, okconn->user_data);
204 
205 	g_free(tmp);
206 }
207 
okc_fatal_connection_cb(OkCupidConnection * okconn)208 static void okc_fatal_connection_cb(OkCupidConnection *okconn)
209 {
210 	PurpleConnection *pc = okconn->oca->pc;
211 
212 	purple_debug_error("okcupid", "fatal connection error\n");
213 
214 	okc_connection_destroy(okconn);
215 
216 	/* We died.  Do not pass Go.  Do not collect $200 */
217 	/* In all seriousness, don't attempt to call the normal callback here.
218 	 * That may lead to the wrong error message being displayed */
219 	purple_connection_error_reason(pc,
220 				PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
221 				_("Server closed the connection."));
222 
223 }
224 
okc_post_or_get_readdata_cb(gpointer data,gint source,PurpleInputCondition cond)225 static void okc_post_or_get_readdata_cb(gpointer data, gint source,
226 		PurpleInputCondition cond)
227 {
228 	OkCupidAccount *oca;
229 	OkCupidConnection *okconn;
230 	gchar buf[4096];
231 	ssize_t len;
232 
233 	okconn = data;
234 	oca = okconn->oca;
235 
236 	if (okconn->method & OKC_METHOD_SSL) {
237 		len = purple_ssl_read(okconn->ssl_conn,
238 				buf, sizeof(buf) - 1);
239 	} else {
240 		len = recv(okconn->fd, buf, sizeof(buf) - 1, 0);
241 	}
242 
243 	if (len < 0)
244 	{
245 		if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
246 			/* Try again later */
247 			return;
248 		}
249 
250 		if (okconn->method & OKC_METHOD_SSL && okconn->rx_len > 0) {
251 			/*
252 			 * This is a slightly hacky workaround for a bug in either
253 			 * GNU TLS or in the SSL implementation on Facebook's web
254 			 * servers.  The sequence of events is:
255 			 * 1. We attempt to read the first time and successfully read
256 			 *    the server's response.
257 			 * 2. We attempt to read a second time and libpurple's call
258 			 *    to gnutls_record_recv() returns the error
259 			 *    GNUTLS_E_UNEXPECTED_PACKET_LENGTH, or
260 			 *    "A TLS packet with unexpected length was received."
261 			 *
262 			 * Normally the server would have closed the connection
263 			 * cleanly and this second read() request would have returned
264 			 * 0.  Or maybe it's normal for SSL connections to be severed
265 			 * in this manner?  In any case, this differs from the behavior
266 			 * of the standard recv() system call.
267 			 */
268 			purple_debug_warning("okcupid",
269 				"ssl error, but data received.  attempting to continue\n");
270 		} else {
271 			/* TODO: Is this a regular occurrence?  If so then maybe resend the request? */
272 			okc_fatal_connection_cb(okconn);
273 			return;
274 		}
275 	}
276 
277 	if (len > 0)
278 	{
279 		buf[len] = '\0';
280 
281 		okconn->rx_buf = g_realloc(okconn->rx_buf,
282 				okconn->rx_len + len + 1);
283 		memcpy(okconn->rx_buf + okconn->rx_len, buf, len + 1);
284 		okconn->rx_len += len;
285 
286 		/* Wait for more data before processing */
287 		return;
288 	}
289 
290 	/* The server closed the connection, let's parse the data */
291 	okc_connection_process_data(okconn);
292 
293 	okc_connection_destroy(okconn);
294 
295 	okc_next_connection(oca);
296 }
297 
okc_post_or_get_ssl_readdata_cb(gpointer data,PurpleSslConnection * ssl,PurpleInputCondition cond)298 static void okc_post_or_get_ssl_readdata_cb (gpointer data,
299 		PurpleSslConnection *ssl, PurpleInputCondition cond)
300 {
301 	okc_post_or_get_readdata_cb(data, -1, cond);
302 }
303 
okc_post_or_get_connect_cb(gpointer data,gint source,const gchar * error_message)304 static void okc_post_or_get_connect_cb(gpointer data, gint source,
305 		const gchar *error_message)
306 {
307 	OkCupidConnection *okconn;
308 	ssize_t len;
309 
310 	okconn = data;
311 	okconn->connect_data = NULL;
312 
313 	if (error_message)
314 	{
315 		purple_debug_error("okcupid", "post_or_get_connect_cb %s\n",
316 				error_message);
317 		okc_fatal_connection_cb(okconn);
318 		return;
319 	}
320 
321 	purple_debug_info("okcupid", "post_or_get_connect_cb\n");
322 	okconn->fd = source;
323 
324 	len = write(okconn->fd, okconn->request->str,
325 			okconn->request->len);
326 	if(len != okconn->request->len) {
327 		purple_debug_error("okcupid", "post_or_get_connect_cb failed to write request\n");
328 		okc_fatal_connection_cb(okconn);
329 		return;
330 	}
331 
332 	okconn->input_watcher = purple_input_add(okconn->fd,
333 			PURPLE_INPUT_READ,
334 			okc_post_or_get_readdata_cb, okconn);
335 }
336 
okc_post_or_get_ssl_connect_cb(gpointer data,PurpleSslConnection * ssl,PurpleInputCondition cond)337 static void okc_post_or_get_ssl_connect_cb(gpointer data,
338 		PurpleSslConnection *ssl, PurpleInputCondition cond)
339 {
340 	OkCupidConnection *okconn;
341 	ssize_t len;
342 
343 	okconn = data;
344 
345 	purple_debug_info("okcupid", "post_or_get_ssl_connect_cb\n");
346 
347 	len = purple_ssl_write(okconn->ssl_conn,
348 			okconn->request->str, okconn->request->len);
349 	if(len != okconn->request->len) {
350 		purple_debug_error("okcupid", "post_or_get_ssl_connect_cb failed to write request\n");
351 		okc_fatal_connection_cb(okconn);
352 		return;
353 	}
354 
355 	purple_ssl_input_add(okconn->ssl_conn,
356 			okc_post_or_get_ssl_readdata_cb, okconn);
357 }
358 
okc_host_lookup_cb(GSList * hosts,gpointer data,const char * error_message)359 static void okc_host_lookup_cb(GSList *hosts, gpointer data,
360 		const char *error_message)
361 {
362 	GSList *host_lookup_list;
363 	struct sockaddr_in *addr;
364 	gchar *hostname;
365 	gchar *ip_address;
366 	OkCupidAccount *oca;
367 	PurpleDnsQueryData *query;
368 
369 	purple_debug_info("okcupid", "updating cache of dns addresses\n");
370 
371 	/* Extract variables */
372 	host_lookup_list = data;
373 
374 	oca = host_lookup_list->data;
375 	host_lookup_list =
376 			g_slist_delete_link(host_lookup_list, host_lookup_list);
377 	hostname = host_lookup_list->data;
378 	host_lookup_list =
379 			g_slist_delete_link(host_lookup_list, host_lookup_list);
380 	query = host_lookup_list->data;
381 	host_lookup_list =
382 			g_slist_delete_link(host_lookup_list, host_lookup_list);
383 
384 	/* The callback has executed, so we no longer need to keep track of
385 	 * the original query.  This always needs to run when the cb is
386 	 * executed. */
387 	oca->dns_queries = g_slist_remove(oca->dns_queries, query);
388 
389 	/* Any problems, capt'n? */
390 	if (error_message != NULL)
391 	{
392 		purple_debug_warning("okcupid",
393 				"Error doing host lookup: %s\n", error_message);
394 		return;
395 	}
396 
397 	if (hosts == NULL)
398 	{
399 		purple_debug_warning("okcupid",
400 				"Could not resolve host name\n");
401 		return;
402 	}
403 
404 	/* Discard the length... */
405 	hosts = g_slist_delete_link(hosts, hosts);
406 	/* Copy the address then free it... */
407 	addr = hosts->data;
408 	ip_address = g_strdup(inet_ntoa(addr->sin_addr));
409 	g_free(addr);
410 	hosts = g_slist_delete_link(hosts, hosts);
411 
412 	/*
413 	 * DNS lookups can return a list of IP addresses, but we only cache
414 	 * the first one.  So free the rest.
415 	 */
416 	while (hosts != NULL)
417 	{
418 		/* Discard the length... */
419 		hosts = g_slist_delete_link(hosts, hosts);
420 		/* Free the address... */
421 		g_free(hosts->data);
422 		hosts = g_slist_delete_link(hosts, hosts);
423 	}
424 
425 	purple_debug_info("okcupid", "Host %s has IP %s\n",
426 			hostname, ip_address);
427 
428 	g_hash_table_insert(oca->hostname_ip_cache, hostname, ip_address);
429 }
430 
okc_cookie_foreach_cb(gchar * cookie_name,gchar * cookie_value,GString * str)431 static void okc_cookie_foreach_cb(gchar *cookie_name,
432 		gchar *cookie_value, GString *str)
433 {
434 	/* TODO: Need to escape name and value? */
435 	g_string_append_printf(str, "%s=%s;", cookie_name, cookie_value);
436 }
437 
okc_cookies_to_string(OkCupidAccount * oca)438 static gchar *okc_cookies_to_string(OkCupidAccount *oca)
439 {
440 	GString *str;
441 
442 	str = g_string_new(NULL);
443 
444 	g_hash_table_foreach(oca->cookie_table,
445 			(GHFunc)okc_cookie_foreach_cb, str);
446 
447 	return g_string_free(str, FALSE);
448 }
449 
okc_ssl_connection_error(PurpleSslConnection * ssl,PurpleSslErrorType errortype,gpointer data)450 static void okc_ssl_connection_error(PurpleSslConnection *ssl,
451 		PurpleSslErrorType errortype, gpointer data)
452 {
453 	OkCupidConnection *okconn = data;
454 	PurpleConnection *pc = okconn->oca->pc;
455 
456 	okconn->ssl_conn = NULL;
457 	okc_connection_destroy(okconn);
458 	purple_connection_ssl_error(pc, errortype);
459 }
460 
okc_post_or_get(OkCupidAccount * oca,OkCupidMethod method,const gchar * host,const gchar * url,const gchar * postdata,OkCupidProxyCallbackFunc callback_func,gpointer user_data,gboolean keepalive)461 void okc_post_or_get(OkCupidAccount *oca, OkCupidMethod method,
462 		const gchar *host, const gchar *url, const gchar *postdata,
463 		OkCupidProxyCallbackFunc callback_func, gpointer user_data,
464 		gboolean keepalive)
465 {
466 	GString *request;
467 	gchar *cookies;
468 	OkCupidConnection *okconn;
469 	gchar *real_url;
470 	gboolean is_proxy = FALSE;
471 	const gchar* const *languages;
472 	gchar *language_names;
473 	PurpleProxyInfo *proxy_info = NULL;
474 	gchar *proxy_auth;
475 	gchar *proxy_auth_base64;
476 
477 	/* TODO: Fix keepalive and use it as much as possible */
478 	keepalive = FALSE;
479 
480 	if (purple_account_get_bool(oca->account, "force_https", TRUE) && !(method & OKC_METHOD_SSL) &&
481 			(!host || g_str_equal(host, "www.okcupid.com") || g_str_equal(host, "api.okcupid.com")))
482 	{
483 		method |= OKC_METHOD_SSL;
484 	}
485 
486 	if (method & OKC_METHOD_SSL)
487 		host = "www.okcupid.com";
488 	if (host == NULL && oca && oca->account)
489 		host = purple_account_get_string(oca->account, "host", "api.okcupid.com");
490 	if (host == NULL)
491 		host = "api.okcupid.com";
492 
493 	if (oca && oca->account && !(method & OKC_METHOD_SSL))
494 	{
495 		proxy_info = purple_proxy_get_setup(oca->account);
496 		if (purple_proxy_info_get_type(proxy_info) == PURPLE_PROXY_USE_GLOBAL)
497 			proxy_info = purple_global_proxy_get_info();
498 		if (purple_proxy_info_get_type(proxy_info) == PURPLE_PROXY_HTTP)
499 		{
500 			is_proxy = TRUE;
501 		}
502 	}
503 	if (is_proxy == TRUE)
504 	{
505 		real_url = g_strdup_printf("http://%s%s", host, url);
506 	} else {
507 		real_url = g_strdup(url);
508 	}
509 
510 	cookies = okc_cookies_to_string(oca);
511 
512 	if (method & OKC_METHOD_POST && !postdata)
513 		postdata = "";
514 
515 	/* Build the request */
516 	request = g_string_new(NULL);
517 	g_string_append_printf(request, "%s %s HTTP/1.0\r\n",
518 			(method & OKC_METHOD_POST) ? "POST" : "GET",
519 			real_url);
520 
521 	if (is_proxy == FALSE)
522 		g_string_append_printf(request, "Host: %s\r\n", host);
523 	g_string_append_printf(request, "Connection: %s\r\n",
524 			(keepalive ? "Keep-Alive" : "close"));
525 	g_string_append_printf(request, "User-Agent: %s (libpurple %s)\r\n", purple_core_get_ui(), purple_core_get_version());
526 	if (method & OKC_METHOD_POST) {
527 		g_string_append_printf(request,
528 				"Content-Type: application/x-www-form-urlencoded\r\n");
529 		g_string_append_printf(request,
530 				"Content-length: %zu\r\n", strlen(postdata));
531 	}
532 	g_string_append_printf(request, "Accept: */*\r\n");
533 	g_string_append_printf(request, "Cookie: %s\r\n", cookies);
534 	g_string_append_printf(request, "Accept-Encoding: gzip\r\n");
535 	g_string_append_printf(request, "X-OkCupid-Api-Version: 1\r\n");
536 
537 	if (is_proxy == TRUE)
538 	{
539 		if (purple_proxy_info_get_username(proxy_info) &&
540 			purple_proxy_info_get_password(proxy_info))
541 		{
542 			proxy_auth = g_strdup_printf("%s:%s", purple_proxy_info_get_username(proxy_info), purple_proxy_info_get_password(proxy_info));
543 			proxy_auth_base64 = purple_base64_encode((guchar *)proxy_auth, strlen(proxy_auth));
544 			g_string_append_printf(request, "Proxy-Authorization: Basic %s\r\n", proxy_auth_base64);
545 			g_free(proxy_auth_base64);
546 			g_free(proxy_auth);
547 		}
548 	}
549 	/* Tell the server what language we accept, so that we get error messages in our language (rather than our IP's) */
550 	languages = g_get_language_names();
551 	language_names = g_strjoinv(", ", (gchar **)languages);
552 	purple_util_chrreplace(language_names, '_', '-');
553 	g_string_append_printf(request, "Accept-Language: %s\r\n", language_names);
554 	g_free(language_names);
555 
556 	purple_debug_misc("okcupid", "requesting url %s\n", url);
557 
558 	g_string_append_printf(request, "\r\n");
559 	if (method & OKC_METHOD_POST)
560 		g_string_append_printf(request, "%s", postdata);
561 
562 	/* If it needs to go over a SSL connection, we probably shouldn't print
563 	 * it in the debug log.  Without this condition a user's password is
564 	 * printed in the debug log */
565 	if (method == OKC_METHOD_POST)
566 		purple_debug_misc("okcupid", "sending request data:\n%s\n",
567 			postdata);
568 
569 	g_free(cookies);
570 	g_free(real_url);
571 	/*
572 	 * Do a separate DNS lookup for the given host name and cache it
573 	 * for next time.
574 	 *
575 	 * TODO: It would be better if we did this before we call
576 	 *       purple_proxy_connect(), so we could re-use the result.
577 	 *       Or even better: Use persistent HTTP connections for servers
578 	 *       that we access continually.
579 	 *
580 	 * TODO: This cache of the hostname<-->IP address does not respect
581 	 *       the TTL returned by the DNS server.  We should expire things
582 	 *       from the cache after some amount of time.
583 	 */
584 	if (!is_proxy && !g_hostname_is_ip_address(host) && !(method & OKC_METHOD_SSL))
585 	{
586 		/* Don't do this for proxy connections, since proxies do the DNS lookup */
587 		gchar *host_ip;
588 
589 		host_ip = g_hash_table_lookup(oca->hostname_ip_cache, host);
590 		if (host_ip != NULL) {
591 			purple_debug_info("okcupid",
592 					"swapping original host %s with cached value of %s\n",
593 					host, host_ip);
594 			host = host_ip;
595 		} else if (oca->account && !oca->account->disconnecting) {
596 			GSList *host_lookup_list = NULL;
597 			PurpleDnsQueryData *query;
598 
599 			host_lookup_list = g_slist_prepend(
600 					host_lookup_list, g_strdup(host));
601 			host_lookup_list = g_slist_prepend(
602 					host_lookup_list, oca);
603 
604 			query = purple_dnsquery_a(host, 80,
605 					okc_host_lookup_cb, host_lookup_list);
606 			oca->dns_queries = g_slist_prepend(oca->dns_queries, query);
607 			host_lookup_list = g_slist_append(host_lookup_list, query);
608 		}
609 	}
610 
611 	okconn = g_new0(OkCupidConnection, 1);
612 	okconn->oca = oca;
613 	okconn->method = method;
614 	okconn->hostname = g_strdup(host);
615 	okconn->request = request;
616 	okconn->callback = callback_func;
617 	okconn->user_data = user_data;
618 	okconn->fd = -1;
619 	okconn->connection_keepalive = keepalive;
620 	okconn->request_time = time(NULL);
621 
622 	g_queue_push_head(oca->waiting_conns, okconn);
623 	okc_next_connection(oca);
624 }
625 
okc_next_connection(OkCupidAccount * oca)626 static void okc_next_connection(OkCupidAccount *oca)
627 {
628 	OkCupidConnection *okconn;
629 
630 	g_return_if_fail(oca != NULL);
631 
632 	if (!g_queue_is_empty(oca->waiting_conns))
633 	{
634 		if(g_slist_length(oca->conns) < OKC_MAX_CONNECTIONS)
635 		{
636 			okconn = g_queue_pop_tail(oca->waiting_conns);
637 			okc_attempt_connection(okconn);
638 		}
639 	}
640 }
641 
okc_attempt_connection(OkCupidConnection * okconn)642 static void okc_attempt_connection(OkCupidConnection *okconn)
643 {
644 	OkCupidAccount *oca = okconn->oca;
645 
646 	oca->conns = g_slist_prepend(oca->conns, okconn);
647 
648 	if (okconn->method & OKC_METHOD_SSL) {
649 		okconn->ssl_conn = purple_ssl_connect(oca->account, okconn->hostname,
650 				443, okc_post_or_get_ssl_connect_cb,
651 				okc_ssl_connection_error, okconn);
652 	} else {
653 		okconn->connect_data = purple_proxy_connect(NULL, oca->account,
654 				okconn->hostname, 80, okc_post_or_get_connect_cb, okconn);
655 	}
656 
657 	return;
658 }
659 
660