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