]> diplodocus.org Git - nmh/blob - uip/popsbr.c
pending-release-notes: add mhshow's "-prefer", and mh-format's %(kibi/kilo)
[nmh] / uip / popsbr.c
1 /*
2 * popsbr.c -- POP client subroutines
3 *
4 * This code is Copyright (c) 2002, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
7 */
8
9 #include <h/mh.h>
10 #include <h/utils.h>
11
12 #ifdef CYRUS_SASL
13 # include <sasl/sasl.h>
14 # include <sasl/saslutil.h>
15 # if SASL_VERSION_FULL < 0x020125
16 /* Cyrus SASL 2.1.25 introduced the sasl_callback_ft prototype,
17 which has an explicit void parameter list, according to best
18 practice. So we need to cast to avoid compile warnings.
19 Provide this prototype for earlier versions. */
20 typedef int (*sasl_callback_ft)();
21 # endif /* SASL_VERSION_FULL < 0x020125 */
22 #endif /* CYRUS_SASL */
23
24 #include <h/popsbr.h>
25 #include <h/signals.h>
26
27 #define TRM "."
28 #define TRMLEN (sizeof TRM - 1)
29
30 static int poprint = 0;
31 static int pophack = 0;
32
33 char response[BUFSIZ];
34
35 static FILE *input;
36 static FILE *output;
37
38 #ifdef CYRUS_SASL
39 static sasl_conn_t *conn; /* SASL connection state */
40 static int sasl_complete = 0; /* Has sasl authentication succeeded? */
41 static int maxoutbuf; /* Maximum output buffer size */
42 static sasl_ssf_t sasl_ssf = 0; /* Security strength factor */
43 static int sasl_get_user(void *, int, const char **, unsigned *);
44 static int sasl_get_pass(sasl_conn_t *, void *, int, sasl_secret_t **);
45 struct pass_context {
46 char *user;
47 char *password;
48 char *host;
49 };
50
51 static sasl_callback_t callbacks[] = {
52 { SASL_CB_USER, (sasl_callback_ft) sasl_get_user, NULL },
53 #define POP_SASL_CB_N_USER 0
54 { SASL_CB_PASS, (sasl_callback_ft) sasl_get_pass, NULL },
55 #define POP_SASL_CB_N_PASS 1
56 { SASL_CB_LOG, NULL, NULL },
57 { SASL_CB_LIST_END, NULL, NULL },
58
59 #define SASL_BUFFER_SIZE 262144
60 };
61 #else /* CYRUS_SASL */
62 # define sasl_fgetc fgetc
63 #endif /* CYRUS_SASL */
64
65 /*
66 * static prototypes
67 */
68
69 static int command(const char *, ...);
70 static int multiline(void);
71
72 #ifdef CYRUS_SASL
73 static int pop_auth_sasl(char *, char *, char *);
74 static int sasl_fgetc(FILE *);
75 #endif /* CYRUS_SASL */
76
77 static int traverse (int (*)(char *), const char *, ...);
78 static int vcommand(const char *, va_list);
79 static int sasl_getline (char *, int, FILE *);
80 static int putline (char *, FILE *);
81
82
83 #ifdef CYRUS_SASL
84 /*
85 * This function implements the AUTH command for various SASL mechanisms
86 *
87 * We do the whole SASL dialog here. If this completes, then we've
88 * authenticated successfully and have (possibly) negotiated a security
89 * layer.
90 */
91
92 #define CHECKB64SIZE(insize, outbuf, outsize) \
93 { size_t wantout = (((insize + 2) / 3) * 4) + 32; \
94 if (wantout > outsize) { \
95 outbuf = mh_xrealloc(outbuf, outsize = wantout); \
96 } \
97 }
98
99 int
100 pop_auth_sasl(char *user, char *host, char *mech)
101 {
102 int result, status, sasl_capability = 0;
103 unsigned int buflen, outlen;
104 char server_mechs[256], *buf, *outbuf = NULL;
105 size_t outbufsize = 0;
106 const char *chosen_mech;
107 sasl_security_properties_t secprops;
108 struct pass_context p_context;
109 sasl_ssf_t *ssf;
110 int *moutbuf;
111
112 /*
113 * First off, we're going to send the CAPA command to see if we can
114 * even support the AUTH command, and if we do, then we'll get a
115 * list of mechanisms the server supports. If we don't support
116 * the CAPA command, then it's unlikely that we will support
117 * SASL
118 */
119
120 if (command("CAPA") == NOTOK) {
121 snprintf(response, sizeof(response),
122 "The POP CAPA command failed; POP server does not "
123 "support SASL");
124 return NOTOK;
125 }
126
127 while ((status = multiline()) != DONE)
128 switch (status) {
129 case NOTOK:
130 return NOTOK;
131 break;
132 case DONE: /* Shouldn't be possible, but just in case */
133 break;
134 case OK:
135 if (strncasecmp(response, "SASL ", 5) == 0) {
136 /*
137 * We've seen the SASL capability. Grab the mech list
138 */
139 sasl_capability++;
140 strncpy(server_mechs, response + 5, sizeof(server_mechs));
141 }
142 break;
143 }
144
145 if (!sasl_capability) {
146 snprintf(response, sizeof(response), "POP server does not support "
147 "SASL");
148 return NOTOK;
149 }
150
151 /*
152 * If we received a preferred mechanism, see if the server supports it.
153 */
154
155 if (mech && stringdex(mech, server_mechs) == -1) {
156 snprintf(response, sizeof(response), "Requested SASL mech \"%s\" is "
157 "not in list of supported mechanisms:\n%s",
158 mech, server_mechs);
159 return NOTOK;
160 }
161
162 /*
163 * Start the SASL process. First off, initialize the SASL library.
164 */
165
166 callbacks[POP_SASL_CB_N_USER].context = user;
167 p_context.user = user;
168 p_context.host = host;
169 callbacks[POP_SASL_CB_N_PASS].context = &p_context;
170
171 result = sasl_client_init(callbacks);
172
173 if (result != SASL_OK) {
174 snprintf(response, sizeof(response), "SASL library initialization "
175 "failed: %s", sasl_errstring(result, NULL, NULL));
176 return NOTOK;
177 }
178
179 result = sasl_client_new("pop", host, NULL, NULL, NULL, 0, &conn);
180
181 if (result != SASL_OK) {
182 snprintf(response, sizeof(response), "SASL client initialization "
183 "failed: %s", sasl_errstring(result, NULL, NULL));
184 return NOTOK;
185 }
186
187 /*
188 * Initialize the security properties
189 */
190
191 memset(&secprops, 0, sizeof(secprops));
192 secprops.maxbufsize = SASL_BUFFER_SIZE;
193 secprops.max_ssf = UINT_MAX;
194
195 result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
196
197 if (result != SASL_OK) {
198 snprintf(response, sizeof(response), "SASL security property "
199 "initialization failed: %s", sasl_errdetail(conn));
200 return NOTOK;
201 }
202
203 /*
204 * Start the actual protocol. Feed the mech list into the library
205 * and get out a possible initial challenge
206 */
207
208 result = sasl_client_start(conn,
209 (const char *) (mech ? mech : server_mechs),
210 NULL, (const char **) &buf,
211 &buflen, &chosen_mech);
212
213 if (result != SASL_OK && result != SASL_CONTINUE) {
214 snprintf(response, sizeof(response), "SASL client start failed: %s",
215 sasl_errdetail(conn));
216 return NOTOK;
217 }
218
219 if (buflen) {
220 CHECKB64SIZE(buflen, outbuf, outbufsize);
221 status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL);
222 if (status != SASL_OK) {
223 snprintf(response, sizeof(response), "SASL base64 encode "
224 "failed: %s", sasl_errstring(status, NULL, NULL));
225 if (outbuf)
226 free(outbuf);
227 return NOTOK;
228 }
229
230 status = command("AUTH %s %s", chosen_mech, outbuf);
231 } else
232 status = command("AUTH %s", chosen_mech);
233
234 while (result == SASL_CONTINUE) {
235 size_t inlen;
236
237 if (status == NOTOK) {
238 if (outbuf)
239 free(outbuf);
240 return NOTOK;
241 }
242
243 /*
244 * If we get a "+OK" prefix to our response, then we should
245 * exit out of this exchange now (because authenticated should
246 * have succeeded)
247 */
248
249 if (strncmp(response, "+OK", 3) == 0)
250 break;
251
252 /*
253 * Otherwise, make sure the server challenge is correctly formatted
254 */
255
256 if (strncmp(response, "+ ", 2) != 0) {
257 command("*");
258 snprintf(response, sizeof(response),
259 "Malformed authentication message from server");
260 if (outbuf)
261 free(outbuf);
262 return NOTOK;
263 }
264
265 /*
266 * For decode, it will always be shorter, so just make sure
267 * that outbuf is as at least as big as the encoded response.
268 */
269
270 inlen = strlen(response + 2);
271
272 if (inlen > outbufsize) {
273 outbuf = mh_xrealloc(outbuf, outbufsize = inlen);
274 }
275
276 result = sasl_decode64(response + 2, strlen(response + 2),
277 outbuf, outbufsize, &outlen);
278
279 if (result != SASL_OK) {
280 command("*");
281 snprintf(response, sizeof(response), "SASL base64 decode "
282 "failed: %s", sasl_errstring(result, NULL, NULL));
283 if (outbuf)
284 free(outbuf);
285 return NOTOK;
286 }
287
288 result = sasl_client_step(conn, outbuf, outlen, NULL,
289 (const char **) &buf, &buflen);
290
291 if (result != SASL_OK && result != SASL_CONTINUE) {
292 command("*");
293 snprintf(response, sizeof(response), "SASL client negotiaton "
294 "failed: %s", sasl_errdetail(conn));
295 if (outbuf)
296 free(outbuf);
297 return NOTOK;
298 }
299
300 CHECKB64SIZE(buflen, outbuf, outbufsize);
301
302 status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL);
303
304 if (status != SASL_OK) {
305 command("*");
306 snprintf(response, sizeof(response), "SASL base64 encode "
307 "failed: %s", sasl_errstring(status, NULL, NULL));
308 if (outbuf)
309 free(outbuf);
310 return NOTOK;
311 }
312
313 status = command(outbuf);
314 }
315
316 if (outbuf)
317 free(outbuf);
318
319 /*
320 * If we didn't get a positive final response, then error out
321 * (that probably means we failed an authorization check).
322 */
323
324 if (status != OK)
325 return NOTOK;
326
327 /*
328 * We _should_ be okay now. Get a few properties now that negotiation
329 * has completed.
330 */
331
332 result = sasl_getprop(conn, SASL_MAXOUTBUF, (const void **) &moutbuf);
333
334 if (result != SASL_OK) {
335 snprintf(response, sizeof(response), "Cannot retrieve SASL negotiated "
336 "output buffer size: %s", sasl_errdetail(conn));
337 return NOTOK;
338 }
339
340 maxoutbuf = *moutbuf;
341
342 result = sasl_getprop(conn, SASL_SSF, (const void **) &ssf);
343
344 sasl_ssf = *ssf;
345
346 if (result != SASL_OK) {
347 snprintf(response, sizeof(response), "Cannot retrieve SASL negotiated "
348 "security strength factor: %s", sasl_errdetail(conn));
349 return NOTOK;
350 }
351
352 /*
353 * Limit this to what we can deal with. Shouldn't matter much because
354 * this is only outgoing data (which should be small)
355 */
356
357 if (maxoutbuf == 0 || maxoutbuf > BUFSIZ)
358 maxoutbuf = BUFSIZ;
359
360 sasl_complete = 1;
361
362 return status;
363 }
364
365 /*
366 * Callback to return the userid sent down via the user parameter
367 */
368
369 static int
370 sasl_get_user(void *context, int id, const char **result, unsigned *len)
371 {
372 char *user = (char *) context;
373
374 if (! result || id != SASL_CB_USER)
375 return SASL_BADPARAM;
376
377 *result = user;
378 if (len)
379 *len = strlen(user);
380
381 return SASL_OK;
382 }
383
384 /*
385 * Callback to return the password (we call ruserpass, which can get it
386 * out of the .netrc
387 */
388
389 static int
390 sasl_get_pass(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret)
391 {
392 struct pass_context *p_context = (struct pass_context *) context;
393 struct nmh_creds creds = { 0, 0, 0 };
394 int len;
395
396 NMH_UNUSED (conn);
397
398 if (! psecret || id != SASL_CB_PASS)
399 return SASL_BADPARAM;
400
401 if (creds.password == NULL) {
402 /*
403 * Pass the 0 third argument to nmh_get_credentials() so
404 * that the default password isn't used. With legacy/.netrc
405 * credentials support, we'll only get here if the -user
406 * switch to send(1)/post(8) wasn't used.
407 */
408 if (nmh_get_credentials (p_context->host, p_context->user, 0, &creds)
409 != OK) {
410 return SASL_BADPARAM;
411 }
412 }
413
414 len = strlen (creds.password);
415
416 *psecret = (sasl_secret_t *) mh_xmalloc(sizeof(sasl_secret_t) + len);
417
418 (*psecret)->len = len;
419 strcpy((char *) (*psecret)->data, creds.password);
420
421 return SASL_OK;
422 }
423 #endif /* CYRUS_SASL */
424
425
426 /*
427 * Split string containing proxy command into an array of arguments
428 * suitable for passing to exec. Returned array must be freed. Shouldn't
429 * be possible to call this with host set to NULL.
430 */
431 char **
432 parse_proxy(char *proxy, char *host)
433 {
434 char **pargv, **p;
435 int pargc = 2;
436 int hlen = strlen(host);
437 int plen = 1;
438 unsigned char *cur, *pro;
439 char *c;
440
441 /* skip any initial space */
442 for (pro = (unsigned char *) proxy; isspace(*pro); pro++)
443 continue;
444
445 /* calculate required size for argument array */
446 for (cur = pro; *cur; cur++) {
447 if (isspace(*cur) && cur[1] && !isspace(cur[1]))
448 plen++, pargc++;
449 else if (*cur == '%' && cur[1] == 'h') {
450 plen += hlen;
451 cur++;
452 } else if (!isspace(*cur))
453 plen++;
454 }
455
456 /* put together list of arguments */
457 p = pargv = mh_xmalloc(pargc * sizeof(char *));
458 c = *pargv = mh_xmalloc(plen * sizeof(char));
459 for (cur = pro; *cur; cur++) {
460 if (isspace(*cur) && cur[1] && !isspace(cur[1])) {
461 *c++ = '\0';
462 *++p = c;
463 } else if (*cur == '%' && cur[1] == 'h') {
464 strcpy (c, host);
465 c += hlen;
466 cur++;
467 } else if (!isspace(*cur))
468 *c++ = *cur;
469 }
470 *c = '\0';
471 *++p = NULL;
472 return pargv;
473 }
474
475 int
476 pop_init (char *host, char *port, char *user, char *pass, char *proxy,
477 int snoop, int sasl, char *mech)
478 {
479 int fd1, fd2;
480 char buffer[BUFSIZ];
481 #ifndef CYRUS_SASL
482 NMH_UNUSED (sasl);
483 NMH_UNUSED (mech);
484 #endif /* ! CYRUS_SASL */
485
486 if (proxy && *proxy) {
487 int pid;
488 int inpipe[2]; /* for reading from the server */
489 int outpipe[2]; /* for sending to the server */
490
491 if (pipe(inpipe) < 0) {
492 adios ("inpipe", "pipe");
493 }
494 if (pipe(outpipe) < 0) {
495 adios ("outpipe", "pipe");
496 }
497
498 pid=fork();
499 if (pid==0) {
500 char **argv;
501
502 /* in child */
503 close(0);
504 close(1);
505 dup2(outpipe[0],0); /* connect read end of connection */
506 dup2(inpipe[1], 1); /* connect write end of connection */
507 if(inpipe[0]>1) close(inpipe[0]);
508 if(inpipe[1]>1) close(inpipe[1]);
509 if(outpipe[0]>1) close(outpipe[0]);
510 if(outpipe[1]>1) close(outpipe[1]);
511
512 /* run the proxy command */
513 argv=parse_proxy(proxy, host);
514 execvp(argv[0],argv);
515
516 perror(argv[0]);
517 close(0);
518 close(1);
519 free(*argv);
520 free(argv);
521 exit(10);
522 }
523
524 /* okay in the parent we do some stuff */
525 close(inpipe[1]); /* child uses this */
526 close(outpipe[0]); /* child uses this */
527
528 /* we read on fd1 */
529 fd1=inpipe[0];
530 /* and write on fd2 */
531 fd2=outpipe[1];
532
533 } else {
534
535 if ((fd1 = client (host, port ? port : "pop3", response,
536 sizeof(response), snoop)) == NOTOK) {
537 return NOTOK;
538 }
539
540 if ((fd2 = dup (fd1)) == NOTOK) {
541 char *s;
542
543 if ((s = strerror(errno)))
544 snprintf (response, sizeof(response),
545 "unable to dup connection descriptor: %s", s);
546 else
547 snprintf (response, sizeof(response),
548 "unable to dup connection descriptor: unknown error");
549 close (fd1);
550 return NOTOK;
551 }
552 }
553 if (pop_set (fd1, fd2, snoop) == NOTOK)
554 return NOTOK;
555
556 SIGNAL (SIGPIPE, SIG_IGN);
557
558 switch (sasl_getline (response, sizeof response, input)) {
559 case OK:
560 if (poprint)
561 fprintf (stderr, "<--- %s\n", response);
562 if (*response == '+') {
563 # ifdef CYRUS_SASL
564 if (sasl) {
565 if (pop_auth_sasl(user, host, mech) != NOTOK)
566 return OK;
567 } else
568 # endif /* CYRUS_SASL */
569 if (command ("USER %s", user) != NOTOK
570 && command ("%s %s", (pophack++, "PASS"),
571 pass) != NOTOK)
572 return OK;
573 }
574 strncpy (buffer, response, sizeof(buffer));
575 command ("QUIT");
576 strncpy (response, buffer, sizeof(response));
577 /* and fall */
578
579 case NOTOK:
580 case DONE:
581 if (poprint)
582 fprintf (stderr, "%s\n", response);
583 fclose (input);
584 fclose (output);
585 return NOTOK;
586 }
587
588 return NOTOK; /* NOTREACHED */
589 }
590
591 int
592 pop_set (int in, int out, int snoop)
593 {
594
595 if ((input = fdopen (in, "r")) == NULL
596 || (output = fdopen (out, "w")) == NULL) {
597 strncpy (response, "fdopen failed on connection descriptor", sizeof(response));
598 if (input)
599 fclose (input);
600 else
601 close (in);
602 close (out);
603 return NOTOK;
604 }
605
606 poprint = snoop;
607
608 return OK;
609 }
610
611
612 int
613 pop_fd (char *in, int inlen, char *out, int outlen)
614 {
615 snprintf (in, inlen, "%d", fileno (input));
616 snprintf (out, outlen, "%d", fileno (output));
617 return OK;
618 }
619
620
621 /*
622 * Find out number of messages available
623 * and their total size.
624 */
625
626 int
627 pop_stat (int *nmsgs, int *nbytes)
628 {
629
630 if (command ("STAT") == NOTOK)
631 return NOTOK;
632
633 *nmsgs = *nbytes = 0;
634 sscanf (response, "+OK %d %d", nmsgs, nbytes);
635
636 return OK;
637 }
638
639
640 int
641 pop_list (int msgno, int *nmsgs, int *msgs, int *bytes)
642 {
643 int i;
644 int *ids = NULL;
645
646 if (msgno) {
647 if (command ("LIST %d", msgno) == NOTOK)
648 return NOTOK;
649 *msgs = *bytes = 0;
650 if (ids) {
651 *ids = 0;
652 sscanf (response, "+OK %d %d %d", msgs, bytes, ids);
653 }
654 else
655 sscanf (response, "+OK %d %d", msgs, bytes);
656 return OK;
657 }
658
659 if (command ("LIST") == NOTOK)
660 return NOTOK;
661
662 for (i = 0; i < *nmsgs; i++)
663 switch (multiline ()) {
664 case NOTOK:
665 return NOTOK;
666 case DONE:
667 *nmsgs = ++i;
668 return OK;
669 case OK:
670 *msgs = *bytes = 0;
671 if (ids) {
672 *ids = 0;
673 sscanf (response, "%d %d %d",
674 msgs++, bytes++, ids++);
675 }
676 else
677 sscanf (response, "%d %d", msgs++, bytes++);
678 break;
679 }
680 for (;;)
681 switch (multiline ()) {
682 case NOTOK:
683 return NOTOK;
684 case DONE:
685 return OK;
686 case OK:
687 break;
688 }
689 }
690
691
692 int
693 pop_retr (int msgno, int (*action)(char *))
694 {
695 return traverse (action, "RETR %d", msgno);
696 }
697
698
699 static int
700 traverse (int (*action)(char *), const char *fmt, ...)
701 {
702 int result;
703 va_list ap;
704 char buffer[sizeof(response)];
705
706 va_start(ap, fmt);
707 result = vcommand (fmt, ap);
708 va_end(ap);
709
710 if (result == NOTOK)
711 return NOTOK;
712 strncpy (buffer, response, sizeof(buffer));
713
714 for (;;)
715 switch (multiline ()) {
716 case NOTOK:
717 return NOTOK;
718
719 case DONE:
720 strncpy (response, buffer, sizeof(response));
721 return OK;
722
723 case OK:
724 (*action) (response);
725 break;
726 }
727 }
728
729
730 int
731 pop_dele (int msgno)
732 {
733 return command ("DELE %d", msgno);
734 }
735
736
737 int
738 pop_noop (void)
739 {
740 return command ("NOOP");
741 }
742
743
744 int
745 pop_rset (void)
746 {
747 return command ("RSET");
748 }
749
750
751 int
752 pop_top (int msgno, int lines, int (*action)(char *))
753 {
754 return traverse (action, "TOP %d %d", msgno, lines);
755 }
756
757
758 int
759 pop_quit (void)
760 {
761 int i;
762
763 i = command ("QUIT");
764 pop_done ();
765
766 return i;
767 }
768
769
770 int
771 pop_done (void)
772 {
773 #ifdef CYRUS_SASL
774 if (conn)
775 sasl_dispose(&conn);
776 #endif /* CYRUS_SASL */
777 fclose (input);
778 fclose (output);
779
780 return OK;
781 }
782
783
784 int
785 command(const char *fmt, ...)
786 {
787 va_list ap;
788 int result;
789
790 va_start(ap, fmt);
791 result = vcommand(fmt, ap);
792 va_end(ap);
793
794 return result;
795 }
796
797
798 static int
799 vcommand (const char *fmt, va_list ap)
800 {
801 char *cp, buffer[65536];
802
803 vsnprintf (buffer, sizeof(buffer), fmt, ap);
804
805 if (poprint) {
806 #ifdef CYRUS_SASL
807 if (sasl_ssf)
808 fprintf(stderr, "(encrypted) ");
809 #endif /* CYRUS_SASL */
810 if (pophack) {
811 if ((cp = strchr (buffer, ' ')))
812 *cp = 0;
813 fprintf (stderr, "---> %s ********\n", buffer);
814 if (cp)
815 *cp = ' ';
816 pophack = 0;
817 }
818 else
819 fprintf (stderr, "---> %s\n", buffer);
820 }
821
822 if (putline (buffer, output) == NOTOK)
823 return NOTOK;
824
825 #ifdef CYRUS_SASL
826 if (poprint && sasl_ssf)
827 fprintf(stderr, "(decrypted) ");
828 #endif /* CYRUS_SASL */
829
830 switch (sasl_getline (response, sizeof response, input)) {
831 case OK:
832 if (poprint)
833 fprintf (stderr, "<--- %s\n", response);
834 return (*response == '+' ? OK : NOTOK);
835
836 case NOTOK:
837 case DONE:
838 if (poprint)
839 fprintf (stderr, "%s\n", response);
840 return NOTOK;
841 }
842
843 return NOTOK; /* NOTREACHED */
844 }
845
846
847 int
848 multiline (void)
849 {
850 char buffer[BUFSIZ + TRMLEN];
851
852 if (sasl_getline (buffer, sizeof buffer, input) != OK)
853 return NOTOK;
854 #ifdef DEBUG
855 if (poprint) {
856 #ifdef CYRUS_SASL
857 if (sasl_ssf)
858 fprintf(stderr, "(decrypted) ");
859 #endif /* CYRUS_SASL */
860 fprintf (stderr, "<--- %s\n", response);
861 }
862 #endif /* DEBUG */
863 if (strncmp (buffer, TRM, TRMLEN) == 0) {
864 if (buffer[TRMLEN] == 0)
865 return DONE;
866 else
867 strncpy (response, buffer + TRMLEN, sizeof(response));
868 }
869 else
870 strncpy (response, buffer, sizeof(response));
871
872 return OK;
873 }
874
875 /*
876 * Note that these functions have been modified to deal with layer encryption
877 * in the SASL case
878 */
879
880 static int
881 sasl_getline (char *s, int n, FILE *iop)
882 {
883 int c = -2;
884 char *p;
885
886 p = s;
887 while (--n > 0 && (c = sasl_fgetc (iop)) != EOF && c != -2)
888 if ((*p++ = c) == '\n')
889 break;
890 if (c == -2)
891 return NOTOK;
892 if (ferror (iop) && c != EOF) {
893 strncpy (response, "error on connection", sizeof(response));
894 return NOTOK;
895 }
896 if (c == EOF && p == s) {
897 strncpy (response, "connection closed by foreign host", sizeof(response));
898 return DONE;
899 }
900 *p = 0;
901 if (*--p == '\n')
902 *p = 0;
903 if (p > s && *--p == '\r')
904 *p = 0;
905
906 return OK;
907 }
908
909
910 static int
911 putline (char *s, FILE *iop)
912 {
913 #ifdef CYRUS_SASL
914 char outbuf[BUFSIZ], *buf;
915 int result;
916 unsigned int buflen;
917
918 if (!sasl_complete) {
919 #endif /* CYRUS_SASL */
920 fprintf (iop, "%s\r\n", s);
921 #ifdef CYRUS_SASL
922 } else {
923 /*
924 * Build an output buffer, encrypt it using sasl_encode, and
925 * squirt out the results.
926 */
927 strncpy(outbuf, s, sizeof(outbuf) - 3);
928 outbuf[sizeof(outbuf) - 3] = '\0'; /* Just in case */
929 strcat(outbuf, "\r\n");
930
931 result = sasl_encode(conn, outbuf, strlen(outbuf),
932 (const char **) &buf, &buflen);
933
934 if (result != SASL_OK) {
935 snprintf(response, sizeof(response), "SASL encoding error: %s",
936 sasl_errdetail(conn));
937 return NOTOK;
938 }
939
940 if (fwrite(buf, buflen, 1, iop) < 1) {
941 advise ("putline", "fwrite");
942 }
943 }
944 #endif /* CYRUS_SASL */
945
946 fflush (iop);
947 if (ferror (iop)) {
948 strncpy (response, "lost connection", sizeof(response));
949 return NOTOK;
950 }
951
952 return OK;
953 }
954
955 #ifdef CYRUS_SASL
956 /*
957 * Okay, our little fgetc replacement. Hopefully this is a little more
958 * efficient than the last one.
959 */
960 static int
961 sasl_fgetc(FILE *f)
962 {
963 static unsigned char *buffer = NULL, *ptr;
964 static unsigned int size = 0;
965 static int cnt = 0;
966 unsigned int retbufsize = 0;
967 int cc, result;
968 char *retbuf, tmpbuf[SASL_BUFFER_SIZE];
969
970 /*
971 * If we have some leftover data, return that
972 */
973
974 if (cnt) {
975 cnt--;
976 return (int) *ptr++;
977 }
978
979 /*
980 * Otherwise, fill our buffer until we have some data to return.
981 */
982
983 while (retbufsize == 0) {
984
985 cc = read(fileno(f), tmpbuf, sizeof(tmpbuf));
986
987 if (cc == 0)
988 return EOF;
989
990 if (cc < 0) {
991 snprintf(response, sizeof(response), "Error during read from "
992 "network: %s", strerror(errno));
993 return -2;
994 }
995
996 /*
997 * We're not allowed to call sasl_decode until sasl_complete is
998 * true, so we do these gyrations ...
999 */
1000
1001 if (!sasl_complete) {
1002
1003 retbuf = tmpbuf;
1004 retbufsize = cc;
1005
1006 } else {
1007
1008 result = sasl_decode(conn, tmpbuf, cc,
1009 (const char **) &retbuf, &retbufsize);
1010
1011 if (result != SASL_OK) {
1012 snprintf(response, sizeof(response), "Error during SASL "
1013 "decoding: %s", sasl_errdetail(conn));
1014 return -2;
1015 }
1016 }
1017 }
1018
1019 if (retbufsize > size) {
1020 buffer = mh_xrealloc(buffer, retbufsize);
1021 size = retbufsize;
1022 }
1023
1024 memcpy(buffer, retbuf, retbufsize);
1025 ptr = buffer + 1;
1026 cnt = retbufsize - 1;
1027
1028 return (int) buffer[0];
1029 }
1030 #endif /* CYRUS_SASL */