]> diplodocus.org Git - nmh/blob - uip/popsbr.c
Print information about the compiler toolchain on Darwin and FreeBSD.
[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 *++p = NULL;
471 return pargv;
472 }
473
474 int
475 pop_init (char *host, char *port, char *user, char *pass, char *proxy,
476 int snoop, int sasl, char *mech)
477 {
478 int fd1, fd2;
479 char buffer[BUFSIZ];
480 #ifndef CYRUS_SASL
481 NMH_UNUSED (sasl);
482 NMH_UNUSED (mech);
483 #endif /* ! CYRUS_SASL */
484
485 if (proxy && *proxy) {
486 int pid;
487 int inpipe[2]; /* for reading from the server */
488 int outpipe[2]; /* for sending to the server */
489
490 pipe(inpipe);
491 pipe(outpipe);
492
493 pid=fork();
494 if (pid==0) {
495 char **argv;
496
497 /* in child */
498 close(0);
499 close(1);
500 dup2(outpipe[0],0); /* connect read end of connection */
501 dup2(inpipe[1], 1); /* connect write end of connection */
502 if(inpipe[0]>1) close(inpipe[0]);
503 if(inpipe[1]>1) close(inpipe[1]);
504 if(outpipe[0]>1) close(outpipe[0]);
505 if(outpipe[1]>1) close(outpipe[1]);
506
507 /* run the proxy command */
508 argv=parse_proxy(proxy, host);
509 execvp(argv[0],argv);
510
511 perror(argv[0]);
512 close(0);
513 close(1);
514 free(*argv);
515 free(argv);
516 exit(10);
517 }
518
519 /* okay in the parent we do some stuff */
520 close(inpipe[1]); /* child uses this */
521 close(outpipe[0]); /* child uses this */
522
523 /* we read on fd1 */
524 fd1=inpipe[0];
525 /* and write on fd2 */
526 fd2=outpipe[1];
527
528 } else {
529
530 if ((fd1 = client (host, port ? port : "pop3", response,
531 sizeof(response), snoop)) == NOTOK) {
532 return NOTOK;
533 }
534
535 if ((fd2 = dup (fd1)) == NOTOK) {
536 char *s;
537
538 if ((s = strerror(errno)))
539 snprintf (response, sizeof(response),
540 "unable to dup connection descriptor: %s", s);
541 else
542 snprintf (response, sizeof(response),
543 "unable to dup connection descriptor: unknown error");
544 close (fd1);
545 return NOTOK;
546 }
547 }
548 if (pop_set (fd1, fd2, snoop) == NOTOK)
549 return NOTOK;
550
551 SIGNAL (SIGPIPE, SIG_IGN);
552
553 switch (sasl_getline (response, sizeof response, input)) {
554 case OK:
555 if (poprint)
556 fprintf (stderr, "<--- %s\n", response);
557 if (*response == '+') {
558 # ifdef CYRUS_SASL
559 if (sasl) {
560 if (pop_auth_sasl(user, host, mech) != NOTOK)
561 return OK;
562 } else
563 # endif /* CYRUS_SASL */
564 if (command ("USER %s", user) != NOTOK
565 && command ("%s %s", (pophack++, "PASS"),
566 pass) != NOTOK)
567 return OK;
568 }
569 strncpy (buffer, response, sizeof(buffer));
570 command ("QUIT");
571 strncpy (response, buffer, sizeof(response));
572 /* and fall */
573
574 case NOTOK:
575 case DONE:
576 if (poprint)
577 fprintf (stderr, "%s\n", response);
578 fclose (input);
579 fclose (output);
580 return NOTOK;
581 }
582
583 return NOTOK; /* NOTREACHED */
584 }
585
586 int
587 pop_set (int in, int out, int snoop)
588 {
589
590 if ((input = fdopen (in, "r")) == NULL
591 || (output = fdopen (out, "w")) == NULL) {
592 strncpy (response, "fdopen failed on connection descriptor", sizeof(response));
593 if (input)
594 fclose (input);
595 else
596 close (in);
597 close (out);
598 return NOTOK;
599 }
600
601 poprint = snoop;
602
603 return OK;
604 }
605
606
607 int
608 pop_fd (char *in, int inlen, char *out, int outlen)
609 {
610 snprintf (in, inlen, "%d", fileno (input));
611 snprintf (out, outlen, "%d", fileno (output));
612 return OK;
613 }
614
615
616 /*
617 * Find out number of messages available
618 * and their total size.
619 */
620
621 int
622 pop_stat (int *nmsgs, int *nbytes)
623 {
624
625 if (command ("STAT") == NOTOK)
626 return NOTOK;
627
628 *nmsgs = *nbytes = 0;
629 sscanf (response, "+OK %d %d", nmsgs, nbytes);
630
631 return OK;
632 }
633
634
635 int
636 pop_list (int msgno, int *nmsgs, int *msgs, int *bytes)
637 {
638 int i;
639 int *ids = NULL;
640
641 if (msgno) {
642 if (command ("LIST %d", msgno) == NOTOK)
643 return NOTOK;
644 *msgs = *bytes = 0;
645 if (ids) {
646 *ids = 0;
647 sscanf (response, "+OK %d %d %d", msgs, bytes, ids);
648 }
649 else
650 sscanf (response, "+OK %d %d", msgs, bytes);
651 return OK;
652 }
653
654 if (command ("LIST") == NOTOK)
655 return NOTOK;
656
657 for (i = 0; i < *nmsgs; i++)
658 switch (multiline ()) {
659 case NOTOK:
660 return NOTOK;
661 case DONE:
662 *nmsgs = ++i;
663 return OK;
664 case OK:
665 *msgs = *bytes = 0;
666 if (ids) {
667 *ids = 0;
668 sscanf (response, "%d %d %d",
669 msgs++, bytes++, ids++);
670 }
671 else
672 sscanf (response, "%d %d", msgs++, bytes++);
673 break;
674 }
675 for (;;)
676 switch (multiline ()) {
677 case NOTOK:
678 return NOTOK;
679 case DONE:
680 return OK;
681 case OK:
682 break;
683 }
684 }
685
686
687 int
688 pop_retr (int msgno, int (*action)(char *))
689 {
690 return traverse (action, "RETR %d", msgno);
691 }
692
693
694 static int
695 traverse (int (*action)(char *), const char *fmt, ...)
696 {
697 int result;
698 va_list ap;
699 char buffer[sizeof(response)];
700
701 va_start(ap, fmt);
702 result = vcommand (fmt, ap);
703 va_end(ap);
704
705 if (result == NOTOK)
706 return NOTOK;
707 strncpy (buffer, response, sizeof(buffer));
708
709 for (;;)
710 switch (multiline ()) {
711 case NOTOK:
712 return NOTOK;
713
714 case DONE:
715 strncpy (response, buffer, sizeof(response));
716 return OK;
717
718 case OK:
719 (*action) (response);
720 break;
721 }
722 }
723
724
725 int
726 pop_dele (int msgno)
727 {
728 return command ("DELE %d", msgno);
729 }
730
731
732 int
733 pop_noop (void)
734 {
735 return command ("NOOP");
736 }
737
738
739 int
740 pop_rset (void)
741 {
742 return command ("RSET");
743 }
744
745
746 int
747 pop_top (int msgno, int lines, int (*action)(char *))
748 {
749 return traverse (action, "TOP %d %d", msgno, lines);
750 }
751
752
753 int
754 pop_quit (void)
755 {
756 int i;
757
758 i = command ("QUIT");
759 pop_done ();
760
761 return i;
762 }
763
764
765 int
766 pop_done (void)
767 {
768 #ifdef CYRUS_SASL
769 if (conn)
770 sasl_dispose(&conn);
771 #endif /* CYRUS_SASL */
772 fclose (input);
773 fclose (output);
774
775 return OK;
776 }
777
778
779 int
780 command(const char *fmt, ...)
781 {
782 va_list ap;
783 int result;
784
785 va_start(ap, fmt);
786 result = vcommand(fmt, ap);
787 va_end(ap);
788
789 return result;
790 }
791
792
793 static int
794 vcommand (const char *fmt, va_list ap)
795 {
796 char *cp, buffer[65536];
797
798 vsnprintf (buffer, sizeof(buffer), fmt, ap);
799
800 if (poprint) {
801 #ifdef CYRUS_SASL
802 if (sasl_ssf)
803 fprintf(stderr, "(encrypted) ");
804 #endif /* CYRUS_SASL */
805 if (pophack) {
806 if ((cp = strchr (buffer, ' ')))
807 *cp = 0;
808 fprintf (stderr, "---> %s ********\n", buffer);
809 if (cp)
810 *cp = ' ';
811 pophack = 0;
812 }
813 else
814 fprintf (stderr, "---> %s\n", buffer);
815 }
816
817 if (putline (buffer, output) == NOTOK)
818 return NOTOK;
819
820 #ifdef CYRUS_SASL
821 if (poprint && sasl_ssf)
822 fprintf(stderr, "(decrypted) ");
823 #endif /* CYRUS_SASL */
824
825 switch (sasl_getline (response, sizeof response, input)) {
826 case OK:
827 if (poprint)
828 fprintf (stderr, "<--- %s\n", response);
829 return (*response == '+' ? OK : NOTOK);
830
831 case NOTOK:
832 case DONE:
833 if (poprint)
834 fprintf (stderr, "%s\n", response);
835 return NOTOK;
836 }
837
838 return NOTOK; /* NOTREACHED */
839 }
840
841
842 int
843 multiline (void)
844 {
845 char buffer[BUFSIZ + TRMLEN];
846
847 if (sasl_getline (buffer, sizeof buffer, input) != OK)
848 return NOTOK;
849 #ifdef DEBUG
850 if (poprint) {
851 #ifdef CYRUS_SASL
852 if (sasl_ssf)
853 fprintf(stderr, "(decrypted) ");
854 #endif /* CYRUS_SASL */
855 fprintf (stderr, "<--- %s\n", response);
856 }
857 #endif /* DEBUG */
858 if (strncmp (buffer, TRM, TRMLEN) == 0) {
859 if (buffer[TRMLEN] == 0)
860 return DONE;
861 else
862 strncpy (response, buffer + TRMLEN, sizeof(response));
863 }
864 else
865 strncpy (response, buffer, sizeof(response));
866
867 return OK;
868 }
869
870 /*
871 * Note that these functions have been modified to deal with layer encryption
872 * in the SASL case
873 */
874
875 static int
876 sasl_getline (char *s, int n, FILE *iop)
877 {
878 int c = -2;
879 char *p;
880
881 p = s;
882 while (--n > 0 && (c = sasl_fgetc (iop)) != EOF && c != -2)
883 if ((*p++ = c) == '\n')
884 break;
885 if (c == -2)
886 return NOTOK;
887 if (ferror (iop) && c != EOF) {
888 strncpy (response, "error on connection", sizeof(response));
889 return NOTOK;
890 }
891 if (c == EOF && p == s) {
892 strncpy (response, "connection closed by foreign host", sizeof(response));
893 return DONE;
894 }
895 *p = 0;
896 if (*--p == '\n')
897 *p = 0;
898 if (*--p == '\r')
899 *p = 0;
900
901 return OK;
902 }
903
904
905 static int
906 putline (char *s, FILE *iop)
907 {
908 #ifdef CYRUS_SASL
909 char outbuf[BUFSIZ], *buf;
910 int result;
911 unsigned int buflen;
912
913 if (!sasl_complete) {
914 #endif /* CYRUS_SASL */
915 fprintf (iop, "%s\r\n", s);
916 #ifdef CYRUS_SASL
917 } else {
918 /*
919 * Build an output buffer, encrypt it using sasl_encode, and
920 * squirt out the results.
921 */
922 strncpy(outbuf, s, sizeof(outbuf) - 3);
923 outbuf[sizeof(outbuf) - 3] = '\0'; /* Just in case */
924 strcat(outbuf, "\r\n");
925
926 result = sasl_encode(conn, outbuf, strlen(outbuf),
927 (const char **) &buf, &buflen);
928
929 if (result != SASL_OK) {
930 snprintf(response, sizeof(response), "SASL encoding error: %s",
931 sasl_errdetail(conn));
932 return NOTOK;
933 }
934
935 fwrite(buf, buflen, 1, iop);
936 }
937 #endif /* CYRUS_SASL */
938
939 fflush (iop);
940 if (ferror (iop)) {
941 strncpy (response, "lost connection", sizeof(response));
942 return NOTOK;
943 }
944
945 return OK;
946 }
947
948 #ifdef CYRUS_SASL
949 /*
950 * Okay, our little fgetc replacement. Hopefully this is a little more
951 * efficient than the last one.
952 */
953 static int
954 sasl_fgetc(FILE *f)
955 {
956 static unsigned char *buffer = NULL, *ptr;
957 static unsigned int size = 0;
958 static int cnt = 0;
959 unsigned int retbufsize = 0;
960 int cc, result;
961 char *retbuf, tmpbuf[SASL_BUFFER_SIZE];
962
963 /*
964 * If we have some leftover data, return that
965 */
966
967 if (cnt) {
968 cnt--;
969 return (int) *ptr++;
970 }
971
972 /*
973 * Otherwise, fill our buffer until we have some data to return.
974 */
975
976 while (retbufsize == 0) {
977
978 cc = read(fileno(f), tmpbuf, sizeof(tmpbuf));
979
980 if (cc == 0)
981 return EOF;
982
983 if (cc < 0) {
984 snprintf(response, sizeof(response), "Error during read from "
985 "network: %s", strerror(errno));
986 return -2;
987 }
988
989 /*
990 * We're not allowed to call sasl_decode until sasl_complete is
991 * true, so we do these gyrations ...
992 */
993
994 if (!sasl_complete) {
995
996 retbuf = tmpbuf;
997 retbufsize = cc;
998
999 } else {
1000
1001 result = sasl_decode(conn, tmpbuf, cc,
1002 (const char **) &retbuf, &retbufsize);
1003
1004 if (result != SASL_OK) {
1005 snprintf(response, sizeof(response), "Error during SASL "
1006 "decoding: %s", sasl_errdetail(conn));
1007 return -2;
1008 }
1009 }
1010 }
1011
1012 if (retbufsize > size) {
1013 buffer = mh_xrealloc(buffer, retbufsize);
1014 size = retbufsize;
1015 }
1016
1017 memcpy(buffer, retbuf, retbufsize);
1018 ptr = buffer + 1;
1019 cnt = retbufsize - 1;
1020
1021 return (int) buffer[0];
1022 }
1023 #endif /* CYRUS_SASL */