pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
IRC: fill required command parameter counts (part 3)
release-2.x.y
2014-01-16, Tomasz Wasilczyk
6b0e0566af20
IRC: fill required command parameter counts (part 3)
/*
* (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl>
*
* 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 pubdir50.c
*
* \brief Obsługa katalogu publicznego od wersji Gadu-Gadu 5.x
*
* \todo Zoptymalizować konwersję CP1250<->UTF8. Obecnie robiona jest
* testowa konwersja, żeby poznać długość tekstu wynikowego.
*/
#include
<errno.h>
#include
<stdlib.h>
#include
<string.h>
#include
<time.h>
#include
"libgadu.h"
#include
"libgadu-config.h"
#include
"libgadu-internal.h"
#include
"encoding.h"
/**
* Tworzy nowe zapytanie katalogu publicznego.
*
* \param type Rodzaj zapytania
*
* \return Zmienna \c gg_pubdir50_t lub \c NULL w przypadku błędu.
*
* \ingroup pubdir50
*/
gg_pubdir50_t
gg_pubdir50_new
(
int
type
)
{
gg_pubdir50_t
res
=
malloc
(
sizeof
(
struct
gg_pubdir50_s
));
gg_debug
(
GG_DEBUG_FUNCTION
,
"** gg_pubdir50_new(%d);
\n
"
,
type
);
if
(
!
res
)
{
gg_debug
(
GG_DEBUG_MISC
,
"// gg_pubdir50_new() out of memory
\n
"
);
return
NULL
;
}
memset
(
res
,
0
,
sizeof
(
struct
gg_pubdir50_s
));
res
->
type
=
type
;
return
res
;
}
/**
* \internal Dodaje lub zastępuje pole zapytania lub odpowiedzi katalogu
* publicznego.
*
* \param req Zapytanie lub odpowiedź
* \param num Numer wyniku odpowiedzi (0 dla zapytania)
* \param field Nazwa pola
* \param value Wartość pola
*
* \return 0 jeśli się powiodło, -1 w przypadku błędu
*/
static
int
gg_pubdir50_add_n
(
gg_pubdir50_t
req
,
int
num
,
const
char
*
field
,
const
char
*
value
)
{
struct
gg_pubdir50_entry
*
tmp
=
NULL
,
*
entry
;
char
*
dupfield
,
*
dupvalue
;
int
i
;
gg_debug
(
GG_DEBUG_FUNCTION
,
"** gg_pubdir50_add_n(%p, %d,
\"
%s
\"
,
\"
%s
\"
);
\n
"
,
req
,
num
,
field
,
value
);
if
(
!
(
dupvalue
=
strdup
(
value
)))
{
gg_debug
(
GG_DEBUG_MISC
,
"// gg_pubdir50_add_n() out of memory
\n
"
);
return
-1
;
}
for
(
i
=
0
;
i
<
req
->
entries_count
;
i
++
)
{
if
(
req
->
entries
[
i
].
num
!=
num
||
strcmp
(
req
->
entries
[
i
].
field
,
field
))
continue
;
free
(
req
->
entries
[
i
].
value
);
req
->
entries
[
i
].
value
=
dupvalue
;
return
0
;
}
if
(
!
(
dupfield
=
strdup
(
field
)))
{
gg_debug
(
GG_DEBUG_MISC
,
"// gg_pubdir50_add_n() out of memory
\n
"
);
free
(
dupvalue
);
return
-1
;
}
if
(
!
(
tmp
=
realloc
(
req
->
entries
,
sizeof
(
struct
gg_pubdir50_entry
)
*
(
req
->
entries_count
+
1
))))
{
gg_debug
(
GG_DEBUG_MISC
,
"// gg_pubdir50_add_n() out of memory
\n
"
);
free
(
dupfield
);
free
(
dupvalue
);
return
-1
;
}
req
->
entries
=
tmp
;
entry
=
&
req
->
entries
[
req
->
entries_count
];
entry
->
num
=
num
;
entry
->
field
=
dupfield
;
entry
->
value
=
dupvalue
;
req
->
entries_count
++
;
return
0
;
}
/**
* Dodaje pole zapytania.
*
* \param req Zapytanie
* \param field Nazwa pola
* \param value Wartość pola
*
* \return 0 jeśli się powiodło, -1 w przypadku błędu
*
* \ingroup pubdir50
*/
int
gg_pubdir50_add
(
gg_pubdir50_t
req
,
const
char
*
field
,
const
char
*
value
)
{
return
gg_pubdir50_add_n
(
req
,
0
,
field
,
value
);
}
/**
* Ustawia numer sekwencyjny zapytania.
*
* \param req Zapytanie
* \param seq Numer sekwencyjny
*
* \return 0 jeśli się powiodło, -1 w przypadku błędu
*
* \ingroup pubdir50
*/
int
gg_pubdir50_seq_set
(
gg_pubdir50_t
req
,
uint32_t
seq
)
{
gg_debug
(
GG_DEBUG_FUNCTION
,
"** gg_pubdir50_seq_set(%p, %d);
\n
"
,
req
,
seq
);
if
(
!
req
)
{
gg_debug
(
GG_DEBUG_MISC
,
"// gg_pubdir50_seq_set() invalid arguments
\n
"
);
errno
=
EFAULT
;
return
-1
;
}
req
->
seq
=
seq
;
return
0
;
}
/**
* Zwalnia zasoby po zapytaniu lub odpowiedzi katalogu publicznego.
*
* \param s Zapytanie lub odpowiedź
*
* \ingroup pubdir50
*/
void
gg_pubdir50_free
(
gg_pubdir50_t
s
)
{
int
i
;
if
(
!
s
)
return
;
for
(
i
=
0
;
i
<
s
->
entries_count
;
i
++
)
{
free
(
s
->
entries
[
i
].
field
);
free
(
s
->
entries
[
i
].
value
);
}
free
(
s
->
entries
);
free
(
s
);
}
/**
* Wysyła zapytanie katalogu publicznego do serwera.
*
* \param sess Struktura sesji
* \param req Zapytanie
*
* \return Numer sekwencyjny zapytania lub 0 w przypadku błędu
*
* \ingroup pubdir50
*/
uint32_t
gg_pubdir50
(
struct
gg_session
*
sess
,
gg_pubdir50_t
req
)
{
int
i
,
size
=
5
;
uint32_t
res
;
char
*
buf
,
*
p
;
struct
gg_pubdir50_request
*
r
;
gg_debug_session
(
sess
,
GG_DEBUG_FUNCTION
,
"** gg_pubdir50(%p, %p);
\n
"
,
sess
,
req
);
if
(
!
sess
||
!
req
)
{
gg_debug_session
(
sess
,
GG_DEBUG_MISC
,
"// gg_pubdir50() invalid arguments
\n
"
);
errno
=
EFAULT
;
return
0
;
}
if
(
sess
->
state
!=
GG_STATE_CONNECTED
)
{
gg_debug_session
(
sess
,
GG_DEBUG_MISC
,
"// gg_pubdir50() not connected
\n
"
);
errno
=
ENOTCONN
;
return
0
;
}
for
(
i
=
0
;
i
<
req
->
entries_count
;
i
++
)
{
/* wyszukiwanie bierze tylko pierwszy wpis */
if
(
req
->
entries
[
i
].
num
)
continue
;
if
(
sess
->
encoding
==
GG_ENCODING_CP1250
)
{
size
+=
strlen
(
req
->
entries
[
i
].
field
)
+
1
;
size
+=
strlen
(
req
->
entries
[
i
].
value
)
+
1
;
}
else
{
char
*
tmp
;
// XXX \todo zoptymalizować
tmp
=
gg_encoding_convert
(
req
->
entries
[
i
].
field
,
sess
->
encoding
,
GG_ENCODING_CP1250
,
-1
,
-1
);
if
(
tmp
==
NULL
)
return
-1
;
size
+=
strlen
(
tmp
)
+
1
;
free
(
tmp
);
// XXX \todo zoptymalizować
tmp
=
gg_encoding_convert
(
req
->
entries
[
i
].
value
,
sess
->
encoding
,
GG_ENCODING_CP1250
,
-1
,
-1
);
if
(
tmp
==
NULL
)
return
-1
;
size
+=
strlen
(
tmp
)
+
1
;
free
(
tmp
);
}
}
if
(
!
(
buf
=
malloc
(
size
)))
{
gg_debug_session
(
sess
,
GG_DEBUG_MISC
,
"// gg_pubdir50() out of memory (%d bytes)
\n
"
,
size
);
return
0
;
}
if
(
!
req
->
seq
)
req
->
seq
=
time
(
NULL
);
res
=
req
->
seq
;
r
=
(
struct
gg_pubdir50_request
*
)
buf
;
r
->
type
=
req
->
type
;
r
->
seq
=
gg_fix32
(
req
->
seq
);
for
(
i
=
0
,
p
=
buf
+
5
;
i
<
req
->
entries_count
;
i
++
)
{
if
(
req
->
entries
[
i
].
num
)
continue
;
if
(
sess
->
encoding
==
GG_ENCODING_CP1250
)
{
strcpy
(
p
,
req
->
entries
[
i
].
field
);
p
+=
strlen
(
p
)
+
1
;
strcpy
(
p
,
req
->
entries
[
i
].
value
);
p
+=
strlen
(
p
)
+
1
;
}
else
{
char
*
tmp
;
// XXX \todo zoptymalizować
tmp
=
gg_encoding_convert
(
req
->
entries
[
i
].
field
,
sess
->
encoding
,
GG_ENCODING_CP1250
,
-1
,
-1
);
if
(
tmp
==
NULL
)
{
free
(
buf
);
return
-1
;
}
strcpy
(
p
,
tmp
);
p
+=
strlen
(
tmp
)
+
1
;
free
(
tmp
);
// XXX \todo zoptymalizować
tmp
=
gg_encoding_convert
(
req
->
entries
[
i
].
value
,
sess
->
encoding
,
GG_ENCODING_CP1250
,
-1
,
-1
);
if
(
tmp
==
NULL
)
{
free
(
buf
);
return
-1
;
}
strcpy
(
p
,
tmp
);
p
+=
strlen
(
tmp
)
+
1
;
free
(
tmp
);
}
}
if
(
gg_send_packet
(
sess
,
GG_PUBDIR50_REQUEST
,
buf
,
size
,
NULL
,
0
)
==
-1
)
res
=
0
;
free
(
buf
);
return
res
;
}
/*
* \internal Analizuje przychodzący pakiet odpowiedzi i zapisuje wynik
* w strukturze \c gg_event.
*
* \param sess Struktura sesji
* \param e Struktura zdarzenia
* \param packet Pakiet odpowiedzi
* \param length Długość pakietu odpowiedzi
*
* \return 0 jeśli się powiodło, -1 w przypadku błędu
*/
int
gg_pubdir50_handle_reply_sess
(
struct
gg_session
*
sess
,
struct
gg_event
*
e
,
const
char
*
packet
,
int
length
)
{
const
char
*
end
=
packet
+
length
,
*
p
;
struct
gg_pubdir50_reply
*
r
=
(
struct
gg_pubdir50_reply
*
)
packet
;
gg_pubdir50_t
res
;
int
num
=
0
;
gg_debug
(
GG_DEBUG_FUNCTION
,
"** gg_pubdir50_handle_reply_sess(%p, %p, %p, %d);
\n
"
,
sess
,
e
,
packet
,
length
);
if
(
!
sess
||
!
e
||
!
packet
)
{
gg_debug
(
GG_DEBUG_MISC
,
"// gg_pubdir50_handle_reply() invalid arguments
\n
"
);
errno
=
EFAULT
;
return
-1
;
}
if
(
length
<
5
)
{
gg_debug
(
GG_DEBUG_MISC
,
"// gg_pubdir50_handle_reply() packet too short
\n
"
);
errno
=
EINVAL
;
return
-1
;
}
if
(
!
(
res
=
gg_pubdir50_new
(
r
->
type
)))
{
gg_debug
(
GG_DEBUG_MISC
,
"// gg_pubdir50_handle_reply() unable to allocate reply
\n
"
);
return
-1
;
}
e
->
event
.
pubdir50
=
res
;
res
->
seq
=
gg_fix32
(
r
->
seq
);
switch
(
res
->
type
)
{
case
GG_PUBDIR50_READ
:
e
->
type
=
GG_EVENT_PUBDIR50_READ
;
break
;
case
GG_PUBDIR50_WRITE
:
e
->
type
=
GG_EVENT_PUBDIR50_WRITE
;
break
;
default
:
e
->
type
=
GG_EVENT_PUBDIR50_SEARCH_REPLY
;
break
;
}
/* brak wyników? */
if
(
length
==
5
)
return
0
;
/* pomiń początek odpowiedzi */
p
=
packet
+
5
;
while
(
p
<
end
)
{
const
char
*
field
,
*
value
;
field
=
p
;
/* sprawdź, czy nie mamy podziału na kolejne pole */
if
(
!*
field
)
{
num
++
;
field
++
;
}
value
=
NULL
;
for
(
p
=
field
;
p
<
end
;
p
++
)
{
/* jeśli mamy koniec tekstu... */
if
(
!*
p
)
{
/* ...i jeszcze nie mieliśmy wartości pola to
* wiemy, że po tym zerze jest wartość... */
if
(
!
value
)
value
=
p
+
1
;
else
/* ...w przeciwym wypadku koniec
* wartości i możemy wychodzić
* grzecznie z pętli */
break
;
}
}
/* sprawdźmy, czy pole nie wychodzi poza pakiet, żeby nie
* mieć segfaultów, jeśli serwer przestanie zakańczać pakietów
* przez \0 */
if
(
p
==
end
)
{
gg_debug
(
GG_DEBUG_MISC
,
"// gg_pubdir50_handle_reply() premature end of packet
\n
"
);
goto
failure
;
}
p
++
;
/* jeśli dostaliśmy namier na następne wyniki, to znaczy że
* mamy koniec wyników i nie jest to kolejna osoba. */
if
(
!
strcasecmp
(
field
,
"nextstart"
))
{
res
->
next
=
atoi
(
value
);
num
--
;
}
else
{
if
(
sess
->
encoding
==
GG_ENCODING_CP1250
)
{
if
(
gg_pubdir50_add_n
(
res
,
num
,
field
,
value
)
==
-1
)
goto
failure
;
}
else
{
char
*
tmp
;
tmp
=
gg_encoding_convert
(
value
,
GG_ENCODING_CP1250
,
sess
->
encoding
,
-1
,
-1
);
if
(
tmp
==
NULL
)
goto
failure
;
if
(
gg_pubdir50_add_n
(
res
,
num
,
field
,
tmp
)
==
-1
)
{
free
(
tmp
);
goto
failure
;
}
free
(
tmp
);
}
}
}
res
->
count
=
num
+
1
;
return
0
;
failure
:
gg_pubdir50_free
(
res
);
return
-1
;
}
/**
* Pobiera pole z odpowiedzi katalogu publicznego.
*
* \param res Odpowiedź
* \param num Numer wyniku odpowiedzi
* \param field Nazwa pola (wielkość liter nie ma znaczenia)
*
* \return Wartość pola lub \c NULL jeśli nie znaleziono
*
* \ingroup pubdir50
*/
const
char
*
gg_pubdir50_get
(
gg_pubdir50_t
res
,
int
num
,
const
char
*
field
)
{
char
*
value
=
NULL
;
int
i
;
gg_debug
(
GG_DEBUG_FUNCTION
,
"** gg_pubdir50_get(%p, %d,
\"
%s
\"
);
\n
"
,
res
,
num
,
field
);
if
(
!
res
||
num
<
0
||
!
field
)
{
gg_debug
(
GG_DEBUG_MISC
,
"// gg_pubdir50_get() invalid arguments
\n
"
);
errno
=
EINVAL
;
return
NULL
;
}
for
(
i
=
0
;
i
<
res
->
entries_count
;
i
++
)
{
if
(
res
->
entries
[
i
].
num
==
num
&&
!
strcasecmp
(
res
->
entries
[
i
].
field
,
field
))
{
value
=
res
->
entries
[
i
].
value
;
break
;
}
}
return
value
;
}
/**
* Zwraca liczbę wyników odpowiedzi.
*
* \param res Odpowiedź
*
* \return Liczba wyników lub -1 w przypadku błędu
*
* \ingroup pubdir50
*/
int
gg_pubdir50_count
(
gg_pubdir50_t
res
)
{
return
(
!
res
)
?
-1
:
res
->
count
;
}
/**
* Zwraca rodzaj zapytania lub odpowiedzi.
*
* \param res Zapytanie lub odpowiedź
*
* \return Rodzaj lub -1 w przypadku błędu
*
* \ingroup pubdir50
*/
int
gg_pubdir50_type
(
gg_pubdir50_t
res
)
{
return
(
!
res
)
?
-1
:
res
->
type
;
}
/**
* Zwraca numer, od którego należy rozpocząc kolejne wyszukiwanie.
*
* Dłuższe odpowiedzi katalogu publicznego są wysyłane przez serwer
* w mniejszych paczkach. Po otrzymaniu odpowiedzi, jeśli numer kolejnego
* wyszukiwania jest większy od zera, dalsze wyniki można otrzymać przez
* wywołanie kolejnego zapytania z określonym numerem początkowym.
*
* \param res Odpowiedź
*
* \return Numer lub -1 w przypadku błędu
*
* \ingroup pubdir50
*/
uin_t
gg_pubdir50_next
(
gg_pubdir50_t
res
)
{
return
(
!
res
)
?
(
unsigned
)
-1
:
res
->
next
;
}
/**
* Zwraca numer sekwencyjny zapytania lub odpowiedzi.
*
* \param res Zapytanie lub odpowiedź
*
* \return Numer sekwencyjny lub -1 w przypadku błędu
*
* \ingroup pubdir50
*/
uint32_t
gg_pubdir50_seq
(
gg_pubdir50_t
res
)
{
return
(
!
res
)
?
(
unsigned
)
-1
:
res
->
seq
;
}
/*
* Local variables:
* c-indentation-style: k&r
* c-basic-offset: 8
* indent-tabs-mode: notnil
* End:
*
* vim: shiftwidth=8:
*/