EXPORTED int dlist_parsemap(struct dlist **dlp, int parsekey, const char *base, unsigned len) { struct protstream *stream; int c; struct dlist *dl = NULL; stream = prot_readmap(base, len); prot_setisclient(stream, 1); /* don't sync literals */ c = dlist_parse(&dl, parsekey, stream, NULL); prot_free(stream); if (c != EOF) { dlist_free(&dl); return IMAP_IOERROR; /* failed to slurp entire buffer */ } *dlp = dl; return 0; }
static void replica_connect(const char *channel) { int wait; struct protoent *proto; sasl_callback_t *cb; int timeout; const char *port, *auth_status = NULL; cb = mysasl_callbacks(NULL, get_config(channel, "sync_authname"), get_config(channel, "sync_realm"), get_config(channel, "sync_password")); /* get the right port */ port = get_config(channel, "sync_port"); if (port) { imap_csync_protocol.service = port; csync_protocol.service = port; } for (wait = 15;; wait *= 2) { sync_backend = backend_connect(sync_backend, servername, &imap_csync_protocol, "", cb, &auth_status, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend) { if (sync_backend->capability & CAPA_REPLICATION) { /* attach our IMAP tag buffer to our protstreams as userdata */ sync_backend->in->userdata = sync_backend->out->userdata = &tagbuf; break; } else { backend_disconnect(sync_backend); sync_backend = NULL; } } sync_backend = backend_connect(sync_backend, servername, &csync_protocol, "", cb, NULL, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend || auth_status || connect_once || wait > 1000) break; fprintf(stderr, "Can not connect to server '%s', retrying in %d seconds\n", servername, wait); sleep(wait); } free_callbacks(cb); cb = NULL; if (!sync_backend) { fprintf(stderr, "Can not connect to server '%s'\n", servername); syslog(LOG_ERR, "Can not connect to server '%s'", servername); _exit(1); } /* Disable Nagle's Algorithm => increase throughput * * http://en.wikipedia.org/wiki/Nagle's_algorithm */ if (servername[0] != '/') { if (sync_backend->sock >= 0 && (proto = getprotobyname("tcp")) != NULL) { int on = 1; if (setsockopt(sync_backend->sock, proto->p_proto, TCP_NODELAY, (void *) &on, sizeof(on)) != 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_NODELAY): %m"); } /* turn on TCP keepalive if set */ if (config_getswitch(IMAPOPT_TCP_KEEPALIVE)) { int r; int optval = 1; socklen_t optlen = sizeof(optval); struct protoent *proto = getprotobyname("TCP"); r = setsockopt(sync_backend->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(SO_KEEPALIVE): %m"); } #ifdef TCP_KEEPCNT optval = config_getint(IMAPOPT_TCP_KEEPALIVE_CNT); if (optval) { r = setsockopt(sync_backend->sock, proto->p_proto, TCP_KEEPCNT, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_KEEPCNT): %m"); } } #endif #ifdef TCP_KEEPIDLE optval = config_getint(IMAPOPT_TCP_KEEPALIVE_IDLE); if (optval) { r = setsockopt(sync_backend->sock, proto->p_proto, TCP_KEEPIDLE, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_KEEPIDLE): %m"); } } #endif #ifdef TCP_KEEPINTVL optval = config_getint(IMAPOPT_TCP_KEEPALIVE_INTVL); if (optval) { r = setsockopt(sync_backend->sock, proto->p_proto, TCP_KEEPINTVL, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_KEEPINTVL): %m"); } } #endif } } else { syslog(LOG_ERR, "unable to getprotobyname(\"tcp\"): %m"); } } #ifdef HAVE_ZLIB /* Does the backend support compression? */ if (CAPA(sync_backend, CAPA_COMPRESS)) { prot_printf(sync_backend->out, "%s\r\n", sync_backend->prot->u.std.compress_cmd.cmd); prot_flush(sync_backend->out); if (sync_parse_response("COMPRESS", sync_backend->in, NULL)) { if (do_compress) fatal("Failed to enable compression, aborting", EC_SOFTWARE); syslog(LOG_NOTICE, "Failed to enable compression, continuing uncompressed"); } else { prot_setcompress(sync_backend->in); prot_setcompress(sync_backend->out); } } else if (do_compress) fatal("Backend does not support compression, aborting", EC_SOFTWARE); #endif /* links to sockets */ sync_in = sync_backend->in; sync_out = sync_backend->out; if (verbose > 1) { prot_setlog(sync_in, fileno(stderr)); prot_setlog(sync_out, fileno(stderr)); } /* Set inactivity timer */ timeout = config_getint(IMAPOPT_SYNC_TIMEOUT); if (timeout < 3) timeout = 3; prot_settimeout(sync_in, timeout); /* Force use of LITERAL+ so we don't need two way communications */ prot_setisclient(sync_in, 1); prot_setisclient(sync_out, 1); }
struct backend *backend_connect(struct backend *ret_backend, const char *server, struct protocol_t *prot, const char *userid, sasl_callback_t *cb, const char **auth_status) { /* need to (re)establish connection to server or create one */ int sock = -1; int r; int err = -1; int ask = 1; /* should we explicitly ask for capabilities? */ struct addrinfo hints, *res0 = NULL, *res; struct sockaddr_un sunsock; char buf[2048]; struct sigaction action; struct backend *ret; char rsessionid[MAX_SESSIONID_SIZE]; if (!ret_backend) { ret = xzmalloc(sizeof(struct backend)); strlcpy(ret->hostname, server, sizeof(ret->hostname)); ret->timeout = NULL; } else ret = ret_backend; if (server[0] == '/') { /* unix socket */ res0 = &hints; memset(res0, 0, sizeof(struct addrinfo)); res0->ai_family = PF_UNIX; res0->ai_socktype = SOCK_STREAM; res0->ai_addr = (struct sockaddr *) &sunsock; res0->ai_addrlen = sizeof(sunsock.sun_family) + strlen(server) + 1; #ifdef SIN6_LEN res0->ai_addrlen += sizeof(sunsock.sun_len); sunsock.sun_len = res0->ai_addrlen; #endif sunsock.sun_family = AF_UNIX; strlcpy(sunsock.sun_path, server, sizeof(sunsock.sun_path)); /* XXX set that we are preauthed */ /* change hostname to 'config_servername' */ strlcpy(ret->hostname, config_servername, sizeof(ret->hostname)); } else { /* inet socket */ memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(server, prot->service, &hints, &res0); if (err) { syslog(LOG_ERR, "getaddrinfo(%s) failed: %s", server, gai_strerror(err)); goto error; } } /* Setup timeout */ timedout = 0; action.sa_flags = 0; action.sa_handler = timed_out; sigemptyset(&action.sa_mask); if(sigaction(SIGALRM, &action, NULL) < 0) { syslog(LOG_ERR, "Setting timeout in backend_connect failed: sigaction: %m"); /* continue anyway */ } for (res = res0; res; res = res->ai_next) { sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) continue; alarm(config_getint(IMAPOPT_CLIENT_TIMEOUT)); if (connect(sock, res->ai_addr, res->ai_addrlen) >= 0) break; if(errno == EINTR && timedout == 1) errno = ETIMEDOUT; close(sock); sock = -1; } /* Remove timeout code */ alarm(0); signal(SIGALRM, SIG_IGN); if (sock < 0) { if (res0 != &hints) freeaddrinfo(res0); syslog(LOG_ERR, "connect(%s) failed: %m", server); goto error; } memcpy(&ret->addr, res->ai_addr, res->ai_addrlen); if (res0 != &hints) freeaddrinfo(res0); ret->in = prot_new(sock, 0); ret->out = prot_new(sock, 1); ret->sock = sock; prot_setflushonread(ret->in, ret->out); ret->prot = prot; /* use literal+ to send literals */ prot_setisclient(ret->in, 1); prot_setisclient(ret->out, 1); if (prot->banner.auto_capa) { /* try to get the capabilities from the banner */ r = ask_capability(ret, /*dobanner*/1, AUTO_CAPA_BANNER); if (r) { /* found capabilities in banner -> don't ask */ ask = 0; } } else { do { /* read the initial greeting */ if (!prot_fgets(buf, sizeof(buf), ret->in)) { syslog(LOG_ERR, "backend_connect(): couldn't read initial greeting: %s", ret->in->error ? ret->in->error : "(null)"); goto error; } } while (strncasecmp(buf, prot->banner.resp, strlen(prot->banner.resp))); strncpy(ret->banner, buf, 2048); } if (ask) { /* get the capabilities */ ask_capability(ret, /*dobanner*/0, AUTO_CAPA_NO); } /* now need to authenticate to backend server, unless we're doing LMTP/CSYNC on a UNIX socket (deliver/sync_client) */ if ((server[0] != '/') || (strcmp(prot->sasl_service, "lmtp") && strcmp(prot->sasl_service, "csync"))) { char *old_mechlist = backend_get_cap_params(ret, CAPA_AUTH); const char *my_status; if ((r = backend_authenticate(ret, userid, cb, &my_status))) { syslog(LOG_ERR, "couldn't authenticate to backend server: %s", sasl_errstring(r, NULL, NULL)); free(old_mechlist); goto error; } else { const void *ssf; sasl_getprop(ret->saslconn, SASL_SSF, &ssf); if (*((sasl_ssf_t *) ssf)) { /* if we have a SASL security layer, compare SASL mech lists before/after AUTH to check for a MITM attack */ char *new_mechlist; int auto_capa = (prot->sasl_cmd.auto_capa == AUTO_CAPA_AUTH_SSF); if (!strcmp(prot->service, "sieve")) { /* XXX Hack to handle ManageSieve servers. * No way to tell from protocol if server will * automatically send capabilities, so we treat it * as optional. */ char ch; /* wait and probe for possible auto-capability response */ usleep(250000); prot_NONBLOCK(ret->in); if ((ch = prot_getc(ret->in)) != EOF) { prot_ungetc(ch, ret->in); } else { auto_capa = AUTO_CAPA_AUTH_NO; } prot_BLOCK(ret->in); } ask_capability(ret, /*dobanner*/0, auto_capa); new_mechlist = backend_get_cap_params(ret, CAPA_AUTH); if (new_mechlist && old_mechlist && strcmp(new_mechlist, old_mechlist)) { syslog(LOG_ERR, "possible MITM attack:" "list of available SASL mechanisms changed"); free(new_mechlist); free(old_mechlist); goto error; } free(new_mechlist); } else if (prot->sasl_cmd.auto_capa == AUTO_CAPA_AUTH_OK) { /* try to get the capabilities from the AUTH success response */ forget_capabilities(ret); parse_capability(ret, my_status); post_parse_capability(ret); } if (!(strcmp(prot->service, "imap") && (strcmp(prot->service, "pop3")))) { parse_sessionid(my_status, rsessionid); syslog(LOG_NOTICE, "proxy %s sessionid=<%s> remote=<%s>", userid, session_id(), rsessionid); } } if (auth_status) *auth_status = my_status; free(old_mechlist); } /* start compression if requested and both client/server support it */ if (config_getswitch(IMAPOPT_PROXY_COMPRESS) && ret && CAPA(ret, CAPA_COMPRESS) && prot->compress_cmd.cmd && do_compress(ret, &prot->compress_cmd)) { syslog(LOG_ERR, "couldn't enable compression on backend server"); goto error; } return ret; error: forget_capabilities(ret); if (ret->in) { prot_free(ret->in); ret->in = NULL; } if (ret->out) { prot_free(ret->out); ret->out = NULL; } if (sock >= 0) close(sock); if (ret->saslconn) { sasl_dispose(&ret->saslconn); ret->saslconn = NULL; } if (!ret_backend) free(ret); return NULL; }
static void replica_connect(const char *channel) { int wait; sasl_callback_t *cb; int timeout; const char *port, *auth_status = NULL; cb = mysasl_callbacks(NULL, get_config(channel, "sync_authname"), get_config(channel, "sync_realm"), get_config(channel, "sync_password")); /* get the right port */ port = get_config(channel, "sync_port"); if (port) { imap_csync_protocol.service = port; csync_protocol.service = port; } for (wait = 15;; wait *= 2) { sync_backend = backend_connect(sync_backend, servername, &imap_csync_protocol, "", cb, &auth_status, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend) { if (sync_backend->capability & CAPA_REPLICATION) { /* attach our IMAP tag buffer to our protstreams as userdata */ sync_backend->in->userdata = sync_backend->out->userdata = &tagbuf; break; } else { backend_disconnect(sync_backend); sync_backend = NULL; } } sync_backend = backend_connect(sync_backend, servername, &csync_protocol, "", cb, NULL, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend || auth_status || connect_once || wait > 1000) break; fprintf(stderr, "Can not connect to server '%s', retrying in %d seconds\n", servername, wait); sleep(wait); } free_callbacks(cb); cb = NULL; if (!sync_backend) { fprintf(stderr, "Can not connect to server '%s'\n", servername); syslog(LOG_ERR, "Can not connect to server '%s'", servername); _exit(1); } if (servername[0] != '/' && sync_backend->sock >= 0) { tcp_disable_nagle(sync_backend->sock); tcp_enable_keepalive(sync_backend->sock); } #ifdef HAVE_ZLIB /* Does the backend support compression? */ if (CAPA(sync_backend, CAPA_COMPRESS)) { prot_printf(sync_backend->out, "%s\r\n", sync_backend->prot->u.std.compress_cmd.cmd); prot_flush(sync_backend->out); if (sync_parse_response("COMPRESS", sync_backend->in, NULL)) { if (do_compress) fatal("Failed to enable compression, aborting", EC_SOFTWARE); syslog(LOG_NOTICE, "Failed to enable compression, continuing uncompressed"); } else { prot_setcompress(sync_backend->in); prot_setcompress(sync_backend->out); } } else if (do_compress) fatal("Backend does not support compression, aborting", EC_SOFTWARE); #endif /* links to sockets */ sync_in = sync_backend->in; sync_out = sync_backend->out; if (verbose > 1) { prot_setlog(sync_in, fileno(stderr)); prot_setlog(sync_out, fileno(stderr)); } /* Set inactivity timer */ timeout = config_getint(IMAPOPT_SYNC_TIMEOUT); if (timeout < 3) timeout = 3; prot_settimeout(sync_in, timeout); /* Force use of LITERAL+ so we don't need two way communications */ prot_setisclient(sync_in, 1); prot_setisclient(sync_out, 1); }
EXPORTED struct backend *backend_connect(struct backend *ret_backend, const char *server, struct protocol_t *prot, const char *userid, sasl_callback_t *cb, const char **auth_status, int logfd) { /* need to (re)establish connection to server or create one */ int sock = -1; int r; int err = -1; int do_tls = 0; int noauth = 0; struct addrinfo hints, *res0 = NULL, *res; struct sockaddr_un sunsock; struct backend *ret; if (!ret_backend) { ret = xzmalloc(sizeof(struct backend)); strlcpy(ret->hostname, server, sizeof(ret->hostname)); ret->timeout = NULL; } else ret = ret_backend; if (server[0] == '/') { /* unix socket */ res0 = &hints; memset(res0, 0, sizeof(struct addrinfo)); res0->ai_family = PF_UNIX; res0->ai_socktype = SOCK_STREAM; res0->ai_addr = (struct sockaddr *) &sunsock; res0->ai_addrlen = sizeof(sunsock.sun_family) + strlen(server) + 1; #ifdef SIN6_LEN res0->ai_addrlen += sizeof(sunsock.sun_len); sunsock.sun_len = res0->ai_addrlen; #endif sunsock.sun_family = AF_UNIX; strlcpy(sunsock.sun_path, server, sizeof(sunsock.sun_path)); if (!strcmp(prot->sasl_service, "lmtp") || !strcmp(prot->sasl_service, "csync")) { noauth = 1; } } else { /* inet socket */ char host[1024], *p; const char *service = prot->service; /* Parse server string for possible port and options */ strlcpy(host, server, sizeof(host)); if ((p = strchr(host, ':'))) { *p++ = '\0'; service = p; if ((p = strchr(service, '/'))) { tok_t tok; char *opt; *p++ = '\0'; tok_initm(&tok, p, "/", 0); while ((opt = tok_next(&tok))) { if (!strcmp(opt, "tls")) do_tls = 1; else if (!strcmp(opt, "noauth")) noauth = 1; } tok_fini(&tok); } } memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(host, service, &hints, &res0); if (err) { syslog(LOG_ERR, "getaddrinfo(%s) failed: %s", server, gai_strerror(err)); goto error; } } for (res = res0; res; res = res->ai_next) { sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) continue; /* Do a non-blocking connect() */ nonblock(sock, 1); if (!connect(sock, res->ai_addr, res->ai_addrlen)) { /* connect() succeeded immediately */ break; } else if (errno == EINPROGRESS) { /* connect() in progress */ int n; fd_set wfds, rfds; time_t now = time(NULL); time_t timeout = now + config_getint(IMAPOPT_CLIENT_TIMEOUT); struct timeval waitfor; /* select() socket for writing until we succeed, fail, or timeout */ do { FD_ZERO(&wfds); FD_SET(sock, &wfds); rfds = wfds; waitfor.tv_sec = timeout - now; waitfor.tv_usec = 0; n = select(sock + 1, &rfds, &wfds, NULL, &waitfor); now = time(NULL); /* Retry select() if interrupted */ } while (n < 0 && errno == EINTR && now < timeout); if (!n) { /* select() timed out */ errno = ETIMEDOUT; } else if (FD_ISSET(sock, &rfds) || FD_ISSET(sock, &wfds)) { /* Socket is ready for I/O - get SO_ERROR to determine status */ socklen_t errlen = sizeof(err); if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &errlen) && !(errno = err)) { /* connect() succeeded */ break; } } } close(sock); sock = -1; } if (sock < 0) { if (res0 != &hints) freeaddrinfo(res0); syslog(LOG_ERR, "connect(%s) failed: %m", server); goto error; } /* Reset socket to blocking */ nonblock(sock, 0); memcpy(&ret->addr, res->ai_addr, res->ai_addrlen); if (res0 != &hints) freeaddrinfo(res0); ret->in = prot_new(sock, 0); ret->out = prot_new(sock, 1); ret->sock = sock; prot_settimeout(ret->in, config_getint(IMAPOPT_CLIENT_TIMEOUT)); prot_setflushonread(ret->in, ret->out); ret->prot = prot; /* use literal+ to send literals */ prot_setisclient(ret->in, 1); prot_setisclient(ret->out, 1); /* Start TLS if required */ if (do_tls) r = backend_starttls(ret, NULL, NULL, NULL); /* Login to the server */ if (prot->type == TYPE_SPEC) r = prot->u.spec.login(ret, userid, cb, auth_status, noauth); else r = backend_login(ret, userid, cb, auth_status, noauth); if (r) goto error; if (logfd >= 0) { prot_setlog(ret->in, logfd); prot_setlog(ret->out, logfd); } else prot_settimeout(ret->in, 0); return ret; error: forget_capabilities(ret); if (ret->in) { prot_free(ret->in); ret->in = NULL; } if (ret->out) { prot_free(ret->out); ret->out = NULL; } if (sock >= 0) close(sock); if (ret->saslconn) { sasl_dispose(&ret->saslconn); ret->saslconn = NULL; } if (!ret_backend) free(ret); return NULL; }
/* verify that the matching MAILBOX exists within the claimed chunk * for each mailbox or mailbox_message in the index */ static int verify_chunk_mailbox_links(struct backup *backup, struct backup_chunk *chunk, struct gzuncat *gzuc, int verbose, FILE *out) { /* * get list of mailboxes in chunk * get list of mailbox_messages in chunk * index mailboxes list by uniqueid * index mailbox_messages list by uniqueid:uid * open chunk * foreach line in chunk * read dlist * skip if it's not a mailbox * if details in dlist match details in mailbox * remove from mailbox list/index * foreach record in dlist * if details in dlist match details in mailbox_message * remove from mailbox_message list/index * failed if either list of mailboxes or list of mailbox_messages is not empty */ struct backup_mailbox_list *mailbox_list = NULL; struct backup_mailbox_message_list *mailbox_message_list = NULL; hash_table mailbox_list_index = HASH_TABLE_INITIALIZER; hash_table mailbox_message_list_index = HASH_TABLE_INITIALIZER; struct backup_mailbox *mailbox = NULL; struct backup_mailbox_message *mailbox_message = NULL; int r; if (out && verbose) fprintf(out, "checking chunk %d mailbox links...\n", chunk->id); mailbox_list = backup_get_mailboxes(backup, chunk->id, 0); mailbox_message_list = backup_get_mailbox_messages(backup, chunk->id); if (mailbox_list->count == 0 && mailbox_message_list->count == 0) { /* nothing we care about in this chunk */ free(mailbox_list); free(mailbox_message_list); if (out && verbose) fprintf(out, "ok\n"); return 0; } /* XXX consider whether the two hashes should use pools */ if (mailbox_list->count) { /* build an index of the mailbox list */ construct_hash_table(&mailbox_list_index, mailbox_list->count, 0); mailbox = mailbox_list->head; while (mailbox) { hash_insert(mailbox->uniqueid, mailbox, &mailbox_list_index); mailbox = mailbox->next; } } if (mailbox_message_list->count) { /* build an index of the mailbox message list */ construct_hash_table(&mailbox_message_list_index, mailbox_message_list->count, 0); mailbox_message = mailbox_message_list->head; while (mailbox_message) { char keybuf[1024]; // FIXME whatever snprintf(keybuf, sizeof(keybuf), "%s:%d", mailbox_message->mailbox_uniqueid, mailbox_message->uid); hash_insert(keybuf, mailbox_message, &mailbox_message_list_index); mailbox_message = mailbox_message->next; } } r = gzuc_member_start_from(gzuc, chunk->offset); if (r) { syslog(LOG_ERR, "%s: error reading chunk %i at offset %jd: %s", __func__, chunk->id, chunk->offset, zError(r)); if (out) fprintf(out, "error reading chunk %i at offset %jd: %s", chunk->id, chunk->offset, zError(r)); goto done; } struct protstream *ps = prot_readcb(_prot_fill_cb, gzuc); prot_setisclient(ps, 1); /* don't sync literals */ struct buf cmd = BUF_INITIALIZER; while (1) { struct dlist *dl = NULL; struct dlist *record = NULL; struct dlist *di = NULL; const char *uniqueid = NULL; int c = parse_backup_line(ps, NULL, &cmd, &dl); if (c == EOF) { const char *error = prot_error(ps); if (error && 0 != strcmp(error, PROT_EOF_STRING)) { syslog(LOG_ERR, "%s: error reading chunk %i data at offset %jd, byte %i: %s", __func__, chunk->id, chunk->offset, prot_bytes_in(ps), error); if (out) fprintf(out, "error reading chunk %i data at offset %jd, byte %i: %s", chunk->id, chunk->offset, prot_bytes_in(ps), error); r = EOF; } break; } if (strcmp(buf_cstring(&cmd), "APPLY") != 0) goto next_line; if (strcmp(dl->name, "MAILBOX") != 0) goto next_line; if (!dlist_getatom(dl, "UNIQUEID", &uniqueid)) goto next_line; if (mailbox_list->count) { mailbox = (struct backup_mailbox *) hash_lookup(uniqueid, &mailbox_list_index); if (mailbox && mailbox_matches(mailbox, dl)) { backup_mailbox_list_remove(mailbox_list, mailbox); hash_del(uniqueid, &mailbox_list_index); backup_mailbox_free(&mailbox); } } if (mailbox_message_list->count) { if (!dlist_getlist(dl, "RECORD", &record)) goto next_line; for (di = record->head; di; di = di->next) { char keybuf[1024]; // FIXME whatever uint32_t uid; if (!dlist_getnum32(di, "UID", &uid)) continue; snprintf(keybuf, sizeof(keybuf), "%s:%d", uniqueid, uid); mailbox_message = (struct backup_mailbox_message *) hash_lookup( keybuf, &mailbox_message_list_index); if (!mailbox_message) continue; if (!mailbox_message_matches(mailbox_message, di)) continue; backup_mailbox_message_list_remove(mailbox_message_list, mailbox_message); hash_del(keybuf, &mailbox_message_list_index); backup_mailbox_message_free(&mailbox_message); } } next_line: if (dl) { dlist_unlink_files(dl); dlist_free(&dl); } } buf_free(&cmd); prot_free(ps); gzuc_member_end(gzuc, NULL); /* anything left in either of the lists is missing from the chunk data. bad! */ mailbox = mailbox_list->head; while (mailbox) { syslog(LOG_DEBUG, "%s: chunk %d missing mailbox data for %s (%s)\n", __func__, chunk->id, mailbox->uniqueid, mailbox->mboxname); if (out) fprintf(out, "chunk %d missing mailbox data for %s (%s)\n", chunk->id, mailbox->uniqueid, mailbox->mboxname); mailbox = mailbox->next; } mailbox_message = mailbox_message_list->head; while (mailbox_message) { syslog(LOG_DEBUG, "%s: chunk %d missing mailbox_message data for %s uid %u\n", __func__, chunk->id, mailbox_message->mailbox_uniqueid, mailbox_message->uid); if (out) fprintf(out, "chunk %d missing mailbox_message data for %s uid %u\n", chunk->id, mailbox_message->mailbox_uniqueid, mailbox_message->uid); mailbox_message = mailbox_message->next; } if (!r) r = mailbox_list->count || mailbox_message_list->count ? -1 : 0; done: free_hash_table(&mailbox_list_index, NULL); free_hash_table(&mailbox_message_list_index, NULL); backup_mailbox_list_empty(mailbox_list); free(mailbox_list); backup_mailbox_message_list_empty(mailbox_message_list); free(mailbox_message_list); syslog(LOG_DEBUG, "%s: chunk %d %s!\n", __func__, chunk->id, r ? "failed" : "passed"); if (out && verbose) fprintf(out, "%s\n", r ? "error" : "ok"); return r; }
static int _verify_message_cb(const struct backup_message *message, void *rock) { struct verify_message_rock *vmrock = (struct verify_message_rock *) rock; struct dlist *dl = NULL; struct dlist *di = NULL; FILE *out = vmrock->out; int r; /* cache the dlist so that multiple reads from the same offset don't * cause expensive reverse seeks in decompression stream */ if (!vmrock->cached_dlist || vmrock->cached_offset != message->offset) { if (vmrock->cached_dlist) { dlist_unlink_files(vmrock->cached_dlist); dlist_free(&vmrock->cached_dlist); } r = gzuc_seekto(vmrock->gzuc, message->offset); if (r) return r; struct protstream *ps = prot_readcb(_prot_fill_cb, vmrock->gzuc); prot_setisclient(ps, 1); /* don't sync literals */ r = parse_backup_line(ps, NULL, NULL, &dl); prot_free(ps); if (r == EOF) { const char *error = prot_error(ps); if (error && 0 != strcmp(error, PROT_EOF_STRING)) { syslog(LOG_ERR, "%s: error reading message %i at offset %jd, byte %i: %s", __func__, message->id, message->offset, prot_bytes_in(ps), error); if (out) fprintf(out, "error reading message %i at offset %jd, byte %i: %s", message->id, message->offset, prot_bytes_in(ps), error); } return r; } vmrock->cached_dlist = dl; vmrock->cached_offset = message->offset; } else { dl = vmrock->cached_dlist; } r = strcmp(dl->name, "MESSAGE"); if (r) return r; r = -1; for (di = dl->head; di; di = di->next) { struct message_guid *guid = NULL; const char *fname = NULL; if (!dlist_tofile(di, NULL, &guid, NULL, &fname)) continue; r = message_guid_cmp(guid, message->guid); if (!r) { if (vmrock->verify_guid) { const char *msg_base = NULL; size_t msg_len = 0; struct message_guid computed_guid; int fd; fd = open(fname, O_RDWR); if (fd != -1) { map_refresh(fd, 1, &msg_base, &msg_len, MAP_UNKNOWN_LEN, fname, NULL); message_guid_generate(&computed_guid, msg_base, msg_len); r = message_guid_cmp(&computed_guid, message->guid); if (r && out) fprintf(out, "guid mismatch for message %i\n", message->id); map_free(&msg_base, &msg_len); close(fd); } else { syslog(LOG_ERR, "IOERROR: %s open %s: %m", __func__, fname); if (out) fprintf(out, "error reading staging file for message %i\n", message->id); r = -1; } } break; } } return r; }
static struct backend *restore_connect(const char *servername, struct buf *tagbuf, const struct restore_options *options) { struct backend *backend = NULL; sasl_callback_t *cb; int timeout; const char *auth_status = NULL; cb = mysasl_callbacks(NULL, config_getstring(IMAPOPT_RESTORE_AUTHNAME), config_getstring(IMAPOPT_RESTORE_REALM), config_getstring(IMAPOPT_RESTORE_PASSWORD)); /* try to connect over IMAP */ backend = backend_connect(backend, servername, &imap_csync_protocol, "", cb, &auth_status, (options->verbose > 1 ? fileno(stderr) : -1)); if (backend) { if (backend->capability & CAPA_REPLICATION) { /* attach our IMAP tag buffer to our protstreams as userdata */ backend->in->userdata = backend->out->userdata = tagbuf; } else { backend_disconnect(backend); backend = NULL; } } /* if that didn't work, fall back to csync */ if (!backend) { backend = backend_connect(backend, servername, &csync_protocol, "", cb, NULL, (options->verbose > 1 ? fileno(stderr) : -1)); } free_callbacks(cb); cb = NULL; if (!backend) { fprintf(stderr, "Can not connect to server '%s'\n", servername); syslog(LOG_ERR, "Can not connect to server '%s'", servername); return NULL; } if (servername[0] != '/' && backend->sock >= 0) { tcp_disable_nagle(backend->sock); tcp_enable_keepalive(backend->sock); } #ifdef HAVE_ZLIB /* Does the backend support compression? */ if (CAPA(backend, CAPA_COMPRESS)) { prot_printf(backend->out, "%s\r\n", backend->prot->u.std.compress_cmd.cmd); prot_flush(backend->out); if (sync_parse_response("COMPRESS", backend->in, NULL)) { if (options->require_compression) fatal("Failed to enable compression, aborting", EX_SOFTWARE); syslog(LOG_NOTICE, "Failed to enable compression, continuing uncompressed"); } else { prot_setcompress(backend->in); prot_setcompress(backend->out); } } else if (options->require_compression) { fatal("Backend does not support compression, aborting", EX_SOFTWARE); } #endif if (options->verbose > 1) { /* XXX did we do this during backend_connect already? */ prot_setlog(backend->in, fileno(stderr)); prot_setlog(backend->out, fileno(stderr)); } /* Set inactivity timer */ timeout = config_getint(IMAPOPT_SYNC_TIMEOUT); if (timeout < 3) timeout = 3; prot_settimeout(backend->in, timeout); /* Force use of LITERAL+ so we don't need two way communications */ prot_setisclient(backend->in, 1); prot_setisclient(backend->out, 1); return backend; }