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