/* * (C) Copyright 2001-2006 Wojtek Kaniewski * Robert J. Woźny * Arkadiusz Miśkiewicz * Tomasz Chiliński * Adam Wysocki * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ /** * \file debug.c * * \brief Funkcje odpluskwiania */ #include #ifdef _WIN32 #include "win32.h" #endif /* _WIN32 */ #include #include #include #include #include "libgadu.h" #include "debug.h" /** * Poziom rejestracji informacji odpluskwiających. Zmienna jest maską bitową * składającą się ze stałych \c GG_DEBUG_... * * \ingroup debug */ int gg_debug_level = 0; /** * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe * \c NULL, informacje są wysyłane do standardowego wyjścia błędu (\c stderr). * * \param level Poziom rejestracji * \param format Format wiadomości (zgodny z \c printf) * \param ap Lista argumentów (zgodna z \c printf) * * \note Funkcja jest przesłaniana przez \c gg_debug_handler_session. * * \ingroup debug */ void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL; /** * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe * \c NULL, informacje są wysyłane do standardowego wyjścia błędu. * * \param sess Sesja której dotyczy informacja lub \c NULL * \param level Poziom rejestracji * \param format Format wiadomości (zgodny z \c printf) * \param ap Lista argumentów (zgodna z \c printf) * * \note Funkcja przesłania przez \c gg_debug_handler_session. * * \ingroup debug */ void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap) = NULL; /** * Plik, do którego będą przekazywane informacje odpluskwiania. * * Funkcja \c gg_debug() i pochodne mogą być przechwytywane przez aplikację * korzystającą z biblioteki, by wyświetlić je na żądanie użytkownika lub * zapisać do późniejszej analizy. Jeśli nie określono pliku, wybrane * informacje będą wysyłane do standardowego wyjścia błędu (\c stderr). * * \ingroup debug */ FILE *gg_debug_file = NULL; #ifndef GG_DEBUG_DISABLE /** * \internal Przekazuje informacje odpluskwiania do odpowiedniej funkcji. * * Jeśli aplikacja ustawiła odpowiednią funkcję obsługi w * \c gg_debug_handler_session lub \c gg_debug_handler, jest ona wywoływana. * W przeciwnym wypadku wynik jest wysyłany do standardowego wyjścia błędu. * * \param sess Struktura sesji (może być \c NULL) * \param level Poziom informacji * \param format Format wiadomości (zgodny z \c printf) * \param ap Lista argumentów (zgodna z \c printf) */ void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap) { if (gg_debug_handler_session != NULL) (*gg_debug_handler_session)(sess, level, format, ap); else if (gg_debug_handler != NULL) (*gg_debug_handler)(level, format, ap); else if ((gg_debug_level & level) != 0) vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap); } /** * \internal Przekazuje informację odpluskawiania. * * \param level Poziom wiadomości * \param format Format wiadomości (zgodny z \c printf) * * \ingroup debug */ void gg_debug(int level, const char *format, ...) { va_list ap; int old_errno = errno; va_start(ap, format); gg_debug_common(NULL, level, format, ap); va_end(ap); errno = old_errno; } /** * \internal Przekazuje informację odpluskwiania związaną z sesją. * * \param gs Struktura sesji * \param level Poziom wiadomości * \param format Format wiadomości (zgodny z \c printf) * * \ingroup debug */ void gg_debug_session(struct gg_session *gs, int level, const char *format, ...) { va_list ap; int old_errno = errno; va_start(ap, format); gg_debug_common(gs, level, format, ap); va_end(ap); errno = old_errno; } /** * \internal Przekazuje zrzut bufora do odpluskwiania. * * \param gs Struktura sesji * \param level Poziom wiadomości * \param buf Bufor danych * \param len Długość bufora danych * * \ingroup debug */ void gg_debug_dump(struct gg_session *gs, int level, const char *buf, size_t len) { char line[80]; int i, j; for (i = 0; i < len; i += 16) { int ofs; sprintf(line, "%.4x: ", i); ofs = 6; for (j = 0; j < 16; j++) { if (i + j < len) sprintf(line + ofs, " %02x", (unsigned char) buf[i + j]); else sprintf(line + ofs, " "); ofs += 3; } sprintf(line + ofs, " "); ofs += 2; for (j = 0; j < 16; j++) { unsigned char ch; if (i + j < len) { ch = buf[i + j]; if (ch < 32 || ch > 126) ch = '.'; } else { ch = ' '; } line[ofs++] = ch; } line[ofs++] = '\n'; line[ofs++] = 0; gg_debug_session(gs, level, "%s", line); } } /** * \internal Zwraca ciąg z nazwą podanego stanu sesji. * * \param state Stan sesji. * * \return Ciąg z nazwą stanu * * \ingroup debug */ const char *gg_debug_state(enum gg_state_t state) { switch (state) { #define GG_DEBUG_STATE(x) case x: return #x; GG_DEBUG_STATE(GG_STATE_IDLE) GG_DEBUG_STATE(GG_STATE_RESOLVING) GG_DEBUG_STATE(GG_STATE_CONNECTING) GG_DEBUG_STATE(GG_STATE_READING_DATA) GG_DEBUG_STATE(GG_STATE_ERROR) GG_DEBUG_STATE(GG_STATE_CONNECTING_HUB) GG_DEBUG_STATE(GG_STATE_CONNECTING_GG) GG_DEBUG_STATE(GG_STATE_READING_KEY) GG_DEBUG_STATE(GG_STATE_READING_REPLY) GG_DEBUG_STATE(GG_STATE_CONNECTED) GG_DEBUG_STATE(GG_STATE_SENDING_QUERY) GG_DEBUG_STATE(GG_STATE_READING_HEADER) GG_DEBUG_STATE(GG_STATE_PARSING) GG_DEBUG_STATE(GG_STATE_DONE) GG_DEBUG_STATE(GG_STATE_LISTENING) GG_DEBUG_STATE(GG_STATE_READING_UIN_1) GG_DEBUG_STATE(GG_STATE_READING_UIN_2) GG_DEBUG_STATE(GG_STATE_SENDING_ACK) GG_DEBUG_STATE(GG_STATE_READING_ACK) GG_DEBUG_STATE(GG_STATE_READING_REQUEST) GG_DEBUG_STATE(GG_STATE_SENDING_REQUEST) GG_DEBUG_STATE(GG_STATE_SENDING_FILE_INFO) GG_DEBUG_STATE(GG_STATE_READING_PRE_FILE_INFO) GG_DEBUG_STATE(GG_STATE_READING_FILE_INFO) GG_DEBUG_STATE(GG_STATE_SENDING_FILE_ACK) GG_DEBUG_STATE(GG_STATE_READING_FILE_ACK) GG_DEBUG_STATE(GG_STATE_SENDING_FILE_HEADER) GG_DEBUG_STATE(GG_STATE_READING_FILE_HEADER) GG_DEBUG_STATE(GG_STATE_GETTING_FILE) GG_DEBUG_STATE(GG_STATE_SENDING_FILE) GG_DEBUG_STATE(GG_STATE_READING_VOICE_ACK) GG_DEBUG_STATE(GG_STATE_READING_VOICE_HEADER) GG_DEBUG_STATE(GG_STATE_READING_VOICE_SIZE) GG_DEBUG_STATE(GG_STATE_READING_VOICE_DATA) GG_DEBUG_STATE(GG_STATE_SENDING_VOICE_ACK) GG_DEBUG_STATE(GG_STATE_SENDING_VOICE_REQUEST) GG_DEBUG_STATE(GG_STATE_READING_TYPE) GG_DEBUG_STATE(GG_STATE_TLS_NEGOTIATION) GG_DEBUG_STATE(GG_STATE_REQUESTING_ID) GG_DEBUG_STATE(GG_STATE_WAITING_FOR_ACCEPT) GG_DEBUG_STATE(GG_STATE_WAITING_FOR_INFO) GG_DEBUG_STATE(GG_STATE_READING_ID) GG_DEBUG_STATE(GG_STATE_SENDING_ID) GG_DEBUG_STATE(GG_STATE_RESOLVING_GG) GG_DEBUG_STATE(GG_STATE_RESOLVING_RELAY) GG_DEBUG_STATE(GG_STATE_CONNECTING_RELAY) GG_DEBUG_STATE(GG_STATE_READING_RELAY) GG_DEBUG_STATE(GG_STATE_DISCONNECTING) #undef GG_DEBUG_STATE /* Celowo nie ma default, żeby kompilator wyłapał brakujące stany */ } return NULL; } /** * \internal Zwraca ciąg z nazwą podanego zdarzenia. * * \param event Zdarzenie. * * \return Ciąg z nazwą zdarzenia * * \ingroup debug */ const char *gg_debug_event(enum gg_event_t event) { switch (event) { #define GG_DEBUG_EVENT(x) case x: return #x; GG_DEBUG_EVENT(GG_EVENT_NONE) GG_DEBUG_EVENT(GG_EVENT_MSG) GG_DEBUG_EVENT(GG_EVENT_NOTIFY) GG_DEBUG_EVENT(GG_EVENT_NOTIFY_DESCR) GG_DEBUG_EVENT(GG_EVENT_STATUS) GG_DEBUG_EVENT(GG_EVENT_ACK) GG_DEBUG_EVENT(GG_EVENT_PONG) GG_DEBUG_EVENT(GG_EVENT_CONN_FAILED) GG_DEBUG_EVENT(GG_EVENT_CONN_SUCCESS) GG_DEBUG_EVENT(GG_EVENT_DISCONNECT) GG_DEBUG_EVENT(GG_EVENT_DCC_NEW) GG_DEBUG_EVENT(GG_EVENT_DCC_ERROR) GG_DEBUG_EVENT(GG_EVENT_DCC_DONE) GG_DEBUG_EVENT(GG_EVENT_DCC_CLIENT_ACCEPT) GG_DEBUG_EVENT(GG_EVENT_DCC_CALLBACK) GG_DEBUG_EVENT(GG_EVENT_DCC_NEED_FILE_INFO) GG_DEBUG_EVENT(GG_EVENT_DCC_NEED_FILE_ACK) GG_DEBUG_EVENT(GG_EVENT_DCC_NEED_VOICE_ACK) GG_DEBUG_EVENT(GG_EVENT_DCC_VOICE_DATA) GG_DEBUG_EVENT(GG_EVENT_PUBDIR50_SEARCH_REPLY) GG_DEBUG_EVENT(GG_EVENT_PUBDIR50_READ) GG_DEBUG_EVENT(GG_EVENT_PUBDIR50_WRITE) GG_DEBUG_EVENT(GG_EVENT_STATUS60) GG_DEBUG_EVENT(GG_EVENT_NOTIFY60) GG_DEBUG_EVENT(GG_EVENT_USERLIST) GG_DEBUG_EVENT(GG_EVENT_IMAGE_REQUEST) GG_DEBUG_EVENT(GG_EVENT_IMAGE_REPLY) GG_DEBUG_EVENT(GG_EVENT_DCC_ACK) GG_DEBUG_EVENT(GG_EVENT_DCC7_NEW) GG_DEBUG_EVENT(GG_EVENT_DCC7_ACCEPT) GG_DEBUG_EVENT(GG_EVENT_DCC7_REJECT) GG_DEBUG_EVENT(GG_EVENT_DCC7_CONNECTED) GG_DEBUG_EVENT(GG_EVENT_DCC7_ERROR) GG_DEBUG_EVENT(GG_EVENT_DCC7_DONE) GG_DEBUG_EVENT(GG_EVENT_DCC7_PENDING) GG_DEBUG_EVENT(GG_EVENT_XML_EVENT) GG_DEBUG_EVENT(GG_EVENT_DISCONNECT_ACK) GG_DEBUG_EVENT(GG_EVENT_TYPING_NOTIFICATION) GG_DEBUG_EVENT(GG_EVENT_USER_DATA) GG_DEBUG_EVENT(GG_EVENT_MULTILOGON_MSG) GG_DEBUG_EVENT(GG_EVENT_MULTILOGON_INFO) GG_DEBUG_EVENT(GG_EVENT_USERLIST100_VERSION) GG_DEBUG_EVENT(GG_EVENT_USERLIST100_REPLY) #undef GG_DEBUG_EVENT /* Celowo nie ma default, żeby kompilator wyłapał brakujące zdarzenia */ } return NULL; } #else #undef gg_debug_common void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap) { } #undef gg_debug void gg_debug(int level, const char *format, ...) { } #undef gg_debug_session void gg_debug_session(struct gg_session *gs, int level, const char *format, ...) { } #undef gg_debug_dump void gg_debug_dump(struct gg_session *gs, int level, const char *buf, size_t len) { } #endif