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