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