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