comparison libpurple/protocols/novell/nmconn.c @ 15373: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
comparison
equal deleted inserted replaced
15372:f79e0f4df793 15373:5fe8042783c1
1 /*
2 * nmconn.c
3 *
4 * Copyright (c) 2004 Novell, Inc. All Rights Reserved.
5 *
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.
9 *
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.
14 *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
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"
28
29 #ifdef _WIN32
30 #include <windows.h>
31 #endif
32
33 #define NO_ESCAPE(ch) ((ch == 0x20) || (ch >= 0x30 && ch <= 0x39) || \
34 (ch >= 0x41 && ch <= 0x5a) || (ch >= 0x61 && ch <= 0x7a))
35
36 /* Read data from conn until the end of a line */
37 static NMERR_T
38 read_line(NMConn * conn, char *buff, int len)
39 {
40 NMERR_T rc = NM_OK;
41 int total_bytes = 0;
42
43 while ((rc == NM_OK) && (total_bytes < (len - 1))) {
44 rc = nm_read_all(conn, &buff[total_bytes], 1);
45 if (rc == NM_OK) {
46 total_bytes += 1;
47 if (buff[total_bytes - 1] == '\n') {
48 break;
49 }
50 }
51 }
52 buff[total_bytes] = '\0';
53
54 return rc;
55 }
56
57 static char *
58 url_escape_string(char *src)
59 {
60 guint32 escape = 0;
61 char *p;
62 char *q;
63 char *encoded = NULL;
64 int ch;
65
66 static const char hex_table[16] = "0123456789abcdef";
67
68 if (src == NULL) {
69 return NULL;
70 }
71
72 /* Find number of chars to escape */
73 for (p = src; *p != '\0'; p++) {
74 ch = (guchar) *p;
75 if (!NO_ESCAPE(ch)) {
76 escape++;
77 }
78 }
79
80 encoded = g_malloc((p - src) + (escape * 2) + 1);
81
82 /* Escape the string */
83 for (p = src, q = encoded; *p != '\0'; p++) {
84 ch = (guchar) * p;
85 if (NO_ESCAPE(ch)) {
86 if (ch != 0x20) {
87 *q = ch;
88 q++;
89 } else {
90 *q = '+';
91 q++;
92 }
93 } else {
94 *q = '%';
95 q++;
96
97 *q = hex_table[ch >> 4];
98 q++;
99
100 *q = hex_table[ch & 15];
101 q++;
102 }
103 }
104 *q = '\0';
105
106 return encoded;
107 }
108
109 static char *
110 encode_method(guint8 method)
111 {
112 char *str;
113
114 switch (method) {
115 case NMFIELD_METHOD_EQUAL:
116 str = "G";
117 break;
118 case NMFIELD_METHOD_UPDATE:
119 str = "F";
120 break;
121 case NMFIELD_METHOD_GTE:
122 str = "E";
123 break;
124 case NMFIELD_METHOD_LTE:
125 str = "D";
126 break;
127 case NMFIELD_METHOD_NE:
128 str = "C";
129 break;
130 case NMFIELD_METHOD_EXIST:
131 str = "B";
132 break;
133 case NMFIELD_METHOD_NOTEXIST:
134 str = "A";
135 break;
136 case NMFIELD_METHOD_SEARCH:
137 str = "9";
138 break;
139 case NMFIELD_METHOD_MATCHBEGIN:
140 str = "8";
141 break;
142 case NMFIELD_METHOD_MATCHEND:
143 str = "7";
144 break;
145 case NMFIELD_METHOD_NOT_ARRAY:
146 str = "6";
147 break;
148 case NMFIELD_METHOD_OR_ARRAY:
149 str = "5";
150 break;
151 case NMFIELD_METHOD_AND_ARRAY:
152 str = "4";
153 break;
154 case NMFIELD_METHOD_DELETE_ALL:
155 str = "3";
156 break;
157 case NMFIELD_METHOD_DELETE:
158 str = "2";
159 break;
160 case NMFIELD_METHOD_ADD:
161 str = "1";
162 break;
163 default: /* NMFIELD_METHOD_VALID */
164 str = "0";
165 break;
166 }
167
168 return str;
169 }
170
171 NMConn *
172 nm_create_conn(const char *addr, int port)
173 {
174 NMConn *conn = g_new0(NMConn, 1);
175 conn->addr = g_strdup(addr);
176 conn->port = port;
177 return conn;
178 }
179
180 void nm_release_conn(NMConn *conn)
181 {
182 if (conn) {
183 GSList *node;
184 for (node = conn->requests; node; node = node->next) {
185 if (node->data)
186 nm_release_request(node->data);
187 }
188 g_slist_free(conn->requests);
189 conn->requests = NULL;
190 if (conn->ssl_conn) {
191 g_free(conn->ssl_conn);
192 conn->ssl_conn = NULL;
193 }
194 g_free(conn->addr);
195 conn->addr = NULL;
196 g_free(conn);
197 }
198 }
199
200 int
201 nm_tcp_write(NMConn * conn, const void *buff, int len)
202 {
203 if (conn == NULL || buff == NULL)
204 return -1;
205
206 if (!conn->use_ssl)
207 return (write(conn->fd, buff, len));
208 else if (conn->ssl_conn && conn->ssl_conn->write)
209 return (conn->ssl_conn->write(conn->ssl_conn->data, buff, len));
210 else
211 return -1;
212 }
213
214 int
215 nm_tcp_read(NMConn * conn, void *buff, int len)
216 {
217 if (conn == NULL || buff == NULL)
218 return -1;
219
220 if (!conn->use_ssl)
221 return (read(conn->fd, buff, len));
222 else if (conn->ssl_conn && conn->ssl_conn->read)
223 return ((conn->ssl_conn->read)(conn->ssl_conn->data, buff, len));
224 else
225 return -1;
226 }
227
228 NMERR_T
229 nm_read_all(NMConn * conn, char *buff, int len)
230 {
231 NMERR_T rc = NM_OK;
232 int bytes_left = len;
233 int bytes_read;
234 int total_bytes = 0;
235 int retry = 1000;
236
237 if (conn == NULL || buff == NULL)
238 return NMERR_BAD_PARM;
239
240 /* Keep reading until buffer is full */
241 while (bytes_left) {
242 bytes_read = nm_tcp_read(conn, &buff[total_bytes], bytes_left);
243 if (bytes_read > 0) {
244 bytes_left -= bytes_read;
245 total_bytes += bytes_read;
246 } else {
247 if (errno == EAGAIN) {
248 if (--retry == 0) {
249 rc = NMERR_TCP_READ;
250 break;
251 }
252 #ifdef _WIN32
253 Sleep(1);
254 #else
255 usleep(1000);
256 #endif
257 } else {
258 rc = NMERR_TCP_READ;
259 break;
260 }
261 }
262 }
263 return rc;
264 }
265
266 NMERR_T
267 nm_read_uint32(NMConn *conn, guint32 *val)
268 {
269 NMERR_T rc = NM_OK;
270
271 rc = nm_read_all(conn, (char *)val, sizeof(*val));
272 if (rc == NM_OK) {
273 *val = GUINT32_FROM_LE(*val);
274 }
275
276 return rc;
277 }
278
279 NMERR_T
280 nm_read_uint16(NMConn *conn, guint16 *val)
281 {
282 NMERR_T rc = NM_OK;
283
284 rc = nm_read_all(conn, (char *)val, sizeof(*val));
285 if (rc == NM_OK) {
286 *val = GUINT16_FROM_LE(*val);
287 }
288
289 return rc;
290 }
291
292 NMERR_T
293 nm_write_fields(NMConn * conn, NMField * fields)
294 {
295 NMERR_T rc = NM_OK;
296 NMField *field;
297 char *value = NULL;
298 char *method = NULL;
299 char buffer[4096];
300 int ret;
301 int bytes_to_send;
302 int val = 0;
303
304 if (conn == NULL || fields == NULL) {
305 return NMERR_BAD_PARM;
306 }
307
308 /* Format each field as valid "post" data and write it out */
309 for (field = fields; (rc == NM_OK) && (field->tag); field++) {
310
311 /* We don't currently handle binary types */
312 if (field->method == NMFIELD_METHOD_IGNORE ||
313 field->type == NMFIELD_TYPE_BINARY) {
314 continue;
315 }
316
317 /* Write the field tag */
318 bytes_to_send = g_snprintf(buffer, sizeof(buffer), "&tag=%s", field->tag);
319 ret = nm_tcp_write(conn, buffer, bytes_to_send);
320 if (ret < 0) {
321 rc = NMERR_TCP_WRITE;
322 }
323
324 /* Write the field method */
325 if (rc == NM_OK) {
326 method = encode_method(field->method);
327 bytes_to_send = g_snprintf(buffer, sizeof(buffer), "&cmd=%s", method);
328 ret = nm_tcp_write(conn, buffer, bytes_to_send);
329 if (ret < 0) {
330 rc = NMERR_TCP_WRITE;
331 }
332 }
333
334 /* Write the field value */
335 if (rc == NM_OK) {
336 switch (field->type) {
337 case NMFIELD_TYPE_UTF8:
338 case NMFIELD_TYPE_DN:
339
340 value = url_escape_string((char *) field->ptr_value);
341 bytes_to_send = g_snprintf(buffer, sizeof(buffer),
342 "&val=%s", value);
343 if (bytes_to_send > (int)sizeof(buffer)) {
344 ret = nm_tcp_write(conn, buffer, sizeof(buffer));
345 } else {
346 ret = nm_tcp_write(conn, buffer, bytes_to_send);
347 }
348
349 if (ret < 0) {
350 rc = NMERR_TCP_WRITE;
351 }
352
353 g_free(value);
354
355 break;
356
357 case NMFIELD_TYPE_ARRAY:
358 case NMFIELD_TYPE_MV:
359
360 val = nm_count_fields((NMField *) field->ptr_value);
361 bytes_to_send = g_snprintf(buffer, sizeof(buffer),
362 "&val=%u", val);
363 ret = nm_tcp_write(conn, buffer, bytes_to_send);
364 if (ret < 0) {
365 rc = NMERR_TCP_WRITE;
366 }
367
368 break;
369
370 default:
371
372 bytes_to_send = g_snprintf(buffer, sizeof(buffer),
373 "&val=%u", field->value);
374 ret = nm_tcp_write(conn, buffer, bytes_to_send);
375 if (ret < 0) {
376 rc = NMERR_TCP_WRITE;
377 }
378
379 break;
380 }
381 }
382
383 /* Write the field type */
384 if (rc == NM_OK) {
385 bytes_to_send = g_snprintf(buffer, sizeof(buffer),
386 "&type=%u", field->type);
387 ret = nm_tcp_write(conn, buffer, bytes_to_send);
388 if (ret < 0) {
389 rc = NMERR_TCP_WRITE;
390 }
391 }
392
393 /* If the field is a sub array then post its fields */
394 if (rc == NM_OK && val > 0) {
395 if (field->type == NMFIELD_TYPE_ARRAY ||
396 field->type == NMFIELD_TYPE_MV) {
397
398 rc = nm_write_fields(conn, (NMField *) field->ptr_value);
399
400 }
401 }
402 }
403
404 return rc;
405 }
406
407 NMERR_T
408 nm_send_request(NMConn *conn, char *cmd, NMField *fields,
409 nm_response_cb cb, gpointer data, NMRequest **request)
410 {
411 NMERR_T rc = NM_OK;
412 char buffer[512];
413 int bytes_to_send;
414 int ret;
415 NMField *request_fields = NULL;
416 char *str = NULL;
417
418 if (conn == NULL || cmd == NULL)
419 return NMERR_BAD_PARM;
420
421 /* Write the post */
422 bytes_to_send = g_snprintf(buffer, sizeof(buffer),
423 "POST /%s HTTP/1.0\r\n", cmd);
424 ret = nm_tcp_write(conn, buffer, bytes_to_send);
425 if (ret < 0) {
426 rc = NMERR_TCP_WRITE;
427 }
428
429 /* Write headers */
430 if (rc == NM_OK) {
431 if (strcmp("login", cmd) == 0) {
432 bytes_to_send = g_snprintf(buffer, sizeof(buffer),
433 "Host: %s:%d\r\n\r\n", conn->addr, conn->port);
434 ret = nm_tcp_write(conn, buffer, bytes_to_send);
435 if (ret < 0) {
436 rc = NMERR_TCP_WRITE;
437 }
438 } else {
439 bytes_to_send = g_snprintf(buffer, sizeof(buffer), "\r\n");
440 ret = nm_tcp_write(conn, buffer, bytes_to_send);
441 if (ret < 0) {
442 rc = NMERR_TCP_WRITE;
443 }
444 }
445 }
446
447 /* Add the transaction id to the request fields */
448 if (rc == NM_OK) {
449 if (fields)
450 request_fields = nm_copy_field_array(fields);
451
452 str = g_strdup_printf("%d", ++(conn->trans_id));
453 request_fields = nm_field_add_pointer(request_fields, NM_A_SZ_TRANSACTION_ID, 0,
454 NMFIELD_METHOD_VALID, 0,
455 str, NMFIELD_TYPE_UTF8);
456 }
457
458 /* Send the request to the server */
459 if (rc == NM_OK) {
460 rc = nm_write_fields(conn, request_fields);
461 }
462
463 /* Write the CRLF to terminate the data */
464 if (rc == NM_OK) {
465 ret = nm_tcp_write(conn, "\r\n", strlen("\r\n"));
466 if (ret < 0) {
467 rc = NMERR_TCP_WRITE;
468 }
469 }
470
471 /* Create a request struct, add it to our queue, and return it */
472 if (rc == NM_OK) {
473 NMRequest *new_request = nm_create_request(cmd, conn->trans_id,
474 time(0), cb, NULL, data);
475 nm_conn_add_request_item(conn, new_request);
476
477 /* Set the out param if it was sent in, otherwise release the request */
478 if (request)
479 *request = new_request;
480 else
481 nm_release_request(new_request);
482 }
483
484 if (request_fields != NULL)
485 nm_free_fields(&request_fields);
486
487 return rc;
488 }
489
490 NMERR_T
491 nm_read_header(NMConn * conn)
492 {
493 NMERR_T rc = NM_OK;
494 char buffer[512];
495 char *ptr = NULL;
496 int i;
497 char rtn_buf[8];
498 int rtn_code = 0;
499
500 if (conn == NULL)
501 return NMERR_BAD_PARM;
502
503 *buffer = '\0';
504 rc = read_line(conn, buffer, sizeof(buffer));
505 if (rc == NM_OK) {
506
507 /* Find the return code */
508 ptr = strchr(buffer, ' ');
509 if (ptr != NULL) {
510 ptr++;
511
512 i = 0;
513 while (isdigit(*ptr) && (i < 3)) {
514 rtn_buf[i] = *ptr;
515 i++;
516 ptr++;
517 }
518 rtn_buf[i] = '\0';
519
520 if (i > 0)
521 rtn_code = atoi(rtn_buf);
522 }
523 }
524
525 /* Finish reading header, in the future we might want to do more processing here */
526 /* TODO: handle more general redirects in the future */
527 while ((rc == NM_OK) && (strcmp(buffer, "\r\n") != 0)) {
528 rc = read_line(conn, buffer, sizeof(buffer));
529 }
530
531 if (rc == NM_OK && rtn_code == 301)
532 rc = NMERR_SERVER_REDIRECT;
533
534 return rc;
535 }
536
537 NMERR_T
538 nm_read_fields(NMConn * conn, int count, NMField ** fields)
539 {
540 NMERR_T rc = NM_OK;
541 guint8 type;
542 guint8 method;
543 guint32 val;
544 char tag[64];
545 NMField *sub_fields = NULL;
546 char *str = NULL;
547
548 if (conn == NULL || fields == NULL)
549 return NMERR_BAD_PARM;
550
551 do {
552 if (count > 0) {
553 count--;
554 }
555
556 /* Read the field type, method, and tag */
557 rc = nm_read_all(conn, (char *)&type, sizeof(type));
558 if (rc != NM_OK || type == 0)
559 break;
560
561 rc = nm_read_all(conn, (char *)&method, sizeof(method));
562 if (rc != NM_OK)
563 break;
564
565 rc = nm_read_uint32(conn, &val);
566 if (rc != NM_OK)
567 break;
568
569 if (val > sizeof(tag)) {
570 rc = NMERR_PROTOCOL;
571 break;
572 }
573
574 rc = nm_read_all(conn, tag, val);
575 if (rc != NM_OK)
576 break;
577
578 if (type == NMFIELD_TYPE_MV || type == NMFIELD_TYPE_ARRAY) {
579
580 /* Read the subarray (first read the number of items in the array) */
581 rc = nm_read_uint32(conn, &val);
582 if (rc != NM_OK)
583 break;
584
585 if (val > 0) {
586 rc = nm_read_fields(conn, val, &sub_fields);
587 if (rc != NM_OK)
588 break;
589 }
590
591 *fields = nm_field_add_pointer(*fields, tag, 0, method,
592 0, sub_fields, type);
593
594 sub_fields = NULL;
595
596 } else if (type == NMFIELD_TYPE_UTF8 || type == NMFIELD_TYPE_DN) {
597
598 /* Read the string (first read the length) */
599 rc = nm_read_uint32(conn, &val);
600 if (rc != NM_OK)
601 break;
602
603 if (val >= NMFIELD_MAX_STR_LENGTH) {
604 rc = NMERR_PROTOCOL;
605 break;
606 }
607
608 if (val > 0) {
609 str = g_new0(char, val + 1);
610
611 rc = nm_read_all(conn, str, val);
612 if (rc != NM_OK)
613 break;
614
615 *fields = nm_field_add_pointer(*fields, tag, 0, method,
616 0, str, type);
617 str = NULL;
618 }
619
620 } else {
621
622 /* Read the numerical value */
623 rc = nm_read_uint32(conn, &val);
624 if (rc != NM_OK)
625 break;
626
627 *fields = nm_field_add_number(*fields, tag, 0, method,
628 0, val, type);
629 }
630
631 } while ((type != 0) && (count != 0));
632
633
634 if (str != NULL) {
635 g_free(str);
636 }
637
638 if (sub_fields != NULL) {
639 nm_free_fields(&sub_fields);
640 }
641
642 return rc;
643 }
644
645 void
646 nm_conn_add_request_item(NMConn * conn, NMRequest * request)
647 {
648 if (conn == NULL || request == NULL)
649 return;
650
651 nm_request_add_ref(request);
652 conn->requests = g_slist_append(conn->requests, request);
653 }
654
655 void
656 nm_conn_remove_request_item(NMConn * conn, NMRequest * request)
657 {
658 if (conn == NULL || request == NULL)
659 return;
660
661 conn->requests = g_slist_remove(conn->requests, request);
662 nm_release_request(request);
663 }
664
665 NMRequest *
666 nm_conn_find_request(NMConn * conn, int trans_id)
667 {
668 NMRequest *req = NULL;
669 GSList *itr = NULL;
670
671 if (conn == NULL)
672 return NULL;
673
674 itr = conn->requests;
675 while (itr) {
676 req = (NMRequest *) itr->data;
677 if (req != NULL && nm_request_get_trans_id(req) == trans_id) {
678 return req;
679 }
680 itr = g_slist_next(itr);
681 }
682 return NULL;
683 }
684
685 const char *
686 nm_conn_get_addr(NMConn * conn)
687 {
688 if (conn == NULL)
689 return NULL;
690 else
691 return conn->addr;
692 }
693
694 int
695 nm_conn_get_port(NMConn * conn)
696 {
697 if (conn == NULL)
698 return -1;
699 else
700 return conn->port;
701 }