diff libpurple/protocols/novell/nmconn.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 44b4e8bd759b
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libpurple/protocols/novell/nmconn.c	Sat Jan 20 02:32:10 2007 +0000
@@ -0,0 +1,701 @@
+/*
+ * nmconn.c
+ *
+ * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA	02111-1307	USA
+ *
+ */
+
+#include <glib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include "nmconn.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#define NO_ESCAPE(ch) ((ch == 0x20) || (ch >= 0x30 && ch <= 0x39) || \
+					(ch >= 0x41 && ch <= 0x5a) || (ch >= 0x61 && ch <= 0x7a))
+
+/* Read data from conn until the end of a line */
+static NMERR_T
+read_line(NMConn * conn, char *buff, int len)
+{
+	NMERR_T rc = NM_OK;
+	int total_bytes = 0;
+
+	while ((rc == NM_OK) && (total_bytes < (len - 1))) {
+		rc = nm_read_all(conn, &buff[total_bytes], 1);
+		if (rc == NM_OK) {
+			total_bytes += 1;
+			if (buff[total_bytes - 1] == '\n') {
+				break;
+			}
+		}
+	}
+	buff[total_bytes] = '\0';
+
+	return rc;
+}
+
+static char *
+url_escape_string(char *src)
+{
+	guint32 escape = 0;
+	char *p;
+	char *q;
+	char *encoded = NULL;
+	int ch;
+
+	static const char hex_table[16] = "0123456789abcdef";
+
+	if (src == NULL) {
+		return NULL;
+	}
+
+	/* Find number of chars to escape */
+	for (p = src; *p != '\0'; p++) {
+		ch = (guchar) *p;
+		if (!NO_ESCAPE(ch)) {
+			escape++;
+		}
+	}
+
+	encoded = g_malloc((p - src) + (escape * 2) + 1);
+
+	/* Escape the string */
+	for (p = src, q = encoded; *p != '\0'; p++) {
+		ch = (guchar) * p;
+		if (NO_ESCAPE(ch)) {
+			if (ch != 0x20) {
+				*q = ch;
+				q++;
+			} else {
+				*q = '+';
+				q++;
+			}
+		} else {
+			*q = '%';
+			q++;
+
+			*q = hex_table[ch >> 4];
+			q++;
+
+			*q = hex_table[ch & 15];
+			q++;
+		}
+	}
+	*q = '\0';
+
+	return encoded;
+}
+
+static char *
+encode_method(guint8 method)
+{
+	char *str;
+
+	switch (method) {
+		case NMFIELD_METHOD_EQUAL:
+			str = "G";
+			break;
+		case NMFIELD_METHOD_UPDATE:
+			str = "F";
+			break;
+		case NMFIELD_METHOD_GTE:
+			str = "E";
+			break;
+		case NMFIELD_METHOD_LTE:
+			str = "D";
+			break;
+		case NMFIELD_METHOD_NE:
+			str = "C";
+			break;
+		case NMFIELD_METHOD_EXIST:
+			str = "B";
+			break;
+		case NMFIELD_METHOD_NOTEXIST:
+			str = "A";
+			break;
+		case NMFIELD_METHOD_SEARCH:
+			str = "9";
+			break;
+		case NMFIELD_METHOD_MATCHBEGIN:
+			str = "8";
+			break;
+		case NMFIELD_METHOD_MATCHEND:
+			str = "7";
+			break;
+		case NMFIELD_METHOD_NOT_ARRAY:
+			str = "6";
+			break;
+		case NMFIELD_METHOD_OR_ARRAY:
+			str = "5";
+			break;
+		case NMFIELD_METHOD_AND_ARRAY:
+			str = "4";
+			break;
+		case NMFIELD_METHOD_DELETE_ALL:
+			str = "3";
+			break;
+		case NMFIELD_METHOD_DELETE:
+			str = "2";
+			break;
+		case NMFIELD_METHOD_ADD:
+			str = "1";
+			break;
+		default:					/* NMFIELD_METHOD_VALID */
+			str = "0";
+			break;
+	}
+
+	return str;
+}
+
+NMConn *
+nm_create_conn(const char *addr, int port)
+{
+	NMConn *conn = 	g_new0(NMConn, 1);
+	conn->addr = g_strdup(addr);
+	conn->port = port;
+	return conn;
+}
+
+void nm_release_conn(NMConn *conn)
+{
+	if (conn) {
+		GSList *node;
+		for (node = conn->requests; node; node = node->next) {
+			if (node->data)
+				nm_release_request(node->data);
+		}
+		g_slist_free(conn->requests);
+		conn->requests = NULL;
+		if (conn->ssl_conn) {
+			g_free(conn->ssl_conn);
+			conn->ssl_conn = NULL;
+		}
+		g_free(conn->addr);
+		conn->addr = NULL;
+		g_free(conn);
+	}
+}
+
+int
+nm_tcp_write(NMConn * conn, const void *buff, int len)
+{
+	if (conn == NULL || buff == NULL)
+		return -1;
+
+	if (!conn->use_ssl)
+		return (write(conn->fd, buff, len));
+	else if (conn->ssl_conn && conn->ssl_conn->write)
+		return (conn->ssl_conn->write(conn->ssl_conn->data, buff, len));
+	else
+		return -1;
+}
+
+int
+nm_tcp_read(NMConn * conn, void *buff, int len)
+{
+	if (conn == NULL || buff == NULL)
+		return -1;
+
+	if (!conn->use_ssl)
+		return (read(conn->fd, buff, len));
+	else if (conn->ssl_conn && conn->ssl_conn->read)
+		return ((conn->ssl_conn->read)(conn->ssl_conn->data, buff, len));
+	else
+		return -1;
+}
+
+NMERR_T
+nm_read_all(NMConn * conn, char *buff, int len)
+{
+	NMERR_T rc = NM_OK;
+	int bytes_left = len;
+	int bytes_read;
+	int total_bytes = 0;
+	int retry = 1000;
+
+	if (conn == NULL || buff == NULL)
+		return NMERR_BAD_PARM;
+
+	/* Keep reading until buffer is full */
+	while (bytes_left) {
+		bytes_read = nm_tcp_read(conn, &buff[total_bytes], bytes_left);
+		if (bytes_read > 0) {
+			bytes_left -= bytes_read;
+			total_bytes += bytes_read;
+		} else {
+			if (errno == EAGAIN) {
+				if (--retry == 0) {
+					rc = NMERR_TCP_READ;
+					break;
+				}
+#ifdef _WIN32
+				Sleep(1);
+#else
+				usleep(1000);
+#endif
+			} else {
+				rc = NMERR_TCP_READ;
+				break;
+			}
+		}
+	}
+	return rc;
+}
+
+NMERR_T
+nm_read_uint32(NMConn *conn, guint32 *val)
+{
+	NMERR_T rc = NM_OK;
+
+	rc = nm_read_all(conn, (char *)val, sizeof(*val));
+	if (rc == NM_OK) {
+		*val = GUINT32_FROM_LE(*val);
+	}
+
+	return rc;
+}
+
+NMERR_T
+nm_read_uint16(NMConn *conn, guint16 *val)
+{
+	NMERR_T rc = NM_OK;
+
+	rc = nm_read_all(conn, (char *)val, sizeof(*val));
+	if (rc == NM_OK) {
+		*val = GUINT16_FROM_LE(*val);
+	}
+
+	return rc;
+}
+
+NMERR_T
+nm_write_fields(NMConn * conn, NMField * fields)
+{
+	NMERR_T rc = NM_OK;
+	NMField *field;
+	char *value = NULL;
+	char *method = NULL;
+	char buffer[4096];
+	int ret;
+	int bytes_to_send;
+	int val = 0;
+
+	if (conn == NULL || fields == NULL) {
+		return NMERR_BAD_PARM;
+	}
+
+	/* Format each field as valid "post" data and write it out */
+	for (field = fields; (rc == NM_OK) && (field->tag); field++) {
+
+		/* We don't currently handle binary types */
+		if (field->method == NMFIELD_METHOD_IGNORE ||
+			field->type == NMFIELD_TYPE_BINARY) {
+			continue;
+		}
+
+		/* Write the field tag */
+		bytes_to_send = g_snprintf(buffer, sizeof(buffer), "&tag=%s", field->tag);
+		ret = nm_tcp_write(conn, buffer, bytes_to_send);
+		if (ret < 0) {
+			rc = NMERR_TCP_WRITE;
+		}
+
+		/* Write the field method */
+		if (rc == NM_OK) {
+			method = encode_method(field->method);
+			bytes_to_send = g_snprintf(buffer, sizeof(buffer), "&cmd=%s", method);
+			ret = nm_tcp_write(conn, buffer, bytes_to_send);
+			if (ret < 0) {
+				rc = NMERR_TCP_WRITE;
+			}
+		}
+
+		/* Write the field value */
+		if (rc == NM_OK) {
+			switch (field->type) {
+				case NMFIELD_TYPE_UTF8:
+				case NMFIELD_TYPE_DN:
+
+					value = url_escape_string((char *) field->ptr_value);
+					bytes_to_send = g_snprintf(buffer, sizeof(buffer),
+											   "&val=%s", value);
+					if (bytes_to_send > (int)sizeof(buffer)) {
+						ret = nm_tcp_write(conn, buffer, sizeof(buffer));
+					} else {
+						ret = nm_tcp_write(conn, buffer, bytes_to_send);
+					}
+
+					if (ret < 0) {
+						rc = NMERR_TCP_WRITE;
+					}
+
+					g_free(value);
+
+					break;
+
+				case NMFIELD_TYPE_ARRAY:
+				case NMFIELD_TYPE_MV:
+
+					val = nm_count_fields((NMField *) field->ptr_value);
+					bytes_to_send = g_snprintf(buffer, sizeof(buffer),
+											   "&val=%u", val);
+					ret = nm_tcp_write(conn, buffer, bytes_to_send);
+					if (ret < 0) {
+						rc = NMERR_TCP_WRITE;
+					}
+
+					break;
+
+				default:
+
+					bytes_to_send = g_snprintf(buffer, sizeof(buffer),
+											   "&val=%u", field->value);
+					ret = nm_tcp_write(conn, buffer, bytes_to_send);
+					if (ret < 0) {
+						rc = NMERR_TCP_WRITE;
+					}
+
+					break;
+			}
+		}
+
+		/* Write the field type */
+		if (rc == NM_OK) {
+			bytes_to_send = g_snprintf(buffer, sizeof(buffer),
+									   "&type=%u", field->type);
+			ret = nm_tcp_write(conn, buffer, bytes_to_send);
+			if (ret < 0) {
+				rc = NMERR_TCP_WRITE;
+			}
+		}
+
+		/* If the field is a sub array then post its fields */
+		if (rc == NM_OK && val > 0) {
+			if (field->type == NMFIELD_TYPE_ARRAY ||
+				field->type == NMFIELD_TYPE_MV) {
+
+				rc = nm_write_fields(conn, (NMField *) field->ptr_value);
+
+			}
+		}
+	}
+
+	return rc;
+}
+
+NMERR_T
+nm_send_request(NMConn *conn, char *cmd, NMField *fields,
+				nm_response_cb cb, gpointer data, NMRequest **request)
+{
+	NMERR_T rc = NM_OK;
+	char buffer[512];
+	int bytes_to_send;
+	int ret;
+	NMField *request_fields = NULL;
+	char *str = NULL;
+
+	if (conn == NULL || cmd == NULL)
+		return NMERR_BAD_PARM;
+
+	/* Write the post */
+	bytes_to_send = g_snprintf(buffer, sizeof(buffer),
+							   "POST /%s HTTP/1.0\r\n", cmd);
+	ret = nm_tcp_write(conn, buffer, bytes_to_send);
+	if (ret < 0) {
+		rc = NMERR_TCP_WRITE;
+	}
+
+	/* Write headers */
+	if (rc == NM_OK) {
+		if (strcmp("login", cmd) == 0) {
+			bytes_to_send = g_snprintf(buffer, sizeof(buffer),
+									   "Host: %s:%d\r\n\r\n", conn->addr, conn->port);
+			ret = nm_tcp_write(conn, buffer, bytes_to_send);
+			if (ret < 0) {
+				rc = NMERR_TCP_WRITE;
+			}
+		} else {
+			bytes_to_send = g_snprintf(buffer, sizeof(buffer), "\r\n");
+			ret = nm_tcp_write(conn, buffer, bytes_to_send);
+			if (ret < 0) {
+				rc = NMERR_TCP_WRITE;
+			}
+		}
+	}
+
+	/* Add the transaction id to the request fields */
+	if (rc == NM_OK) {
+		if (fields)
+			request_fields = nm_copy_field_array(fields);
+
+		str = g_strdup_printf("%d", ++(conn->trans_id));
+		request_fields = nm_field_add_pointer(request_fields, NM_A_SZ_TRANSACTION_ID, 0,
+											  NMFIELD_METHOD_VALID, 0,
+											  str, NMFIELD_TYPE_UTF8);
+	}
+
+	/* Send the request to the server */
+	if (rc == NM_OK) {
+		rc = nm_write_fields(conn, request_fields);
+	}
+
+	/* Write the CRLF to terminate the data */
+	if (rc == NM_OK) {
+		ret = nm_tcp_write(conn, "\r\n", strlen("\r\n"));
+		if (ret < 0) {
+			rc = NMERR_TCP_WRITE;
+		}
+	}
+
+	/* Create a request struct, add it to our queue, and return it */
+	if (rc == NM_OK) {
+		NMRequest *new_request = nm_create_request(cmd, conn->trans_id,
+												   time(0), cb, NULL, data);
+		nm_conn_add_request_item(conn, new_request);
+
+		/* Set the out param if it was sent in, otherwise release the request */
+		if (request)
+			*request = new_request;
+		else
+			nm_release_request(new_request);
+	}
+
+	if (request_fields != NULL)
+		nm_free_fields(&request_fields);
+
+	return rc;
+}
+
+NMERR_T
+nm_read_header(NMConn * conn)
+{
+	NMERR_T rc = NM_OK;
+	char buffer[512];
+	char *ptr = NULL;
+	int i;
+	char rtn_buf[8];
+	int rtn_code = 0;
+
+	if (conn == NULL)
+		return NMERR_BAD_PARM;
+
+	*buffer = '\0';
+	rc = read_line(conn, buffer, sizeof(buffer));
+	if (rc == NM_OK) {
+
+		/* Find the return code */
+		ptr = strchr(buffer, ' ');
+		if (ptr != NULL) {
+			ptr++;
+
+			i = 0;
+			while (isdigit(*ptr) && (i < 3)) {
+				rtn_buf[i] = *ptr;
+				i++;
+				ptr++;
+			}
+			rtn_buf[i] = '\0';
+
+			if (i > 0)
+				rtn_code = atoi(rtn_buf);
+		}
+	}
+
+	/* Finish reading header, in the future we might want to do more processing here */
+	/* TODO: handle more general redirects in the future */
+	while ((rc == NM_OK) && (strcmp(buffer, "\r\n") != 0)) {
+		rc = read_line(conn, buffer, sizeof(buffer));
+	}
+
+	if (rc == NM_OK && rtn_code == 301)
+		rc = NMERR_SERVER_REDIRECT;
+
+	return rc;
+}
+
+NMERR_T
+nm_read_fields(NMConn * conn, int count, NMField ** fields)
+{
+	NMERR_T rc = NM_OK;
+	guint8 type;
+	guint8 method;
+	guint32 val;
+	char tag[64];
+	NMField *sub_fields = NULL;
+	char *str = NULL;
+
+	if (conn == NULL || fields == NULL)
+		return NMERR_BAD_PARM;
+
+	do {
+		if (count > 0) {
+			count--;
+		}
+
+		/* Read the field type, method, and tag */
+		rc = nm_read_all(conn, (char *)&type, sizeof(type));
+		if (rc != NM_OK || type == 0)
+			break;
+
+		rc = nm_read_all(conn, (char *)&method, sizeof(method));
+		if (rc != NM_OK)
+			break;
+
+		rc = nm_read_uint32(conn, &val);
+		if (rc != NM_OK)
+			break;
+
+		if (val > sizeof(tag)) {
+			rc = NMERR_PROTOCOL;
+			break;
+		}
+
+		rc = nm_read_all(conn, tag, val);
+		if (rc != NM_OK)
+			break;
+
+		if (type == NMFIELD_TYPE_MV || type == NMFIELD_TYPE_ARRAY) {
+
+			/* Read the subarray (first read the number of items in the array) */
+			rc = nm_read_uint32(conn, &val);
+			if (rc != NM_OK)
+				break;
+
+			if (val > 0) {
+				rc = nm_read_fields(conn, val, &sub_fields);
+				if (rc != NM_OK)
+					break;
+			}
+
+			*fields = nm_field_add_pointer(*fields, tag, 0, method,
+									   0, sub_fields, type);
+
+			sub_fields = NULL;
+
+		} else if (type == NMFIELD_TYPE_UTF8 || type == NMFIELD_TYPE_DN) {
+
+			/* Read the string (first read the length) */
+			rc = nm_read_uint32(conn, &val);
+			if (rc != NM_OK)
+				break;
+
+			if (val >= NMFIELD_MAX_STR_LENGTH) {
+				rc = NMERR_PROTOCOL;
+				break;
+			}
+
+			if (val > 0) {
+				str = g_new0(char, val + 1);
+
+				rc = nm_read_all(conn, str, val);
+				if (rc != NM_OK)
+					break;
+
+				*fields = nm_field_add_pointer(*fields, tag, 0, method,
+											   0, str, type);
+				str = NULL;
+			}
+
+		} else {
+
+			/* Read the numerical value */
+			rc = nm_read_uint32(conn, &val);
+			if (rc != NM_OK)
+				break;
+
+			*fields = nm_field_add_number(*fields, tag, 0, method,
+										  0, val, type);
+		}
+
+	} while ((type != 0) && (count != 0));
+
+
+	if (str != NULL) {
+		g_free(str);
+	}
+
+	if (sub_fields != NULL) {
+		nm_free_fields(&sub_fields);
+	}
+
+	return rc;
+}
+
+void
+nm_conn_add_request_item(NMConn * conn, NMRequest * request)
+{
+	if (conn == NULL || request == NULL)
+		return;
+
+	nm_request_add_ref(request);
+	conn->requests = g_slist_append(conn->requests, request);
+}
+
+void
+nm_conn_remove_request_item(NMConn * conn, NMRequest * request)
+{
+	if (conn == NULL || request == NULL)
+		return;
+
+	conn->requests = g_slist_remove(conn->requests, request);
+	nm_release_request(request);
+}
+
+NMRequest *
+nm_conn_find_request(NMConn * conn, int trans_id)
+{
+	NMRequest *req = NULL;
+	GSList *itr = NULL;
+
+	if (conn == NULL)
+		return NULL;
+
+	itr = conn->requests;
+	while (itr) {
+		req = (NMRequest *) itr->data;
+		if (req != NULL && nm_request_get_trans_id(req) == trans_id) {
+			return req;
+		}
+		itr = g_slist_next(itr);
+	}
+	return NULL;
+}
+
+const char *
+nm_conn_get_addr(NMConn * conn)
+{
+	if (conn == NULL)
+		return NULL;
+	else
+		return conn->addr;
+}
+
+int
+nm_conn_get_port(NMConn * conn)
+{
+	if (conn == NULL)
+		return -1;
+	else
+		return conn->port;
+}