Use GSList functions instead of manual iterations
[pidgin-git.git] / libpurple / protocols / novell / nmconn.c
blob04d0c4e4d6ca60723900863f07f84c11e86ff80a
1 /*
2 * nmconn.c
4 * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
21 #include <glib.h>
22 #include <unistd.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <time.h>
27 #include "nmconn.h"
29 #ifdef _WIN32
30 #include <windows.h>
31 #endif
33 #include "util.h"
35 #define NO_ESCAPE(ch) ((ch == 0x20) || (ch >= 0x30 && ch <= 0x39) || \
36 (ch >= 0x41 && ch <= 0x5a) || (ch >= 0x61 && ch <= 0x7a))
38 /* Read data from conn until the end of a line */
39 static NMERR_T
40 read_line(NMConn * conn, char *buff, int len)
42 NMERR_T rc = NM_OK;
43 int total_bytes = 0;
45 while ((rc == NM_OK) && (total_bytes < (len - 1))) {
46 rc = nm_read_all(conn, &buff[total_bytes], 1);
47 if (rc == NM_OK) {
48 total_bytes += 1;
49 if (buff[total_bytes - 1] == '\n') {
50 break;
54 buff[total_bytes] = '\0';
56 return rc;
59 static char *
60 url_escape_string(char *src)
62 guint32 escape = 0;
63 char *p;
64 char *q;
65 char *encoded = NULL;
66 int ch;
68 static const char hex_table[16] = "0123456789abcdef";
70 if (src == NULL) {
71 return NULL;
74 /* Find number of chars to escape */
75 for (p = src; *p != '\0'; p++) {
76 ch = (guchar) *p;
77 if (!NO_ESCAPE(ch)) {
78 escape++;
82 encoded = g_malloc((p - src) + (escape * 2) + 1);
84 /* Escape the string */
85 for (p = src, q = encoded; *p != '\0'; p++) {
86 ch = (guchar) * p;
87 if (NO_ESCAPE(ch)) {
88 if (ch != 0x20) {
89 *q = ch;
90 q++;
91 } else {
92 *q = '+';
93 q++;
95 } else {
96 *q = '%';
97 q++;
99 *q = hex_table[ch >> 4];
100 q++;
102 *q = hex_table[ch & 15];
103 q++;
106 *q = '\0';
108 return encoded;
111 static char *
112 encode_method(guint8 method)
114 char *str;
116 switch (method) {
117 case NMFIELD_METHOD_EQUAL:
118 str = "G";
119 break;
120 case NMFIELD_METHOD_UPDATE:
121 str = "F";
122 break;
123 case NMFIELD_METHOD_GTE:
124 str = "E";
125 break;
126 case NMFIELD_METHOD_LTE:
127 str = "D";
128 break;
129 case NMFIELD_METHOD_NE:
130 str = "C";
131 break;
132 case NMFIELD_METHOD_EXIST:
133 str = "B";
134 break;
135 case NMFIELD_METHOD_NOTEXIST:
136 str = "A";
137 break;
138 case NMFIELD_METHOD_SEARCH:
139 str = "9";
140 break;
141 case NMFIELD_METHOD_MATCHBEGIN:
142 str = "8";
143 break;
144 case NMFIELD_METHOD_MATCHEND:
145 str = "7";
146 break;
147 case NMFIELD_METHOD_NOT_ARRAY:
148 str = "6";
149 break;
150 case NMFIELD_METHOD_OR_ARRAY:
151 str = "5";
152 break;
153 case NMFIELD_METHOD_AND_ARRAY:
154 str = "4";
155 break;
156 case NMFIELD_METHOD_DELETE_ALL:
157 str = "3";
158 break;
159 case NMFIELD_METHOD_DELETE:
160 str = "2";
161 break;
162 case NMFIELD_METHOD_ADD:
163 str = "1";
164 break;
165 default: /* NMFIELD_METHOD_VALID */
166 str = "0";
167 break;
170 return str;
173 NMConn *
174 nm_create_conn(const char *addr, int port)
176 NMConn *conn = g_new0(NMConn, 1);
177 conn->addr = g_strdup(addr);
178 conn->port = port;
179 return conn;
182 void nm_release_conn(NMConn *conn)
184 if (conn) {
185 g_slist_free_full(conn->requests, (GDestroyNotify)nm_release_request);
186 conn->requests = NULL;
187 g_free(conn->ssl_conn);
188 conn->ssl_conn = NULL;
189 g_free(conn->addr);
190 conn->addr = NULL;
191 g_free(conn);
196 nm_tcp_write(NMConn * conn, const void *buff, int len)
198 if (conn == NULL || buff == NULL)
199 return -1;
201 if (!conn->use_ssl)
202 return (write(conn->fd, buff, len));
203 else if (conn->ssl_conn && conn->ssl_conn->write)
204 return (conn->ssl_conn->write(conn->ssl_conn->data, buff, len));
205 else
206 return -1;
210 nm_tcp_read(NMConn * conn, void *buff, int len)
212 if (conn == NULL || buff == NULL)
213 return -1;
215 if (!conn->use_ssl)
216 return (read(conn->fd, buff, len));
217 else if (conn->ssl_conn && conn->ssl_conn->read)
218 return ((conn->ssl_conn->read)(conn->ssl_conn->data, buff, len));
219 else
220 return -1;
223 NMERR_T
224 nm_read_all(NMConn * conn, char *buff, int len)
226 NMERR_T rc = NM_OK;
227 int bytes_left = len;
228 int bytes_read;
229 int total_bytes = 0;
230 int retry = 1000;
232 if (conn == NULL || buff == NULL)
233 return NMERR_BAD_PARM;
235 /* Keep reading until buffer is full */
236 while (bytes_left) {
237 bytes_read = nm_tcp_read(conn, &buff[total_bytes], bytes_left);
238 if (bytes_read > 0) {
239 bytes_left -= bytes_read;
240 total_bytes += bytes_read;
241 } else {
242 if (errno == EAGAIN) {
243 if (--retry == 0) {
244 rc = NMERR_TCP_READ;
245 break;
247 #ifdef _WIN32
248 Sleep(1);
249 #else
250 usleep(1000);
251 #endif
252 } else {
253 rc = NMERR_TCP_READ;
254 break;
258 return rc;
261 NMERR_T
262 nm_read_uint32(NMConn *conn, guint32 *val)
264 NMERR_T rc = NM_OK;
266 rc = nm_read_all(conn, (char *)val, sizeof(*val));
267 if (rc == NM_OK) {
268 *val = GUINT32_FROM_LE(*val);
271 return rc;
274 NMERR_T
275 nm_read_uint16(NMConn *conn, guint16 *val)
277 NMERR_T rc = NM_OK;
279 rc = nm_read_all(conn, (char *)val, sizeof(*val));
280 if (rc == NM_OK) {
281 *val = GUINT16_FROM_LE(*val);
284 return rc;
287 NMERR_T
288 nm_write_fields(NMConn * conn, NMField * fields)
290 NMERR_T rc = NM_OK;
291 NMField *field;
292 char *value = NULL;
293 char *method = NULL;
294 char buffer[4096];
295 int ret;
296 int bytes_to_send;
297 int val = 0;
299 if (conn == NULL || fields == NULL) {
300 return NMERR_BAD_PARM;
303 /* Format each field as valid "post" data and write it out */
304 for (field = fields; (rc == NM_OK) && (field->tag); field++) {
306 /* We don't currently handle binary types */
307 if (field->method == NMFIELD_METHOD_IGNORE ||
308 field->type == NMFIELD_TYPE_BINARY) {
309 continue;
312 /* Write the field tag */
313 bytes_to_send = g_snprintf(buffer, sizeof(buffer), "&tag=%s", field->tag);
314 ret = nm_tcp_write(conn, buffer, bytes_to_send);
315 if (ret < 0) {
316 rc = NMERR_TCP_WRITE;
319 /* Write the field method */
320 if (rc == NM_OK) {
321 method = encode_method(field->method);
322 bytes_to_send = g_snprintf(buffer, sizeof(buffer), "&cmd=%s", method);
323 ret = nm_tcp_write(conn, buffer, bytes_to_send);
324 if (ret < 0) {
325 rc = NMERR_TCP_WRITE;
329 /* Write the field value */
330 if (rc == NM_OK) {
331 switch (field->type) {
332 case NMFIELD_TYPE_UTF8:
333 case NMFIELD_TYPE_DN:
335 value = url_escape_string((char *) field->ptr_value);
336 bytes_to_send = g_snprintf(buffer, sizeof(buffer),
337 "&val=%s", value);
338 if (bytes_to_send > (int)sizeof(buffer)) {
339 ret = nm_tcp_write(conn, buffer, sizeof(buffer));
340 } else {
341 ret = nm_tcp_write(conn, buffer, bytes_to_send);
344 if (ret < 0) {
345 rc = NMERR_TCP_WRITE;
348 g_free(value);
350 break;
352 case NMFIELD_TYPE_ARRAY:
353 case NMFIELD_TYPE_MV:
355 val = nm_count_fields((NMField *) field->ptr_value);
356 bytes_to_send = g_snprintf(buffer, sizeof(buffer),
357 "&val=%u", val);
358 ret = nm_tcp_write(conn, buffer, bytes_to_send);
359 if (ret < 0) {
360 rc = NMERR_TCP_WRITE;
363 break;
365 default:
367 bytes_to_send = g_snprintf(buffer, sizeof(buffer),
368 "&val=%u", field->value);
369 ret = nm_tcp_write(conn, buffer, bytes_to_send);
370 if (ret < 0) {
371 rc = NMERR_TCP_WRITE;
374 break;
378 /* Write the field type */
379 if (rc == NM_OK) {
380 bytes_to_send = g_snprintf(buffer, sizeof(buffer),
381 "&type=%u", field->type);
382 ret = nm_tcp_write(conn, buffer, bytes_to_send);
383 if (ret < 0) {
384 rc = NMERR_TCP_WRITE;
388 /* If the field is a sub array then post its fields */
389 if (rc == NM_OK && val > 0) {
390 if (field->type == NMFIELD_TYPE_ARRAY ||
391 field->type == NMFIELD_TYPE_MV) {
393 rc = nm_write_fields(conn, (NMField *) field->ptr_value);
399 return rc;
402 NMERR_T
403 nm_send_request(NMConn *conn, char *cmd, NMField *fields,
404 nm_response_cb cb, gpointer data, NMRequest **request)
406 NMERR_T rc = NM_OK;
407 char buffer[512];
408 int bytes_to_send;
409 int ret;
410 NMField *request_fields = NULL;
411 char *str = NULL;
413 if (conn == NULL || cmd == NULL)
414 return NMERR_BAD_PARM;
416 /* Write the post */
417 bytes_to_send = g_snprintf(buffer, sizeof(buffer),
418 "POST /%s HTTP/1.0\r\n", cmd);
419 ret = nm_tcp_write(conn, buffer, bytes_to_send);
420 if (ret < 0) {
421 rc = NMERR_TCP_WRITE;
424 /* Write headers */
425 if (rc == NM_OK) {
426 if (purple_strequal("login", cmd)) {
427 bytes_to_send = g_snprintf(buffer, sizeof(buffer),
428 "Host: %s:%d\r\n\r\n", conn->addr, conn->port);
429 ret = nm_tcp_write(conn, buffer, bytes_to_send);
430 if (ret < 0) {
431 rc = NMERR_TCP_WRITE;
433 } else {
434 bytes_to_send = g_snprintf(buffer, sizeof(buffer), "\r\n");
435 ret = nm_tcp_write(conn, buffer, bytes_to_send);
436 if (ret < 0) {
437 rc = NMERR_TCP_WRITE;
442 /* Add the transaction id to the request fields */
443 if (rc == NM_OK) {
444 if (fields)
445 request_fields = nm_copy_field_array(fields);
447 str = g_strdup_printf("%d", ++(conn->trans_id));
448 request_fields = nm_field_add_pointer(request_fields, NM_A_SZ_TRANSACTION_ID, 0,
449 NMFIELD_METHOD_VALID, 0,
450 str, NMFIELD_TYPE_UTF8);
453 /* Send the request to the server */
454 if (rc == NM_OK) {
455 rc = nm_write_fields(conn, request_fields);
458 /* Write the CRLF to terminate the data */
459 if (rc == NM_OK) {
460 ret = nm_tcp_write(conn, "\r\n", strlen("\r\n"));
461 if (ret < 0) {
462 rc = NMERR_TCP_WRITE;
466 /* Create a request struct, add it to our queue, and return it */
467 if (rc == NM_OK) {
468 NMRequest *new_request = nm_create_request(cmd, conn->trans_id,
469 time(0), cb, NULL, data);
470 nm_conn_add_request_item(conn, new_request);
472 /* Set the out param if it was sent in, otherwise release the request */
473 if (request)
474 *request = new_request;
475 else
476 nm_release_request(new_request);
479 if (request_fields != NULL)
480 nm_free_fields(&request_fields);
482 return rc;
485 NMERR_T
486 nm_read_header(NMConn * conn)
488 NMERR_T rc = NM_OK;
489 char buffer[512];
490 char *ptr = NULL;
491 int i;
492 char rtn_buf[8];
493 int rtn_code = 0;
495 if (conn == NULL)
496 return NMERR_BAD_PARM;
498 *buffer = '\0';
499 rc = read_line(conn, buffer, sizeof(buffer));
500 if (rc == NM_OK) {
502 /* Find the return code */
503 ptr = strchr(buffer, ' ');
504 if (ptr != NULL) {
505 ptr++;
507 i = 0;
508 while (isdigit(*ptr) && (i < 3)) {
509 rtn_buf[i] = *ptr;
510 i++;
511 ptr++;
513 rtn_buf[i] = '\0';
515 if (i > 0)
516 rtn_code = atoi(rtn_buf);
520 /* Finish reading header, in the future we might want to do more processing here */
521 /* TODO: handle more general redirects in the future */
522 while ((rc == NM_OK) && (!purple_strequal(buffer, "\r\n"))) {
523 rc = read_line(conn, buffer, sizeof(buffer));
526 if (rc == NM_OK && rtn_code == 301)
527 rc = NMERR_SERVER_REDIRECT;
529 return rc;
532 NMERR_T
533 nm_read_fields(NMConn * conn, int count, NMField ** fields)
535 NMERR_T rc = NM_OK;
536 guint8 type;
537 guint8 method;
538 guint32 val;
539 char tag[64];
540 NMField *sub_fields = NULL;
541 char *str = NULL;
543 if (conn == NULL || fields == NULL)
544 return NMERR_BAD_PARM;
546 do {
547 if (count > 0) {
548 count--;
551 /* Read the field type, method, and tag */
552 rc = nm_read_all(conn, (char *)&type, sizeof(type));
553 if (rc != NM_OK || type == 0)
554 break;
556 rc = nm_read_all(conn, (char *)&method, sizeof(method));
557 if (rc != NM_OK)
558 break;
560 rc = nm_read_uint32(conn, &val);
561 if (rc != NM_OK)
562 break;
564 if (val > sizeof(tag)) {
565 rc = NMERR_PROTOCOL;
566 break;
569 rc = nm_read_all(conn, tag, val);
570 if (rc != NM_OK)
571 break;
573 if (type == NMFIELD_TYPE_MV || type == NMFIELD_TYPE_ARRAY) {
575 /* Read the subarray (first read the number of items in the array) */
576 rc = nm_read_uint32(conn, &val);
577 if (rc != NM_OK)
578 break;
580 if (val > 0) {
581 rc = nm_read_fields(conn, val, &sub_fields);
582 if (rc != NM_OK)
583 break;
586 *fields = nm_field_add_pointer(*fields, tag, 0, method,
587 0, sub_fields, type);
589 sub_fields = NULL;
591 } else if (type == NMFIELD_TYPE_UTF8 || type == NMFIELD_TYPE_DN) {
593 /* Read the string (first read the length) */
594 rc = nm_read_uint32(conn, &val);
595 if (rc != NM_OK)
596 break;
598 if (val >= NMFIELD_MAX_STR_LENGTH) {
599 rc = NMERR_PROTOCOL;
600 break;
603 if (val > 0) {
604 str = g_new0(char, val + 1);
606 rc = nm_read_all(conn, str, val);
607 if (rc != NM_OK)
608 break;
610 *fields = nm_field_add_pointer(*fields, tag, 0, method,
611 0, str, type);
612 str = NULL;
615 } else {
617 /* Read the numerical value */
618 rc = nm_read_uint32(conn, &val);
619 if (rc != NM_OK)
620 break;
622 *fields = nm_field_add_number(*fields, tag, 0, method,
623 0, val, type);
626 } while (count != 0);
628 g_free(str);
630 if (sub_fields != NULL) {
631 nm_free_fields(&sub_fields);
634 return rc;
637 void
638 nm_conn_add_request_item(NMConn * conn, NMRequest * request)
640 if (conn == NULL || request == NULL)
641 return;
643 nm_request_add_ref(request);
644 conn->requests = g_slist_append(conn->requests, request);
647 void
648 nm_conn_remove_request_item(NMConn * conn, NMRequest * request)
650 if (conn == NULL || request == NULL)
651 return;
653 conn->requests = g_slist_remove(conn->requests, request);
654 nm_release_request(request);
657 NMRequest *
658 nm_conn_find_request(NMConn * conn, int trans_id)
660 NMRequest *req = NULL;
661 GSList *itr = NULL;
663 if (conn == NULL)
664 return NULL;
666 itr = conn->requests;
667 while (itr) {
668 req = (NMRequest *) itr->data;
669 if (req != NULL && nm_request_get_trans_id(req) == trans_id) {
670 return req;
672 itr = g_slist_next(itr);
674 return NULL;
677 const char *
678 nm_conn_get_addr(NMConn * conn)
680 if (conn == NULL)
681 return NULL;
682 else
683 return conn->addr;
687 nm_conn_get_port(NMConn * conn)
689 if (conn == NULL)
690 return -1;
691 else
692 return conn->port;