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