| |
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 */ |