Mercurial > pidgin
view libpurple/protocols/gg/lib/common.c @ 24653:fecedf6d9ee1
propagate from branch 'im.pidgin.pidgin' (head 97d295a04f3a9852a3b29cf0df43d56d1ddcc672)
to branch 'im.pidgin.pidgin.openq' (head a0d99396d1603b53838d74cad4391ece34f3ebbf)
author | SHiNE CsyFeK <csyfek@gmail.com> |
---|---|
date | Fri, 05 Dec 2008 14:51:36 +0000 |
parents | 44b4e8bd759b |
children | 259bbfb423d4 |
line wrap: on
line source
/* $Id: common.c 16856 2006-08-19 01:13:25Z evands $ */ /* * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> * Robert J. Woźny <speedy@ziew.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version * 2.1 as published by the Free Software Foundation. * * 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, * USA. */ #ifndef _WIN32 #include <sys/types.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #ifdef sun # include <sys/filio.h> #endif #endif #include <errno.h> #include <fcntl.h> #ifndef _WIN32 #include <netdb.h> #endif #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "libgadu.h" FILE *gg_debug_file = NULL; #ifndef GG_DEBUG_DISABLE /* * gg_debug() // funkcja wewnętrzna * * wyświetla komunikat o danym poziomie, o ile użytkownik sobie tego życzy. * * - level - poziom wiadomości * - format... - treść wiadomości (kompatybilna z printf()) */ void gg_debug(int level, const char *format, ...) { va_list ap; int old_errno = errno; if (gg_debug_handler) { va_start(ap, format); (*gg_debug_handler)(level, format, ap); va_end(ap); goto cleanup; } if ((gg_debug_level & level)) { va_start(ap, format); vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap); va_end(ap); } cleanup: errno = old_errno; } #endif /* * gg_vsaprintf() // funkcja pomocnicza * * robi dokładnie to samo, co vsprintf(), tyle że alokuje sobie wcześniej * miejsce na dane. powinno działać na tych maszynach, które mają funkcję * vsnprintf() zgodną z C99, jak i na wcześniejszych. * * - format - opis wyświetlanego tekstu jak dla printf() * - ap - lista argumentów dla printf() * * zaalokowany bufor, który należy później zwolnić, lub NULL * jeśli nie udało się wykonać zadania. */ char *gg_vsaprintf(const char *format, va_list ap) { int size = 0; const char *start; char *buf = NULL; #ifdef __GG_LIBGADU_HAVE_VA_COPY va_list aq; va_copy(aq, ap); #else # ifdef __GG_LIBGADU_HAVE___VA_COPY va_list aq; __va_copy(aq, ap); # endif #endif start = format; #ifndef __GG_LIBGADU_HAVE_C99_VSNPRINTF { int res; char *tmp; size = 128; do { size *= 2; if (!(tmp = realloc(buf, size))) { free(buf); return NULL; } buf = tmp; res = vsnprintf(buf, size, format, ap); } while (res == size - 1 || res == -1); } #else { char tmp[2]; /* libce Solarisa przy buforze NULL zawsze zwracają -1, więc * musimy podać coś istniejącego jako cel printf()owania. */ size = vsnprintf(tmp, sizeof(tmp), format, ap); if (!(buf = malloc(size + 1))) return NULL; } #endif format = start; #ifdef __GG_LIBGADU_HAVE_VA_COPY vsnprintf(buf, size + 1, format, aq); va_end(aq); #else # ifdef __GG_LIBGADU_HAVE___VA_COPY vsnprintf(buf, size + 1, format, aq); va_end(aq); # else vsnprintf(buf, size + 1, format, ap); # endif #endif return buf; } /* * gg_saprintf() // funkcja pomocnicza * * robi dokładnie to samo, co sprintf(), tyle że alokuje sobie wcześniej * miejsce na dane. powinno działać na tych maszynach, które mają funkcję * vsnprintf() zgodną z C99, jak i na wcześniejszych. * * - format... - treść taka sama jak w funkcji printf() * * zaalokowany bufor, który należy później zwolnić, lub NULL * jeśli nie udało się wykonać zadania. */ char *gg_saprintf(const char *format, ...) { va_list ap; char *res; va_start(ap, format); res = gg_vsaprintf(format, ap); va_end(ap); return res; } /* * gg_get_line() // funkcja pomocnicza * * podaje kolejną linię z bufora tekstowego. niszczy go bezpowrotnie, dzieląc * na kolejne stringi. zdarza się, nie ma potrzeby pisania funkcji dublującej * bufor żeby tylko mieć nieruszone dane wejściowe, skoro i tak nie będą nam * poźniej potrzebne. obcina `\r\n'. * * - ptr - wskaźnik do zmiennej, która przechowuje aktualną pozycję * w przemiatanym buforze * * wskaźnik do kolejnej linii tekstu lub NULL, jeśli to już koniec bufora. */ char *gg_get_line(char **ptr) { char *foo, *res; if (!ptr || !*ptr || !strcmp(*ptr, "")) return NULL; res = *ptr; if (!(foo = strchr(*ptr, '\n'))) *ptr += strlen(*ptr); else { *ptr = foo + 1; *foo = 0; if (strlen(res) > 1 && res[strlen(res) - 1] == '\r') res[strlen(res) - 1] = 0; } return res; } /* * gg_connect() // funkcja pomocnicza * * łączy się z serwerem. pierwszy argument jest typu (void *), żeby nie * musieć niczego inkludować w libgadu.h i nie psuć jakiś głupich zależności * na dziwnych systemach. * * - addr - adres serwera (struct in_addr *) * - port - port serwera * - async - asynchroniczne połączenie * * deskryptor gniazda lub -1 w przypadku błędu (kod błędu w zmiennej errno). */ int gg_connect(void *addr, int port, int async) { int sock, one = 1, errno2; struct sockaddr_in sin; struct in_addr *a = addr; struct sockaddr_in myaddr; gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async); if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno)); return -1; } memset(&myaddr, 0, sizeof(myaddr)); myaddr.sin_family = AF_INET; myaddr.sin_addr.s_addr = gg_local_ip; if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno)); return -1; } #ifdef ASSIGN_SOCKETS_TO_THREADS gg_win32_thread_socket(0, sock); #endif if (async) { #ifdef FIONBIO if (ioctl(sock, FIONBIO, &one) == -1) { #else if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { #endif gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno)); errno2 = errno; close(sock); errno = errno2; return -1; } } sin.sin_port = htons(port); sin.sin_family = AF_INET; sin.sin_addr.s_addr = a->s_addr; if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) { if (errno && (!async || errno != EINPROGRESS)) { gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno)); errno2 = errno; close(sock); errno = errno2; return -1; } gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n"); } return sock; } /* * gg_read_line() // funkcja pomocnicza * * czyta jedną linię tekstu z gniazda. * * - sock - deskryptor gniazda * - buf - wskaźnik do bufora * - length - długość bufora * * jeśli trafi na błąd odczytu lub podano nieprawidłowe parametry, zwraca NULL. * inaczej zwraca buf. */ char *gg_read_line(int sock, char *buf, int length) { int ret; if (!buf || length < 0) return NULL; for (; length > 1; buf++, length--) { do { if ((ret = read(sock, buf, 1)) == -1 && errno != EINTR) { gg_debug(GG_DEBUG_MISC, "// gg_read_line() error on read (errno=%d, %s)\n", errno, strerror(errno)); *buf = 0; return NULL; } else if (ret == 0) { gg_debug(GG_DEBUG_MISC, "// gg_read_line() eof reached\n"); *buf = 0; return NULL; } } while (ret == -1 && errno == EINTR); if (*buf == '\n') { buf++; break; } } *buf = 0; return buf; } /* * gg_chomp() // funkcja pomocnicza * * ucina "\r\n" lub "\n" z końca linii. * * - line - linia do przycięcia */ void gg_chomp(char *line) { int len; if (!line) return; len = strlen(line); if (len > 0 && line[len - 1] == '\n') line[--len] = 0; if (len > 0 && line[len - 1] == '\r') line[--len] = 0; } /* * gg_urlencode() // funkcja wewnętrzna * * zamienia podany tekst na ciąg znaków do formularza http. przydaje się * przy różnych usługach katalogu publicznego. * * - str - ciąg znaków do zakodowania * * zaalokowany bufor, który należy później zwolnić albo NULL * w przypadku błędu. */ char *gg_urlencode(const char *str) { char *q, *buf, hex[] = "0123456789abcdef"; const char *p; unsigned int size = 0; if (!str) str = ""; for (p = str; *p; p++, size++) { if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == ' ') || (*p == '@') || (*p == '.') || (*p == '-')) size += 2; } if (!(buf = malloc(size + 1))) return NULL; for (p = str, q = buf; *p; p++, q++) { if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || (*p == '@') || (*p == '.') || (*p == '-')) *q = *p; else { if (*p == ' ') *q = '+'; else { *q++ = '%'; *q++ = hex[*p >> 4 & 15]; *q = hex[*p & 15]; } } } *q = 0; return buf; } /* * gg_http_hash() // funkcja wewnętrzna * * funkcja licząca hash dla adresu e-mail, hasła i paru innych. * * - format... - format kolejnych parametrów ('s' jeśli dany parametr jest * ciągiem znaków lub 'u' jeśli numerem GG) * * hash wykorzystywany przy rejestracji i wszelkich manipulacjach własnego * wpisu w katalogu publicznym. */ int gg_http_hash(const char *format, ...) { unsigned int a, c, i, j; va_list ap; int b = -1; va_start(ap, format); for (j = 0; j < strlen(format); j++) { char *arg, buf[16]; if (format[j] == 'u') { snprintf(buf, sizeof(buf), "%d", va_arg(ap, uin_t)); arg = buf; } else { if (!(arg = va_arg(ap, char*))) arg = ""; } i = 0; while ((c = (unsigned char) arg[i++]) != 0) { a = (c ^ b) + (c << 8); b = (a >> 24) | (a << 8); } } va_end(ap); return (b < 0 ? -b : b); } /* * gg_gethostbyname() // funkcja pomocnicza * * odpowiednik gethostbyname() troszczący się o współbieżność, gdy mamy do * dyspozycji funkcję gethostbyname_r(). * * - hostname - nazwa serwera * * zwraca wskaźnik na strukturę in_addr, którą należy zwolnić. */ struct in_addr *gg_gethostbyname(const char *hostname) { struct in_addr *addr = NULL; #ifdef HAVE_GETHOSTBYNAME_R char *tmpbuf = NULL, *buf = NULL; struct hostent *hp = NULL, *hp2 = NULL; int h_errnop, ret; size_t buflen = 1024; int new_errno; new_errno = ENOMEM; if (!(addr = malloc(sizeof(struct in_addr)))) goto cleanup; if (!(hp = calloc(1, sizeof(*hp)))) goto cleanup; if (!(buf = malloc(buflen))) goto cleanup; tmpbuf = buf; while ((ret = gethostbyname_r(hostname, hp, buf, buflen, &hp2, &h_errnop)) == ERANGE) { buflen *= 2; if (!(tmpbuf = realloc(buf, buflen))) break; buf = tmpbuf; } if (ret) new_errno = h_errnop; if (ret || !hp2 || !tmpbuf) goto cleanup; memcpy(addr, hp->h_addr, sizeof(struct in_addr)); free(buf); free(hp); return addr; cleanup: errno = new_errno; if (addr) free(addr); if (hp) free(hp); if (buf) free(buf); return NULL; #else struct hostent *hp; if (!(addr = malloc(sizeof(struct in_addr)))) { goto cleanup; } if (!(hp = gethostbyname(hostname))) goto cleanup; memcpy(addr, hp->h_addr, sizeof(struct in_addr)); return addr; cleanup: if (addr) free(addr); return NULL; #endif } #ifdef ASSIGN_SOCKETS_TO_THREADS typedef struct gg_win32_thread { int id; int socket; struct gg_win32_thread *next; } gg_win32_thread; struct gg_win32_thread *gg_win32_threads = 0; /* * gg_win32_thread_socket() // funkcja pomocnicza, tylko dla win32 * * zwraca deskryptor gniazda, które było ostatnio tworzone dla wątku * o podanym identyfikatorze. * * jeśli na win32 przy połączeniach synchronicznych zapamiętamy w jakim * wątku uruchomiliśmy funkcję, która się z czymkolwiek łączy, to z osobnego * wątku możemy anulować połączenie poprzez gg_win32_thread_socket(watek, -1); * * - thread_id - id wątku. jeśli jest równe 0, brany jest aktualny wątek, * jeśli równe -1, usuwa wpis o podanym sockecie. * - socket - deskryptor gniazda. jeśli równe 0, zwraca deskryptor gniazda * dla podanego wątku, jeśli równe -1, usuwa wpis, jeśli coś * innego, ustawia dla podanego wątku dany numer deskryptora. * * jeśli socket jest równe 0, zwraca deskryptor gniazda dla podanego wątku. */ int gg_win32_thread_socket(int thread_id, int socket) { char close = (thread_id == -1) || socket == -1; gg_win32_thread *wsk = gg_win32_threads; gg_win32_thread **p_wsk = &gg_win32_threads; if (!thread_id) thread_id = GetCurrentThreadId(); while (wsk) { if ((thread_id == -1 && wsk->socket == socket) || wsk->id == thread_id) { if (close) { /* socket zostaje usuniety */ closesocket(wsk->socket); *p_wsk = wsk->next; free(wsk); return 1; } else if (!socket) { /* socket zostaje zwrocony */ return wsk->socket; } else { /* socket zostaje ustawiony */ wsk->socket = socket; return socket; } } p_wsk = &(wsk->next); wsk = wsk->next; } if (close && socket != -1) closesocket(socket); if (close || !socket) return 0; /* Dodaje nowy element */ wsk = malloc(sizeof(gg_win32_thread)); wsk->id = thread_id; wsk->socket = socket; wsk->next = 0; *p_wsk = wsk; return socket; } #endif /* ASSIGN_SOCKETS_TO_THREADS */ static char gg_base64_charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* * gg_base64_encode() * * zapisuje ciąg znaków w base64. * * - buf - ciąg znaków. * * zaalokowany bufor. */ char *gg_base64_encode(const char *buf) { char *out, *res; unsigned int i = 0, j = 0, k = 0, len = strlen(buf); res = out = malloc((len / 3 + 1) * 4 + 2); if (!res) return NULL; while (j <= len) { switch (i % 4) { case 0: k = (buf[j] & 252) >> 2; break; case 1: if (j < len) k = ((buf[j] & 3) << 4) | ((buf[j + 1] & 240) >> 4); else k = (buf[j] & 3) << 4; j++; break; case 2: if (j < len) k = ((buf[j] & 15) << 2) | ((buf[j + 1] & 192) >> 6); else k = (buf[j] & 15) << 2; j++; break; case 3: k = buf[j++] & 63; break; } *out++ = gg_base64_charset[k]; i++; } if (i % 4) for (j = 0; j < 4 - (i % 4); j++, out++) *out = '='; *out = 0; return res; } /* * gg_base64_decode() * * dekoduje ciąg znaków z base64. * * - buf - ciąg znaków. * * zaalokowany bufor. */ char *gg_base64_decode(const char *buf) { char *res, *save, *foo, val; const char *end; unsigned int index = 0; if (!buf) return NULL; save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2); if (!save) return NULL; end = buf + strlen(buf); while (*buf && buf < end) { if (*buf == '\r' || *buf == '\n') { buf++; continue; } if (!(foo = strchr(gg_base64_charset, *buf))) foo = gg_base64_charset; val = (int)(foo - gg_base64_charset); buf++; switch (index) { case 0: *res |= val << 2; break; case 1: *res++ |= val >> 4; *res |= val << 4; break; case 2: *res++ |= val >> 2; *res |= val << 6; break; case 3: *res++ |= val; break; } index++; index %= 4; } *res = 0; return save; } /* * gg_proxy_auth() // funkcja wewnętrzna * * tworzy nagłówek autoryzacji dla proxy. * * zaalokowany tekst lub NULL, jeśli proxy nie jest włączone lub nie wymaga * autoryzacji. */ char *gg_proxy_auth() { char *tmp, *enc, *out; unsigned int tmp_size; if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password) return NULL; if (!(tmp = malloc((tmp_size = strlen(gg_proxy_username) + strlen(gg_proxy_password) + 2)))) return NULL; snprintf(tmp, tmp_size, "%s:%s", gg_proxy_username, gg_proxy_password); if (!(enc = gg_base64_encode(tmp))) { free(tmp); return NULL; } free(tmp); if (!(out = malloc(strlen(enc) + 40))) { free(enc); return NULL; } snprintf(out, strlen(enc) + 40, "Proxy-Authorization: Basic %s\r\n", enc); free(enc); return out; } static uint32_t gg_crc32_table[256]; static int gg_crc32_initialized = 0; /* * gg_crc32_make_table() // funkcja wewnętrzna */ static void gg_crc32_make_table() { uint32_t h = 1; unsigned int i, j; memset(gg_crc32_table, 0, sizeof(gg_crc32_table)); for (i = 128; i; i >>= 1) { h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0); for (j = 0; j < 256; j += 2 * i) gg_crc32_table[i + j] = gg_crc32_table[j] ^ h; } gg_crc32_initialized = 1; } /* * gg_crc32() * * wyznacza sumę kontrolną CRC32 danego bloku danych. * * - crc - suma kontrola poprzedniego bloku danych lub 0 jeśli pierwszy * - buf - bufor danych * - size - ilość danych * * suma kontrolna CRC32. */ uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len) { if (!gg_crc32_initialized) gg_crc32_make_table(); if (!buf || len < 0) return crc; crc ^= 0xffffffffL; while (len--) crc = (crc >> 8) ^ gg_crc32_table[(crc ^ *buf++) & 0xff]; return crc ^ 0xffffffffL; } /* * Local variables: * c-indentation-style: k&r * c-basic-offset: 8 * indent-tabs-mode: notnil * End: * * vim: shiftwidth=8: */