Bugfixes.
* imap4d/util.c (util_parse_internal_date): Use MU_DATETIME_INTERNALDATE format. * include/mailutils/util.h (MU_DATETIME_IMAP_SEARCH): Rename to MU_DATETIME_INTERNALDATE (previous incorrect definition removed). (MU_DATETIME_RFC822): New format. * libmailutils/address/address.c: Accessors for local_part and domain invalidate email field. (validate_email): New static function. (mu_address_sget_email): Reconstruct email, if necessary. (mu_address_format_string): Likewise. * libmailutils/base/date.c (mu_c_streamftime): Allow for %$ specifier for compatibility with mu_scan_datetime. (mu_scan_datetime): Use %$ to idicate optional end of string. * libmailutils/tests/scantime.at: Update. * libproto/imap/fetch.c (_fill_response): Use MU_DATETIME_RFC822 format. * mu/imap.c (fetch_response_printer): Print subject.
Showing
7 changed files
with
185 additions
and
34 deletions
... | @@ -374,7 +374,7 @@ util_parse_internal_date (char *date, time_t *timep, | ... | @@ -374,7 +374,7 @@ util_parse_internal_date (char *date, time_t *timep, |
374 | mu_timezone tz; | 374 | mu_timezone tz; |
375 | time_t time; | 375 | time_t time; |
376 | 376 | ||
377 | if (mu_scan_datetime (date, MU_DATETIME_IMAP_SEARCH, &tm, &tz, NULL)) | 377 | if (mu_scan_datetime (date, MU_DATETIME_INTERNALDATE, &tm, &tz, NULL)) |
378 | return 1; | 378 | return 1; |
379 | 379 | ||
380 | adjust_tm (&tm, &tz, flag); | 380 | adjust_tm (&tm, &tz, flag); | ... | ... |
... | @@ -74,8 +74,9 @@ int mu_scan_datetime (const char *input, const char *fmt, struct tm *tm, | ... | @@ -74,8 +74,9 @@ int mu_scan_datetime (const char *input, const char *fmt, struct tm *tm, |
74 | /* Common datetime formats: */ | 74 | /* Common datetime formats: */ |
75 | #define MU_DATETIME_FROM "%a %b %e %H:%M:%S %Y" | 75 | #define MU_DATETIME_FROM "%a %b %e %H:%M:%S %Y" |
76 | #define MU_DATETIME_IMAP "%d-%b-%Y %H:%M:%S %z" | 76 | #define MU_DATETIME_IMAP "%d-%b-%Y %H:%M:%S %z" |
77 | #define MU_DATETIME_IMAP_SEARCH "%d-%b-%Y%? %H:%M:%S %z" | 77 | #define MU_DATETIME_INTERNALDATE "%d-%b-%Y%$ %H:%M:%S %z" |
78 | #define MU_DATETIME_INTERNALDATE "%a, %e %b %Y %H:%M:%S %z" | 78 | /* FIXME: [%a, ] part is actually optional */ |
79 | #define MU_DATETIME_RFC822 "%a, %e %b %Y %H:%M:%S %z" | ||
79 | 80 | ||
80 | 81 | ||
81 | /* ----------------------- */ | 82 | /* ----------------------- */ | ... | ... |
... | @@ -13,7 +13,7 @@ | ... | @@ -13,7 +13,7 @@ |
13 | Lesser General Public License for more details. | 13 | Lesser General Public License for more details. |
14 | 14 | ||
15 | You should have received a copy of the GNU Lesser General | 15 | You should have received a copy of the GNU Lesser General |
16 | Public License along with this library. If not, see | 16 | Public License along with this library. If not, see |
17 | <http://www.gnu.org/licenses/>. */ | 17 | <http://www.gnu.org/licenses/>. */ |
18 | 18 | ||
19 | #ifdef HAVE_CONFIG_H | 19 | #ifdef HAVE_CONFIG_H |
... | @@ -213,11 +213,11 @@ mu_address_concatenate (mu_address_t to, mu_address_t *from) | ... | @@ -213,11 +213,11 @@ mu_address_concatenate (mu_address_t to, mu_address_t *from) |
213 | return 0; | 213 | return 0; |
214 | } | 214 | } |
215 | 215 | ||
216 | mu_address_t | 216 | mu_address_t |
217 | _address_get_nth (mu_address_t addr, size_t no) | 217 | _address_get_nth (mu_address_t addr, size_t no) |
218 | { | 218 | { |
219 | int i; | 219 | int i; |
220 | 220 | ||
221 | for (i = 1; addr; addr = addr->next, i++) | 221 | for (i = 1; addr; addr = addr->next, i++) |
222 | if (i == no) | 222 | if (i == no) |
223 | break; | 223 | break; |
... | @@ -239,13 +239,13 @@ mu_address_get_nth (mu_address_t addr, size_t no, mu_address_t *pret) | ... | @@ -239,13 +239,13 @@ mu_address_get_nth (mu_address_t addr, size_t no, mu_address_t *pret) |
239 | #define AC4(a,b,c,d) a ## b ## c ## d | 239 | #define AC4(a,b,c,d) a ## b ## c ## d |
240 | #define ACCESSOR(action,field) AC4(mu_address_,action,_,field) | 240 | #define ACCESSOR(action,field) AC4(mu_address_,action,_,field) |
241 | 241 | ||
242 | #define DECL_SET(field) \ | 242 | #define DECL_SET(field) \ |
243 | int \ | 243 | int \ |
244 | ACCESSOR(set, field) (mu_address_t addr, size_t no, const char *buf) \ | 244 | ACCESSOR(set, field) (mu_address_t addr, size_t no, const char *buf) \ |
245 | { \ | 245 | { \ |
246 | char *s; \ | 246 | char *s; \ |
247 | mu_address_t subaddr; \ | 247 | mu_address_t subaddr; \ |
248 | \ | 248 | \ |
249 | if (addr == NULL) \ | 249 | if (addr == NULL) \ |
250 | return EINVAL; \ | 250 | return EINVAL; \ |
251 | \ | 251 | \ |
... | @@ -253,22 +253,59 @@ ACCESSOR(set, field) (mu_address_t addr, size_t no, const char *buf) \ | ... | @@ -253,22 +253,59 @@ ACCESSOR(set, field) (mu_address_t addr, size_t no, const char *buf) \ |
253 | if (!subaddr) \ | 253 | if (!subaddr) \ |
254 | return MU_ERR_NOENT; \ | 254 | return MU_ERR_NOENT; \ |
255 | \ | 255 | \ |
256 | s = strdup (buf); \ | 256 | if (buf) \ |
257 | if (!s) \ | 257 | { \ |
258 | return errno; \ | 258 | s = strdup (buf); \ |
259 | \ | 259 | if (!s) \ |
260 | return errno; \ | ||
261 | } \ | ||
262 | else \ | ||
263 | s = (char *) buf; \ | ||
264 | \ | ||
260 | free (subaddr->field); \ | 265 | free (subaddr->field); \ |
261 | subaddr->field = s; \ | 266 | subaddr->field = s; \ |
262 | \ | 267 | \ |
263 | return 0; \ | 268 | return 0; \ |
264 | } | 269 | } |
265 | 270 | ||
271 | #define DECL_SET_EI(field) \ | ||
272 | int \ | ||
273 | ACCESSOR(set, field) (mu_address_t addr, size_t no, const char *buf) \ | ||
274 | { \ | ||
275 | char *s; \ | ||
276 | mu_address_t subaddr; \ | ||
277 | \ | ||
278 | if (addr == NULL) \ | ||
279 | return EINVAL; \ | ||
280 | \ | ||
281 | subaddr = _address_get_nth (addr, no); \ | ||
282 | if (!subaddr) \ | ||
283 | return MU_ERR_NOENT; \ | ||
284 | \ | ||
285 | if (buf) \ | ||
286 | { \ | ||
287 | s = strdup (buf); \ | ||
288 | if (!s) \ | ||
289 | return errno; \ | ||
290 | } \ | ||
291 | else \ | ||
292 | s = (char *) buf; \ | ||
293 | \ | ||
294 | free (subaddr->field); \ | ||
295 | subaddr->field = s; \ | ||
296 | \ | ||
297 | free (subaddr->email); \ | ||
298 | subaddr->email = NULL; \ | ||
299 | \ | ||
300 | return 0; \ | ||
301 | } | ||
302 | |||
266 | #define DECL_SGET(field) \ | 303 | #define DECL_SGET(field) \ |
267 | int \ | 304 | int \ |
268 | ACCESSOR(sget,field) (mu_address_t addr, size_t no, char const **sptr) \ | 305 | ACCESSOR(sget,field) (mu_address_t addr, size_t no, char const **sptr) \ |
269 | { \ | 306 | { \ |
270 | mu_address_t subaddr; \ | 307 | mu_address_t subaddr; \ |
271 | \ | 308 | \ |
272 | if (addr == NULL) \ | 309 | if (addr == NULL) \ |
273 | return EINVAL; \ | 310 | return EINVAL; \ |
274 | \ | 311 | \ |
... | @@ -287,7 +324,7 @@ ACCESSOR(get,field) (mu_address_t addr, size_t no, char *buf, size_t len, \ | ... | @@ -287,7 +324,7 @@ ACCESSOR(get,field) (mu_address_t addr, size_t no, char *buf, size_t len, \ |
287 | size_t i; \ | 324 | size_t i; \ |
288 | const char *str; \ | 325 | const char *str; \ |
289 | int status = ACCESSOR(sget, field) (addr, no, &str); \ | 326 | int status = ACCESSOR(sget, field) (addr, no, &str); \ |
290 | \ | 327 | \ |
291 | if (status) \ | 328 | if (status) \ |
292 | return status; \ | 329 | return status; \ |
293 | \ | 330 | \ |
... | @@ -322,7 +359,13 @@ ACCESSOR(aget, field) (mu_address_t addr, size_t no, char **buf) \ | ... | @@ -322,7 +359,13 @@ ACCESSOR(aget, field) (mu_address_t addr, size_t no, char **buf) \ |
322 | DECL_SET(field) \ | 359 | DECL_SET(field) \ |
323 | DECL_SGET(field) \ | 360 | DECL_SGET(field) \ |
324 | DECL_GET(field) \ | 361 | DECL_GET(field) \ |
325 | DECL_AGET(field) | 362 | DECL_AGET(field) |
363 | |||
364 | #define DECL_ACCESSORS_EI(field) \ | ||
365 | DECL_SET_EI(field) \ | ||
366 | DECL_SGET(field) \ | ||
367 | DECL_GET(field) \ | ||
368 | DECL_AGET(field) | ||
326 | 369 | ||
327 | 370 | ||
328 | 371 | ||
... | @@ -330,15 +373,115 @@ DECL_AGET(field) | ... | @@ -330,15 +373,115 @@ DECL_AGET(field) |
330 | DECL_ACCESSORS(personal) | 373 | DECL_ACCESSORS(personal) |
331 | /* Comments */ | 374 | /* Comments */ |
332 | DECL_ACCESSORS(comments) | 375 | DECL_ACCESSORS(comments) |
333 | /* Email */ | ||
334 | DECL_ACCESSORS(email) | ||
335 | /* Local part */ | 376 | /* Local part */ |
336 | DECL_ACCESSORS(local_part) | 377 | DECL_ACCESSORS_EI(local_part) |
337 | /* Domain */ | 378 | /* Domain */ |
338 | DECL_ACCESSORS(domain) | 379 | DECL_ACCESSORS_EI(domain) |
339 | /* Route */ | 380 | /* Route */ |
340 | DECL_ACCESSORS(route) | 381 | DECL_ACCESSORS(route) |
341 | 382 | ||
383 | /* Email */ | ||
384 | int | ||
385 | mu_address_set_email (mu_address_t addr, size_t no, const char *buf) | ||
386 | { | ||
387 | char *s; | ||
388 | mu_address_t subaddr; | ||
389 | |||
390 | if (addr == NULL) | ||
391 | return EINVAL; | ||
392 | |||
393 | subaddr = _address_get_nth (addr, no); | ||
394 | if (!subaddr) | ||
395 | return MU_ERR_NOENT; | ||
396 | |||
397 | if (buf) | ||
398 | { | ||
399 | s = strdup (buf); | ||
400 | if (!s) | ||
401 | return errno; | ||
402 | } | ||
403 | else | ||
404 | s = (char *) buf; | ||
405 | |||
406 | free (subaddr->email); | ||
407 | subaddr->email = s; | ||
408 | |||
409 | free (subaddr->local_part); | ||
410 | free (subaddr->domain); | ||
411 | if (s) | ||
412 | { | ||
413 | char *p = strchr (subaddr->email, '@'); | ||
414 | if (p) | ||
415 | { | ||
416 | size_t len = p - subaddr->email; | ||
417 | subaddr->local_part = malloc (len + 1); | ||
418 | if (subaddr->local_part) | ||
419 | { | ||
420 | memcpy (subaddr->local_part, p, len); | ||
421 | subaddr->local_part[len] = 0; | ||
422 | } | ||
423 | subaddr->domain = strdup (p + 1); | ||
424 | } | ||
425 | } | ||
426 | else | ||
427 | { | ||
428 | subaddr->local_part = NULL; | ||
429 | subaddr->domain = NULL; | ||
430 | } | ||
431 | |||
432 | return 0; | ||
433 | } | ||
434 | |||
435 | static int | ||
436 | validate_email (mu_address_t subaddr) | ||
437 | { | ||
438 | if (!subaddr->email) | ||
439 | { | ||
440 | if (subaddr->local_part) | ||
441 | { | ||
442 | const char *domain; | ||
443 | |||
444 | if (subaddr->domain) | ||
445 | domain = subaddr->domain; | ||
446 | else | ||
447 | mu_get_user_email_domain (&domain); | ||
448 | if (domain) | ||
449 | { | ||
450 | char *p; | ||
451 | subaddr->email = malloc (strlen (subaddr->local_part) + | ||
452 | strlen (domain) + 2); | ||
453 | if (!subaddr->email) | ||
454 | return ENOMEM; | ||
455 | p = mu_stpcpy (subaddr->email, subaddr->local_part); | ||
456 | *p++ = '@'; | ||
457 | mu_stpcpy (p, (char*) domain); | ||
458 | } | ||
459 | } | ||
460 | } | ||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | int | ||
465 | mu_address_sget_email (mu_address_t addr, size_t no, char const **sptr) | ||
466 | { | ||
467 | mu_address_t subaddr; | ||
468 | |||
469 | if (addr == NULL) | ||
470 | return EINVAL; | ||
471 | |||
472 | subaddr = _address_get_nth (addr, no); | ||
473 | if (!subaddr) | ||
474 | return MU_ERR_NOENT; | ||
475 | |||
476 | validate_email (subaddr); | ||
477 | *sptr = subaddr->email; | ||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | DECL_GET(email) | ||
482 | DECL_AGET(email) | ||
483 | |||
484 | |||
342 | 485 | ||
343 | 486 | ||
344 | #define format_char(c) do {\ | 487 | #define format_char(c) do {\ |
... | @@ -349,7 +492,7 @@ DECL_ACCESSORS(route) | ... | @@ -349,7 +492,7 @@ DECL_ACCESSORS(route) |
349 | }\ | 492 | }\ |
350 | else\ | 493 | else\ |
351 | rc++;\ | 494 | rc++;\ |
352 | } while(0) | 495 | } while(0) |
353 | 496 | ||
354 | #define format_string(str) do {\ | 497 | #define format_string(str) do {\ |
355 | if (buflen) \ | 498 | if (buflen) \ |
... | @@ -361,22 +504,23 @@ DECL_ACCESSORS(route) | ... | @@ -361,22 +504,23 @@ DECL_ACCESSORS(route) |
361 | else\ | 504 | else\ |
362 | rc += strlen (str);\ | 505 | rc += strlen (str);\ |
363 | } while (0) | 506 | } while (0) |
364 | 507 | ||
365 | size_t | 508 | size_t |
366 | mu_address_format_string (mu_address_t addr, char *buf, size_t buflen) | 509 | mu_address_format_string (mu_address_t addr, char *buf, size_t buflen) |
367 | { | 510 | { |
368 | int rc = 0; | 511 | int rc = 0; |
369 | int comma = 0; | 512 | int comma = 0; |
370 | 513 | ||
371 | for (;addr; addr = addr->next) | 514 | for (;addr; addr = addr->next) |
372 | { | 515 | { |
516 | validate_email (addr); | ||
373 | if (addr->email) | 517 | if (addr->email) |
374 | { | 518 | { |
375 | int space = 0; | 519 | int space = 0; |
376 | 520 | ||
377 | if (comma) | 521 | if (comma) |
378 | format_char (','); | 522 | format_char (','); |
379 | 523 | ||
380 | if (addr->personal) | 524 | if (addr->personal) |
381 | { | 525 | { |
382 | format_char ('"'); | 526 | format_char ('"'); |
... | @@ -384,7 +528,7 @@ mu_address_format_string (mu_address_t addr, char *buf, size_t buflen) | ... | @@ -384,7 +528,7 @@ mu_address_format_string (mu_address_t addr, char *buf, size_t buflen) |
384 | format_char ('"'); | 528 | format_char ('"'); |
385 | space++; | 529 | space++; |
386 | } | 530 | } |
387 | 531 | ||
388 | if (addr->comments) | 532 | if (addr->comments) |
389 | { | 533 | { |
390 | if (space) | 534 | if (space) |
... | @@ -394,7 +538,7 @@ mu_address_format_string (mu_address_t addr, char *buf, size_t buflen) | ... | @@ -394,7 +538,7 @@ mu_address_format_string (mu_address_t addr, char *buf, size_t buflen) |
394 | format_char (')'); | 538 | format_char (')'); |
395 | space++; | 539 | space++; |
396 | } | 540 | } |
397 | 541 | ||
398 | if (space) | 542 | if (space) |
399 | format_char (' '); | 543 | format_char (' '); |
400 | format_char ('<'); | 544 | format_char ('<'); |
... | @@ -435,14 +579,14 @@ int | ... | @@ -435,14 +579,14 @@ int |
435 | mu_address_is_group (mu_address_t addr, size_t no, int *yes) | 579 | mu_address_is_group (mu_address_t addr, size_t no, int *yes) |
436 | { | 580 | { |
437 | mu_address_t subaddr; | 581 | mu_address_t subaddr; |
438 | 582 | ||
439 | if (addr == NULL) | 583 | if (addr == NULL) |
440 | return EINVAL; | 584 | return EINVAL; |
441 | 585 | ||
442 | subaddr = _address_get_nth (addr, no); | 586 | subaddr = _address_get_nth (addr, no); |
443 | if (!subaddr) | 587 | if (!subaddr) |
444 | return MU_ERR_NOENT; | 588 | return MU_ERR_NOENT; |
445 | 589 | ||
446 | if (yes) | 590 | if (yes) |
447 | *yes = _address_is_group (subaddr); | 591 | *yes = _address_is_group (subaddr); |
448 | return 0; | 592 | return 0; |
... | @@ -465,7 +609,7 @@ mu_address_to_string (mu_address_t addr, char *buf, size_t len, size_t *n) | ... | @@ -465,7 +609,7 @@ mu_address_to_string (mu_address_t addr, char *buf, size_t len, size_t *n) |
465 | return ENOMEM; | 609 | return ENOMEM; |
466 | mu_address_format_string (addr, addr->addr, i+1); | 610 | mu_address_format_string (addr, addr->addr, i+1); |
467 | } | 611 | } |
468 | 612 | ||
469 | i = mu_cpystr (buf, addr->addr, len); | 613 | i = mu_cpystr (buf, addr->addr, len); |
470 | if (n) | 614 | if (n) |
471 | *n = i; | 615 | *n = i; |
... | @@ -562,12 +706,12 @@ mu_address_dup (mu_address_t src) | ... | @@ -562,12 +706,12 @@ mu_address_dup (mu_address_t src) |
562 | 706 | ||
563 | return dst; | 707 | return dst; |
564 | } | 708 | } |
565 | 709 | ||
566 | int | 710 | int |
567 | mu_address_union (mu_address_t *a, mu_address_t b) | 711 | mu_address_union (mu_address_t *a, mu_address_t b) |
568 | { | 712 | { |
569 | mu_address_t last = NULL; | 713 | mu_address_t last = NULL; |
570 | 714 | ||
571 | if (!a || !b) | 715 | if (!a || !b) |
572 | return EINVAL; | 716 | return EINVAL; |
573 | 717 | ||
... | @@ -601,4 +745,3 @@ mu_address_union (mu_address_t *a, mu_address_t b) | ... | @@ -601,4 +745,3 @@ mu_address_union (mu_address_t *a, mu_address_t b) |
601 | } | 745 | } |
602 | return 0; | 746 | return 0; |
603 | } | 747 | } |
604 | ... | ... |
... | @@ -515,6 +515,10 @@ mu_c_streamftime (mu_stream_t str, const char *fmt, struct tm *input_tm, | ... | @@ -515,6 +515,10 @@ mu_c_streamftime (mu_stream_t str, const char *fmt, struct tm *input_tm, |
515 | /* A literal '%' character. */ | 515 | /* A literal '%' character. */ |
516 | rc = mu_stream_write (str, "%", 1, NULL); | 516 | rc = mu_stream_write (str, "%", 1, NULL); |
517 | break; | 517 | break; |
518 | |||
519 | case '$': | ||
520 | /* Ignored for compatibilty with mu_scan_datetime */ | ||
521 | break; | ||
518 | 522 | ||
519 | case '+': | 523 | case '+': |
520 | /* Not supported (date and time in date(1) format. */ | 524 | /* Not supported (date and time in date(1) format. */ |
... | @@ -849,7 +853,7 @@ mu_scan_datetime (const char *input, const char *fmt, | ... | @@ -849,7 +853,7 @@ mu_scan_datetime (const char *input, const char *fmt, |
849 | rc = MU_ERR_PARSE; | 853 | rc = MU_ERR_PARSE; |
850 | break; | 854 | break; |
851 | 855 | ||
852 | case '?': | 856 | case '$': |
853 | eof_ok = 1; | 857 | eof_ok = 1; |
854 | break; | 858 | break; |
855 | } | 859 | } | ... | ... |
... | @@ -47,7 +47,7 @@ sec=1,min=55,hour=11,mday=11,mon=10,year=111,wday=5,tz=3600 | ... | @@ -47,7 +47,7 @@ sec=1,min=55,hour=11,mday=11,mon=10,year=111,wday=5,tz=3600 |
47 | ]) | 47 | ]) |
48 | 48 | ||
49 | SCANTIME([IMAP search time format],[imap-search], | 49 | SCANTIME([IMAP search time format],[imap-search], |
50 | [%d-%b-%Y%? %H:%M:%S %z], | 50 | [%d-%b-%Y%$ %H:%M:%S %z], |
51 | [03-May-2011 13:25:26 +0100 | 51 | [03-May-2011 13:25:26 +0100 |
52 | 03-May-2011], | 52 | 03-May-2011], |
53 | [sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,tz=3600 | 53 | [sec=26,min=25,hour=13,mday=3,mon=4,year=111,wday=2,tz=3600 | ... | ... |
... | @@ -472,7 +472,7 @@ _fill_response (void *item, void *data) | ... | @@ -472,7 +472,7 @@ _fill_response (void *item, void *data) |
472 | else | 472 | else |
473 | { | 473 | { |
474 | if (mu_scan_datetime (elt->v.string, | 474 | if (mu_scan_datetime (elt->v.string, |
475 | MU_DATETIME_IMAP, | 475 | MU_DATETIME_RFC822, |
476 | &env->envelope->date, | 476 | &env->envelope->date, |
477 | &env->envelope->tz, NULL)) | 477 | &env->envelope->tz, NULL)) |
478 | rc = MU_ERR_FAILURE; | 478 | rc = MU_ERR_FAILURE; | ... | ... |
... | @@ -266,6 +266,9 @@ fetch_response_printer (void *item, void *data) | ... | @@ -266,6 +266,9 @@ fetch_response_printer (void *item, void *data) |
266 | mu_stream_printf (str, "ENVELOPE:\n"); | 266 | mu_stream_printf (str, "ENVELOPE:\n"); |
267 | 267 | ||
268 | format_date (str, "date", &resp->envelope.date, &resp->envelope.tz); | 268 | format_date (str, "date", &resp->envelope.date, &resp->envelope.tz); |
269 | mu_stream_printf (str, " subject = %s\n", | ||
270 | resp->envelope.subject ? | ||
271 | resp->envelope.subject : "NIL"); | ||
269 | 272 | ||
270 | format_email (str, "from", resp->envelope.from); | 273 | format_email (str, "from", resp->envelope.from); |
271 | format_email (str, "sender", resp->envelope.sender); | 274 | format_email (str, "sender", resp->envelope.sender); | ... | ... |
-
Please register or sign in to post a comment