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