static int compare_action (void *item, void *data) { struct comp_op *op = item; struct comp_data *dp = data; char *a, *ap, *b, *bp; mu_header_t h; if (mu_message_get_header (dp->m[0], &h) || mu_header_aget_value (h, op->field, &a)) return 0; if (mu_message_get_header (dp->m[1], &h) || mu_header_aget_value (h, op->field, &b)) { free (a); return 0; } ap = a; bp = b; if (mu_c_strcasecmp (op->field, MU_HEADER_SUBJECT) == 0) { if (mu_c_strncasecmp (ap, "re:", 3) == 0) ap += 3; if (mu_c_strncasecmp (b, "re:", 3) == 0) bp += 3; } dp->r = op->comp (ap, bp); free (a); free (b); return dp->r; /* go on until the difference is found */ }
int mu_message_unencapsulate (mu_message_t msg, mu_message_t *newmsg, mu_mime_io_buffer_t info) { int ret = 0; mu_header_t hdr; mu_stream_t istream; if (msg == NULL) return EINVAL; if (newmsg == NULL) return MU_ERR_OUT_PTR_NULL; if (info == NULL /* FIXME: not needed? */ && (ret = mu_message_get_header (msg, &hdr)) == 0) { const char *s; if (!(mu_header_sget_value (hdr, MU_HEADER_CONTENT_TYPE, &s) == 0 && mu_c_strncasecmp (s, MESSAGE_RFC822_STR, sizeof (MESSAGE_RFC822_STR) - 1) == 0)) return EINVAL; } if ((ret = _attachment_setup (&info, msg, &istream)) != 0) return ret; ret = mu_stream_to_message (istream, &info->msg); mu_stream_unref (istream); if (ret == 0) *newmsg = info->msg; _attachment_free (info, ret && ret != EAGAIN); return ret; }
static pwcheck_fp find_pwcheck (const char *algo, int len) { struct passwd_algo *p; for (p = pwtab; p->algo; p++) if (len == p->len && mu_c_strncasecmp (p->algo, algo, len) == 0) return p->pwcheck; return NULL; }
static char * check_prefix (char *str, const char *prefix) { int pflen = strlen (prefix); if (strlen (str) > pflen && mu_c_strncasecmp (str, prefix, pflen) == 0) return str + pflen; else return NULL; }
/* Open the connection to the server. The server sends an affirmative greeting that may contain a timestamp for APOP. */ int mu_pop3_connect (mu_pop3_t pop3) { int status; /* Sanity checks. */ if (pop3 == NULL) return EINVAL; /* A networking stack. */ if (pop3->carrier == NULL) return EINVAL; /* Enter the pop state machine, and boogy: AUTHORISATION State. */ switch (pop3->state) { default: /* FALLTHROUGH */ /* If pop3 was in an error state going through here should clear it. */ case MU_POP3_NO_STATE: /* If the stream was previoulsy open this is sudden death: for many pop servers, it is important to let them time to remove any locks or move the .user.pop files. This happen when we do close() and immediately open(). For example, the user does not want to read the entire file, and wants to start to read a new message, closing the connection and immediately contacting the server again, and he'll end up having "-ERR Mail Lock busy" or something similar. To prevent this race condition we sleep 2 seconds. You can see this behaviour in an environment where QPopper(Qualcomm POP3 server) is use and the user as a big mailbox. */ status = mu_pop3_disconnect (pop3); if (status != 0) mu_pop3_sleep (2); pop3->state = MU_POP3_CONNECT; case MU_POP3_CONNECT: /* Establish the connection. */ status = mu_stream_open (pop3->carrier); MU_POP3_CHECK_EAGAIN (pop3, status); pop3->acknowledge = 0; pop3->state = MU_POP3_GREETINGS; case MU_POP3_GREETINGS: /* Get the greetings. */ { size_t len = 0; char *right, *left; status = mu_pop3_response (pop3, NULL, 0, &len); MU_POP3_CHECK_EAGAIN (pop3, status); mu_pop3_debug_ack (pop3); if (mu_c_strncasecmp (pop3->ack.buf, "+OK", 3) != 0) { mu_stream_close (pop3->carrier); pop3->state = MU_POP3_NO_STATE; return EACCES; } /* Get the timestamp. */ right = memchr (pop3->ack.buf, '<', len); if (right) { len = len - (right - pop3->ack.buf); left = memchr (right, '>', len); if (left) { len = left - right + 1; pop3->timestamp = calloc (len + 1, 1); if (pop3->timestamp == NULL) { mu_stream_close (pop3->carrier); MU_POP3_CHECK_ERROR (pop3, ENOMEM); } memcpy (pop3->timestamp, right, len); } } pop3->state = MU_POP3_NO_STATE; } } /* End AUTHORISATION state. */ return status; }
/* FIXME: Do we need to escape body line that begins with "From "? This will require reading the body line by line instead of by chunks, considerably hurting perfomance when expunging. But should not this be the responsibility of the client ? */ static int mbox_append_message0 (mu_mailbox_t mailbox, mu_message_t msg, mu_off_t *psize, int is_expunging, int first) { mbox_data_t mud = mailbox->data; int status = 0; size_t n = 0; char nl = '\n'; size_t orig_size = *psize; char *s; switch (mud->state) { case MBOX_NO_STATE: mud->off = 0; mud->state = MBOX_STATE_APPEND_SENDER; case MBOX_STATE_APPEND_SENDER: /* Generate the sender for the "From " separator. */ { char *s; mu_envelope_t envelope = NULL; mu_message_get_envelope (msg, &envelope); status = mu_envelope_aget_sender (envelope, &mud->sender); switch (status) { case 0: break; case EAGAIN: return status; case MU_ERR_NOENT: /* Envelope headers not found: try to guess */ free (mud->sender); mud->sender = NULL; status = restore_sender (msg, mud); if (status == 0) break; default: free (mud->sender); free (mud->date); mud->date = mud->sender = NULL; mud->state = MBOX_NO_STATE; return status; } /* Nuke trailing newline. */ s = strchr (mud->sender, nl); if (s) *s = '\0'; mud->state = MBOX_STATE_APPEND_DATE; } case MBOX_STATE_APPEND_DATE: /* Generate a date for the "From " separator. */ { mu_envelope_t envelope = NULL; const char *envarr[5]; mu_message_get_envelope (msg, &envelope); status = mu_envelope_aget_date (envelope, &mud->date); switch (status) { case 0: break; case EAGAIN: return status; case MU_ERR_NOENT: /* Envelope headers not found: try to guess */ free (mud->date); mud->date = NULL; status = restore_date (msg, mud); if (status == 0) break; default: free (mud->sender); free (mud->date); mud->date = mud->sender = NULL; mud->state = MBOX_NO_STATE; return status; } /* Nuke trailing newline. */ s = strchr (mud->date, nl); if (s) *s = '\0'; /* Write the separator to the mailbox. */ envarr[0] = "From "; envarr[1] = mud->sender; envarr[2] = " "; envarr[3] = mud->date; envarr[4] = "\n"; status = write_array (mailbox->stream, psize, 5, envarr); if (status) break; free (mud->sender); free (mud->date); mud->sender = mud->date = NULL; /* If we are not expunging get the message in one block via the stream message instead of the header/body. This is good for POP where there is no separation between header and body(RETR). */ if (! is_expunging) { mud->state = MBOX_STATE_APPEND_MESSAGE; break; } mud->state = MBOX_STATE_APPEND_HEADER; } case MBOX_STATE_APPEND_HEADER: /* Append the Header. */ { char buffer[1024]; size_t nread = 0; mu_stream_t is = NULL; mu_header_t header = NULL; mu_message_get_header (msg, &header); mu_header_get_stream (header, &is); do { status = mu_stream_readline (is, buffer, sizeof (buffer), mud->off, &nread); if (status != 0) { if (status != EAGAIN) { mud->state = MBOX_NO_STATE; mud->off = 0; } mu_stream_truncate (mailbox->stream, orig_size); return status; } mud->off += nread; if (*buffer == '\n') break; /* We do not copy the Status since it is rewritten by the attribute code below. Ditto for X-UID and X-IMAPBase. FIXME: - We have a problem here the header may not fit the buffer. - Should we skip the IMAP "X-Status"? */ if ((mu_c_strncasecmp (buffer, "Status", 6) == 0) || (mu_c_strncasecmp (buffer, "X-IMAPbase", 10) == 0) /* FIXME: isn't the length of "X-UID" 5, not 4? And this will match X-UID and X-UIDL, is this intended? */ || (mu_c_strncasecmp (buffer, "X-UID", 4) == 0 && (buffer[5] == ':' || buffer[5] == ' ' || buffer[5] == '\t'))) continue; status = mu_stream_write (mailbox->stream, buffer, nread, *psize, &n); if (status) break; *psize += n; } while (nread > 0); mud->off = 0; /* Rewrite the X-IMAPbase marker. */ if (first && is_expunging) { n = sprintf (buffer, "X-IMAPbase: %lu %u\n", (unsigned long) mud->uidvalidity, (unsigned) mud->uidnext); status = mu_stream_write (mailbox->stream, buffer, n, *psize, &n); if (status) break; *psize += n; } mud->state = MBOX_STATE_APPEND_ATTRIBUTE; } case MBOX_STATE_APPEND_ATTRIBUTE: /* Put the new attributes. */ { #define STATUS_PREFIX_LEN (sizeof(MU_HEADER_STATUS) - 1 + 2) char abuf[STATUS_PREFIX_LEN + MU_STATUS_BUF_SIZE + 1]; size_t na = 0; mu_attribute_t attr = NULL; strcpy(abuf, MU_HEADER_STATUS); strcat(abuf, ": "); mu_message_get_attribute (msg, &attr); mu_attribute_to_string (attr, abuf + STATUS_PREFIX_LEN, sizeof(abuf) - STATUS_PREFIX_LEN - 1, &na); strcat (abuf, "\n"); na = strlen (abuf); mu_stream_write (mailbox->stream, abuf, na, *psize, &n); if (status != 0) break; *psize += n; mud->state = MBOX_STATE_APPEND_UID; } case MBOX_STATE_APPEND_UID: /* The new X-UID. */ { char suid[64]; size_t uid = 0; if (is_expunging) { status = mu_message_get_uid (msg, &uid); if (status == EAGAIN) return status; } else uid = mud->uidnext++; if (status == 0 || uid != 0) { n = sprintf (suid, "X-UID: %u\n", (unsigned) uid); /* Put the UID. */ status = mu_stream_write (mailbox->stream, suid, n, *psize, &n); if (status) break; *psize += n; } /* New line separator of the Header. */ status = mu_stream_write (mailbox->stream, &nl , 1, *psize, &n); if (status) break; *psize += n; mud->state = MBOX_STATE_APPEND_BODY; } case MBOX_STATE_APPEND_BODY: /* Append the Body. */ { char buffer[1024]; size_t nread = 0; mu_stream_t is = NULL; mu_body_t body = NULL; mu_message_get_body (msg, &body); mu_body_get_stream (body, &is); do { status = mu_stream_read (is, buffer, sizeof (buffer), mud->off, &nread); if (status != 0) { if (status != EAGAIN) { mud->state = MBOX_NO_STATE; mud->off = 0; } return status; } mud->off += nread; status = mu_stream_write (mailbox->stream, buffer, nread, *psize, &n); if (status) break; *psize += n; } while (nread > 0); mud->off = 0; n = 0; status = mu_stream_write (mailbox->stream, &nl, 1, *psize, &n); if (status) break; *psize += n; } default: break; } /* If not expunging we are taking the stream message. */ if (!is_expunging) { switch (mud->state) { case MBOX_STATE_APPEND_MESSAGE: { /* Append the Message. */ char buffer[1024]; size_t nread = 0; mu_stream_t is = NULL; mu_message_get_stream (msg, &is); do { status = mu_stream_read (is, buffer, sizeof (buffer), mud->off, &nread); if (status != 0) { if (status != EAGAIN) { mud->state = MBOX_NO_STATE; mud->off = 0; } mu_stream_truncate (mailbox->stream, orig_size); return status; } status = mu_stream_write (mailbox->stream, buffer, nread, *psize, &n); if (status) break; mud->off += nread; *psize += n; } while (nread > 0); if (status) break; status = mu_stream_write (mailbox->stream, &nl, 1, *psize, &n); if (status == 0) *psize += n; } default: break; } } /* is_expunging */ mud->state = MBOX_NO_STATE; if (status) mu_stream_truncate (mailbox->stream, orig_size); else mu_stream_flush (mailbox->stream); return status; }
int mu_progmailer_send (struct _mu_progmailer *pm, mu_message_t msg) { int status; mu_stream_t stream = NULL; char buffer[512]; size_t len = 0; int rc; mu_header_t hdr; mu_body_t body; int found_nl = 0; int exit_status; if (!pm || !msg) return EINVAL; mu_message_get_header (msg, &hdr); status = mu_header_get_streamref (hdr, &stream); if (status) { mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR, ("cannot get header stream: %s", mu_strerror (status))); return status; } mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE, ("Sending headers...")); mu_stream_seek (stream, 0, MU_SEEK_SET, NULL); while ((status = mu_stream_readline (stream, buffer, sizeof (buffer), &len)) == 0 && len != 0) { if (mu_c_strncasecmp (buffer, MU_HEADER_FCC, sizeof (MU_HEADER_FCC) - 1)) { mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_PROT, ("Header: %s", buffer)); if (write (pm->fd, buffer, len) == -1) { status = errno; mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR, ("write failed: %s", strerror (status))); break; } } found_nl = (len == 1 && buffer[0] == '\n'); } if (!found_nl) { if (write (pm->fd, "\n", 1) == -1) { status = errno; mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR, ("write failed: %s", strerror (status))); } } mu_stream_destroy (&stream); mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE, ("Sending body...")); mu_message_get_body (msg, &body); status = mu_body_get_streamref (body, &stream); if (status) { mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR, ("cannot get body stream: %s\n", mu_strerror (status))); return status; } mu_stream_seek (stream, 0, MU_SEEK_SET, NULL); while ((status = mu_stream_read (stream, buffer, sizeof (buffer), &len)) == 0 && len != 0) { if (write (pm->fd, buffer, len) == -1) { status = errno; mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR, ("write failed: %s\n", strerror (status))); break; } } mu_body_get_streamref (body, &stream); close (pm->fd); rc = waitpid (pm->pid, &exit_status, 0); if (status == 0) { if (rc < 0) { if (errno == ECHILD) status = 0; else { status = errno; mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR, ("waitpid(%lu) failed: %s\n", (unsigned long) pm->pid, strerror (status))); } } else if (WIFEXITED (exit_status)) { exit_status = WEXITSTATUS (exit_status); mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE, ("%s exited with: %d\n", pm->command, exit_status)); status = (exit_status == 0) ? 0 : MU_ERR_PROCESS_EXITED; } else if (WIFSIGNALED (exit_status)) status = MU_ERR_PROCESS_SIGNALED; else status = MU_ERR_PROCESS_UNKNOWN_FAILURE; } pm->pid = -1; return status; }