comparison libpurple/protocols/gg/lib/pubdir50.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 7ade887fd3f6
comparison
equal deleted inserted replaced
15372:f79e0f4df793 15373:5fe8042783c1
1 /* $Id: pubdir50.c 16856 2006-08-19 01:13:25Z evands $ */
2
3 /*
4 * (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License Version
8 * 2.1 as published by the Free Software Foundation.
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 Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
18 * USA.
19 */
20
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <time.h>
25
26 #include "libgadu.h"
27
28 /*
29 * gg_pubdir50_new()
30 *
31 * tworzy nową zmienną typu gg_pubdir50_t.
32 *
33 * zaalokowana zmienna lub NULL w przypadku braku pamięci.
34 */
35 gg_pubdir50_t gg_pubdir50_new(int type)
36 {
37 gg_pubdir50_t res = malloc(sizeof(struct gg_pubdir50_s));
38
39 gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_new(%d);\n", type);
40
41 if (!res) {
42 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_new() out of memory\n");
43 return NULL;
44 }
45
46 memset(res, 0, sizeof(struct gg_pubdir50_s));
47
48 res->type = type;
49
50 return res;
51 }
52
53 /*
54 * gg_pubdir50_add_n() // funkcja wewnętrzna
55 *
56 * funkcja dodaje lub zastępuje istniejące pole do zapytania lub odpowiedzi.
57 *
58 * - req - wskaźnik opisu zapytania,
59 * - num - numer wyniku (0 dla zapytania),
60 * - field - nazwa pola,
61 * - value - wartość pola,
62 *
63 * 0/-1
64 */
65 static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value)
66 {
67 struct gg_pubdir50_entry *tmp = NULL, *entry;
68 char *dupfield, *dupvalue;
69 int i;
70
71 gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_add_n(%p, %d, \"%s\", \"%s\");\n", req, num, field, value);
72
73 if (!(dupvalue = strdup(value))) {
74 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
75 return -1;
76 }
77
78 for (i = 0; i < req->entries_count; i++) {
79 if (req->entries[i].num != num || strcmp(req->entries[i].field, field))
80 continue;
81
82 free(req->entries[i].value);
83 req->entries[i].value = dupvalue;
84
85 return 0;
86 }
87
88 if (!(dupfield = strdup(field))) {
89 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
90 free(dupvalue);
91 return -1;
92 }
93
94 if (!(tmp = realloc(req->entries, sizeof(struct gg_pubdir50_entry) * (req->entries_count + 1)))) {
95 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
96 free(dupfield);
97 free(dupvalue);
98 return -1;
99 }
100
101 req->entries = tmp;
102
103 entry = &req->entries[req->entries_count];
104 entry->num = num;
105 entry->field = dupfield;
106 entry->value = dupvalue;
107
108 req->entries_count++;
109
110 return 0;
111 }
112
113 /*
114 * gg_pubdir50_add()
115 *
116 * funkcja dodaje pole do zapytania.
117 *
118 * - req - wskaźnik opisu zapytania,
119 * - field - nazwa pola,
120 * - value - wartość pola,
121 *
122 * 0/-1
123 */
124 int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value)
125 {
126 return gg_pubdir50_add_n(req, 0, field, value);
127 }
128
129 /*
130 * gg_pubdir50_seq_set()
131 *
132 * ustawia numer sekwencyjny zapytania.
133 *
134 * - req - zapytanie,
135 * - seq - nowy numer sekwencyjny.
136 *
137 * 0/-1.
138 */
139 int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq)
140 {
141 gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_seq_set(%p, %d);\n", req, seq);
142
143 if (!req) {
144 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_seq_set() invalid arguments\n");
145 errno = EFAULT;
146 return -1;
147 }
148
149 req->seq = seq;
150
151 return 0;
152 }
153
154 /*
155 * gg_pubdir50_free()
156 *
157 * zwalnia pamięć po zapytaniu lub rezultacie szukania użytkownika.
158 *
159 * - s - zwalniana zmienna,
160 */
161 void gg_pubdir50_free(gg_pubdir50_t s)
162 {
163 int i;
164
165 if (!s)
166 return;
167
168 for (i = 0; i < s->entries_count; i++) {
169 free(s->entries[i].field);
170 free(s->entries[i].value);
171 }
172
173 free(s->entries);
174 free(s);
175 }
176
177 /*
178 * gg_pubdir50()
179 *
180 * wysyła zapytanie katalogu publicznego do serwera.
181 *
182 * - sess - sesja,
183 * - req - zapytanie.
184 *
185 * numer sekwencyjny wyszukiwania lub 0 w przypadku błędu.
186 */
187 uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
188 {
189 int i, size = 5;
190 uint32_t res;
191 char *buf, *p;
192 struct gg_pubdir50_request *r;
193
194 gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
195
196 if (!sess || !req) {
197 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
198 errno = EFAULT;
199 return 0;
200 }
201
202 if (sess->state != GG_STATE_CONNECTED) {
203 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
204 errno = ENOTCONN;
205 return 0;
206 }
207
208 for (i = 0; i < req->entries_count; i++) {
209 /* wyszukiwanie bierze tylko pierwszy wpis */
210 if (req->entries[i].num)
211 continue;
212
213 size += strlen(req->entries[i].field) + 1;
214 size += strlen(req->entries[i].value) + 1;
215 }
216
217 if (!(buf = malloc(size))) {
218 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
219 return 0;
220 }
221
222 r = (struct gg_pubdir50_request*) buf;
223 res = time(NULL);
224 r->type = req->type;
225 r->seq = (req->seq) ? gg_fix32(req->seq) : gg_fix32(time(NULL));
226 req->seq = gg_fix32(r->seq);
227
228 for (i = 0, p = buf + 5; i < req->entries_count; i++) {
229 if (req->entries[i].num)
230 continue;
231
232 strcpy(p, req->entries[i].field);
233 p += strlen(p) + 1;
234
235 strcpy(p, req->entries[i].value);
236 p += strlen(p) + 1;
237 }
238
239 if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1)
240 res = 0;
241
242 free(buf);
243
244 return res;
245 }
246
247 /*
248 * gg_pubdir50_handle_reply() // funkcja wewnętrzna
249 *
250 * analizuje przychodzący pakiet odpowiedzi i zapisuje wynik w struct gg_event.
251 *
252 * - e - opis zdarzenia
253 * - packet - zawartość pakietu odpowiedzi
254 * - length - długość pakietu odpowiedzi
255 *
256 * 0/-1
257 */
258 int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
259 {
260 const char *end = packet + length, *p;
261 struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet;
262 gg_pubdir50_t res;
263 int num = 0;
264
265 gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply(%p, %p, %d);\n", e, packet, length);
266
267 if (!e || !packet) {
268 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n");
269 errno = EFAULT;
270 return -1;
271 }
272
273 if (length < 5) {
274 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() packet too short\n");
275 errno = EINVAL;
276 return -1;
277 }
278
279 if (!(res = gg_pubdir50_new(r->type))) {
280 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() unable to allocate reply\n");
281 return -1;
282 }
283
284 e->event.pubdir50 = res;
285
286 res->seq = gg_fix32(r->seq);
287
288 switch (res->type) {
289 case GG_PUBDIR50_READ:
290 e->type = GG_EVENT_PUBDIR50_READ;
291 break;
292
293 case GG_PUBDIR50_WRITE:
294 e->type = GG_EVENT_PUBDIR50_WRITE;
295 break;
296
297 default:
298 e->type = GG_EVENT_PUBDIR50_SEARCH_REPLY;
299 break;
300 }
301
302 /* brak wyników? */
303 if (length == 5)
304 return 0;
305
306 /* pomiń początek odpowiedzi */
307 p = packet + 5;
308
309 while (p < end) {
310 const char *field, *value;
311
312 field = p;
313
314 /* sprawdź, czy nie mamy podziału na kolejne pole */
315 if (!*field) {
316 num++;
317 field++;
318 }
319
320 value = NULL;
321
322 for (p = field; p < end; p++) {
323 /* jeśli mamy koniec tekstu... */
324 if (!*p) {
325 /* ...i jeszcze nie mieliśmy wartości pola to
326 * wiemy, że po tym zerze jest wartość... */
327 if (!value)
328 value = p + 1;
329 else
330 /* ...w przeciwym wypadku koniec
331 * wartości i możemy wychodzić
332 * grzecznie z pętli */
333 break;
334 }
335 }
336
337 /* sprawdźmy, czy pole nie wychodzi poza pakiet, żeby nie
338 * mieć segfaultów, jeśli serwer przestanie zakańczać pakietów
339 * przez \0 */
340
341 if (p == end) {
342 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() premature end of packet\n");
343 goto failure;
344 }
345
346 p++;
347
348 /* jeśli dostaliśmy namier na następne wyniki, to znaczy że
349 * mamy koniec wyników i nie jest to kolejna osoba. */
350 if (!strcasecmp(field, "nextstart")) {
351 res->next = atoi(value);
352 num--;
353 } else {
354 if (gg_pubdir50_add_n(res, num, field, value) == -1)
355 goto failure;
356 }
357 }
358
359 res->count = num + 1;
360
361 return 0;
362
363 failure:
364 gg_pubdir50_free(res);
365 return -1;
366 }
367
368 /*
369 * gg_pubdir50_get()
370 *
371 * pobiera informację z rezultatu wyszukiwania.
372 *
373 * - res - rezultat wyszukiwania,
374 * - num - numer odpowiedzi,
375 * - field - nazwa pola (wielkość liter nie ma znaczenia).
376 *
377 * wartość pola lub NULL, jeśli nie znaleziono.
378 */
379 const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field)
380 {
381 char *value = NULL;
382 int i;
383
384 gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_get(%p, %d, \"%s\");\n", res, num, field);
385
386 if (!res || num < 0 || !field) {
387 gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_get() invalid arguments\n");
388 errno = EINVAL;
389 return NULL;
390 }
391
392 for (i = 0; i < res->entries_count; i++) {
393 if (res->entries[i].num == num && !strcasecmp(res->entries[i].field, field)) {
394 value = res->entries[i].value;
395 break;
396 }
397 }
398
399 return value;
400 }
401
402 /*
403 * gg_pubdir50_count()
404 *
405 * zwraca ilość wyników danego zapytania.
406 *
407 * - res - odpowiedź
408 *
409 * ilość lub -1 w przypadku błędu.
410 */
411 int gg_pubdir50_count(gg_pubdir50_t res)
412 {
413 return (!res) ? -1 : res->count;
414 }
415
416 /*
417 * gg_pubdir50_type()
418 *
419 * zwraca rodzaj zapytania lub odpowiedzi.
420 *
421 * - res - zapytanie lub odpowiedź
422 *
423 * ilość lub -1 w przypadku błędu.
424 */
425 int gg_pubdir50_type(gg_pubdir50_t res)
426 {
427 return (!res) ? -1 : res->type;
428 }
429
430 /*
431 * gg_pubdir50_next()
432 *
433 * zwraca numer, od którego należy rozpocząć kolejne wyszukiwanie, jeśli
434 * zależy nam na kolejnych wynikach.
435 *
436 * - res - odpowiedź
437 *
438 * numer lub -1 w przypadku błędu.
439 */
440 uin_t gg_pubdir50_next(gg_pubdir50_t res)
441 {
442 return (!res) ? (unsigned) -1 : res->next;
443 }
444
445 /*
446 * gg_pubdir50_seq()
447 *
448 * zwraca numer sekwencyjny zapytania lub odpowiedzi.
449 *
450 * - res - zapytanie lub odpowiedź
451 *
452 * numer lub -1 w przypadku błędu.
453 */
454 uint32_t gg_pubdir50_seq(gg_pubdir50_t res)
455 {
456 return (!res) ? (unsigned) -1 : res->seq;
457 }
458
459 /*
460 * Local variables:
461 * c-indentation-style: k&r
462 * c-basic-offset: 8
463 * indent-tabs-mode: notnil
464 * End:
465 *
466 * vim: shiftwidth=8:
467 */